Your Interop library is automatically created for you from the ActiveX DLL that you told Visual Studio 2008 to use.. This Interop library is the RCW component customized for your ActiveX
Trang 1Runtime Callable Wrapper (RCW)
.NET Code (C# or VB.NET)
ActiveX DLL or OCX (COM Component)
Your New NET Code
.NETs Built-In Interoperability Technology
Your existing ActiveX Component Code
Figure 28-6
The Runtime Callable Wrapper
The Runtime Callable Wrapper, or RCW, is the magic piece of code that enables interaction to occur
between NET and COM One RCW is created for each COM component in your project To create an
RCW for a COM component, you can use Visual Studio 2008
To add an ActiveX DLL to the References section of your project, choose Website ➪ Add Reference or
choose the Add Reference menu item that appears when you right-click the root node of your project in the Solution Explorer
The Add Reference dialog box appears with five tabs: NET, COM, Projects, Browse, and Recent, as
shown in Figure 28-7 For this example, select the COM tab and locate the component that you want to add to your NET project After you have located the component, highlight the item and click OK to add
a reference to the component to your project The newly added component will then be found inside a
newly createdBinfolder in your project
Your Interop library is automatically created for you from the ActiveX DLL that you told Visual Studio
2008 to use This Interop library is the RCW component customized for your ActiveX control, as shown previously in Figure 28-6 The name of the Interop file is simplyInterop.OriginalName.DLL
It is also possible to create the RCW files manually instead of doing it through Visual Studio 2008 In the NET Framework, you will find a method to create RCW Interop files for controls manually through a
command-line tool called the Type Library Importer You invoke the Type Library Importer by using the
tlbimp.exeexecutable
For example, to create the Interop library for the SQLDMO object used earlier, start up a Visual Studio
2008 Command Prompt from the Microsoft Visual Studio 2008 ➪ Visual Studio Tools group within your Start Menu From the comment prompt, type
tlbimp sqldmo.dll /out:sqldmoex.dll
In this example, the/out:parameter specifies the name of the RCW Interop library to be created If you omit this parameter, you get the same name that Visual Studio would generate for you
Trang 2Figure 28-7 The Type Library Importer is useful when you are not using Visual Studio 2008 as your development
environment, if you want to have more control over the assemblies that get created for you, or if you are
automating the process of connecting to COM components
The Type Library Importer is a wrapper application around theTypeLibConvertorclass of the
Sys-tem.Runtime.InteropServicesnamespace
Using COM Objects in ASP.NET Code
To continue working through some additional examples, you next take a look at a simple example of
using a COM object written in Visual Basic 6 within an ASP.NET page
In the first step, you create an ActiveX DLL that you can use for the upcoming examples Add the Visual
Basic 6 code shown in Listing 28-1 to a class calledNameFunctionsClassand compile it as an ActiveX
DLL calledNameComponent.dll
Listing 28-1: VB6 code for ActiveX DLL, NameComponent.DLL
Option Explicit
Private m_sFirstName As String
Private m_sLastName As String
Public Property Let FirstName(Value As String)
m_sFirstName = Value
Trang 3End Property
Public Property Get FirstName() As String
FirstName = m_sFirstName
End Property
Public Property Let LastName(Value As String)
m_sLastName = Value
End Property
Public Property Get LastName() As String
LastName = m_sLastName
End Property
Public Property Let FullName(Value As String)
m_sFirstName = Split(Value, " ")(0)
If (InStr(Value, " ") > 0) Then
m_sLastName = Split(Value, " ")(1)
Else
m_sLastName = ""
End If
End Property
Public Property Get FullName() As String
FullName = m_sFirstName + " " + m_sLastName
End Property
Public Property Get FullNameLength() As Long
FullNameLength = Len(Me.FullName)
End Property
Now that you have created an ActiveX DLL to use in your ASP.NET pages, the next step is to create a
new ASP.NET project using Visual Studio 2008 Replace the HTML code in theDefault.aspxfile with
the HTML code illustrated in Listing 28-2 This adds a number of text boxes and labels to the HTML page,
as well as the Visual Basic or C# code for the functionality
Listing 28-2: Using NameComponent.dll
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub AnalyzeName_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim Name As New NameComponent.NameFunctionsClass()
If (FirstName.Text.Length > 0) Then
Name.FirstName = FirstName.Text
End If
If (LastName.Text.Length > 0) Then
Continued
Trang 4Name.LastName = LastName.Text
End If
If (FullName.Text.Length > 0) Then
Name.FullName = FullName.Text
End If
FirstName.Text = Name.FirstName
LastName.Text = Name.LastName
FullName.Text = Name.FullName
FullNameLength.Text = Name.FullNameLength.ToString
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Using COM Components</title>
</head>
<body>
<form id="form1" runat="server">
<p>
<asp:Label ID="Label1" runat="server">First Name:</asp:Label>
<asp:TextBox ID="FirstName" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label ID="Label2" runat="server">Last Name:</asp:Label>
<asp:TextBox ID="LastName" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label ID="Label3" runat="server">Full Name:</asp:Label>
<asp:TextBox ID="FullName" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label ID="Label4" runat="server">Full Name Length:</asp:Label>
<asp:Label ID="FullNameLength" runat="server"
Font-Bold="True">0</asp:Label>
</p>
<p>
<asp:Button ID="AnalyzeName" runat="server"
OnClick="AnalyzeName_Click" Text="Analyze Name"></asp:Button>
</p>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
Trang 5protected void AnalyzeName_Click(object sender, System.EventArgs e)
{
NameComponent.NameFunctionsClass Name =
new NameComponent.NameFunctionsClass();
if (FirstName.Text.Length > 0)
{
string firstName = FirstName.Text.ToString();
Name.set_FirstName(ref firstName);
}
if (LastName.Text.Length > 0)
{
string lastName = LastName.Text.ToString();
Name.set_LastName(ref lastName);
}
if (FullName.Text.Length > 0)
{
string fullName = FullName.Text.ToString();
Name.set_FullName(ref fullName);
}
FirstName.Text = Name.get_FirstName();
LastName.Text = Name.get_LastName();
FullName.Text = Name.get_FullName();
FullNameLength.Text = Name.FullNameLength.ToString();
}
</script>
Now you need to add the reference to the ActiveX DLL that you created in the previous step To do so, follow these steps:
1. Right-click your project in the Solution Explorer dialog
2. Select the Add Reference menu item
3. In the Add Reference dialog box, select the fourth tab, Browse
4. Locate theNameComponent.dllobject by browsing to its location
5. Click OK to addNameComponent.dllto the list of selected components and close the dialog box
If you are not using Visual Studio 2008 or code-behind pages, you can still add a reference to your COM
control by creating the RCW manually using the Type Library Converter and then placing anImports
statement (VB) orusingstatement (C#) in the page.
After you have selected your component using the Add Reference dialog, an RCW file is created for the component and added to your application
That’s all there is to it! Simply run the application to see the COM interoperability layer in action
Figure 28-8 shows the ASP.NET page that you created When the Analyze Name button is clicked, the
fields in the First Name, Last Name, and Full Name text boxes are sent to the RCW to be passed to
Trang 6theNameComponent.DLLActiveX component Data is retrieved in the same manner to repopulate the text
boxes and to indicate the length of the full name
Figure 28-8
Accessing Tricky COM Members in C#
Sometimes, some members of COM objects do not expose themselves properly to C# In the
preceding examples, theStringproperties did not expose themselves, but theLong property
(FullNameLength) did
You know when there is a problem because, although you can see the property, you cannot compile the
application For instance, instead of the code shown in Listing 28-2 for C#, use the following piece of code
to set theFirstNameproperty of theNameComponent.dllActiveX component:
if (FirstName.Text.Length > 0)
Name.FirstName = FirstName.Text.ToString();
When you try to compile this code, you get the following error:
c:\inetpub\wwwroot\wrox\Default.aspx.cs(67): Property, indexer, or event ’FirstName’
is not supported by the language; try directly calling accessor methods ’NameComponent
.NameFunctionsClass.get_FirstName()’ or ’NameComponent.NameFunctionsClass
.set_FirstName(ref string)’
TheFirstNameproperty seems to be fine It shows up in IntelliSense, but you can’t use it Instead, you
must useset_FirstName(andget_FirstNameto read) These methods do not show up in IntelliSense,
but rest assured, they exist
Trang 7Furthermore, these methods expect aref stringparameter rather than aString In the example from
Listing 28-2, two steps are used to do this properly First,Stringis assigned to a local variable, and then the variable is passed to the method usingref
Releasing COM Objects Manually
One of the great things about NET is that it has its own garbage collection — it can clean up after itself This is not always the case when using COM interoperability, however .NET has no way of knowing
when to release a COM object from memory because it does not have the built-in garbage collection
mechanism that NET relies on
Because of this limitation, you should release COM objects from memory as soon as possible using the
ReleaseComObjectclass of theSystem.Runtime.InteropServices.Marshalclass:
C#
System.Runtime.InteropServices.Marshal.ReleaseComObject(Object);
It should be noted that if you attempt to use this object again before it goes out of scope, you would raise
an exception
Error Handling
Error handling in NET uses exceptions instead of the HRESULT values used by Visual Basic 6 applica-tions Luckily, the RCW does most of the work to convert between the two
Take, for instance, the code shown in Listing 28-3 In this example, a user-defined error is raised if the
numerator or the denominator is greater than 1000 Also notice that we are not capturing a divide-by-zero error Notice what happens when the ActiveX component raises the error on its own
Begin this example by compiling the code listed in Listing 28-3 into a class namedDivideClasswithin
an ActiveX component calledDivideComponent.dll
Listing 28-3: Raising errors in VB6
Public Function DivideNumber(Numerator As Double, _
Denominator As Double) As Double
If ((Numerator > 1000) Or (Denominator > 1000)) Then
Err.Raise vbObjectError + 1, _
"DivideComponent:Divide.DivideNumber", _
"Numerator and denominator both have to " + _
"be less than or equal to 1000."
End If
DivideNumber = Numerator / Denominator
End Function
Next, create a new ASP.NET project; add a reference to theDivideComponent.dll(invoking Visual
Studio 2008 to create its own copy of the RCW) Remember, you can also do this manually by using the
tlbimpexecutable
Trang 8Now add the code shown in Listing 28-4 to an ASP.NET page.
Listing 28-4: Error handling in NET
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Calculate_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim Divide As New DivideComponent.DivideClass()
Try
Answer.Text = Divide.DivideNumber(Numerator.Text, Denominator.Text)
Catch ex As Exception
Answer.Text = ex.Message.ToString()
End Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(Divide)
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Using COM Components</title>
</head>
<body>
<form id="form1" runat="server">
<p>
<asp:Label ID="Label1" runat="server">Numerator:</asp:Label>
<asp:TextBox ID="Numerator" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label ID="Label2" runat="server">Denominator:</asp:Label>
<asp:TextBox ID="Denominator" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label ID="Label3" runat="server">
Numerator divided by Denominator:</asp:Label>
<asp:Label ID="Answer" runat="server" Font-Bold="True">0</asp:Label>
</p>
<p>
<asp:Button ID="Calculate"
runat="server"
OnClick="Calculate_Click"
Text="Calculate">
</asp:Button>
</p>
Trang 9</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Calculate_Click(object sender, System.EventArgs e)
{
DivideComponent.DivideClass myDivide = new DivideComponent.DivideClass();
try
{
double numerator = double.Parse(Numerator.Text);
double denominator = double.Parse(Denominator.Text);
Answer.Text = myDivide.DivideNumber(ref numerator,
ref denominator).ToString();
}
catch (Exception ex)
{
Answer.Text = ex.Message.ToString();
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(myDivide);
}
</script>
The code in Listing 28-4 passes the user-entered values for theNumeratorandDenominatorto the
DivideComponent.dllActiveX component for it to divide Running the application with invalid data
gives the result shown in Figure 28-9
Figure 28-9
Trang 10Depending on the language that you are using to run the ASP.NET application, you will see different
values for different sets of data For valid inputs, you will always see the correct result, of course, and
for any input that is over 1000, you see the Visual Basic 6 appointed error description ofNumerator and
denominator both have to be less than or equal to 1000
However, for invalid Strings, Visual Basic 2008 reportsCast from string "abc" to type ‘Double’ is not
valid.whereas C# reportsInput string was not in a correct format For a divide by zero, they both
reportDivide by Zerobecause the error is coming directly from the Visual Basic 6 runtime
Deploying COM Components with NET Applications
Deploying COM components with your NET applications is very easy; especially when compared to just
deploying ActiveX controls Two scenarios are possible when deploying NET applications with COM
components:
❑ Using private assemblies
Private Assemblies
Installing all or parts of the ActiveX component local to the NET application is considered installing
private assemblies In this scenario, each installation of your NET application on the same machine has,
at least, its own copy of the Interop library for the ActiveX component you are referencing, as shown in
Figure 28-10
Interop.MyCOM.dll MyApp.exe
C:\Program Files\First Application Location\
Interop.MyCOM.dll MyApp.exe
C:\Program Files\Second Application Location\
MyCOM.DLL
C:\Program Files\Third Party COM Controls\
(Managed Code) (RCW)
(Managed Code) (RCW)
(ActiveX DLL)
Figure 28-10
It is up to you whether you decide to install the ActiveX component as local to the application or in a
shared directory for all calling applications
It was once considered proper practice to separate ActiveX components into their own directory so that if
these components were referenced again by other applications, you did not have to register or install the