Article source code: interopcom.zip
When a COM client calls a .NET object, the .NET Framework will create a COM callable wrapper (CCW). COM clients use the CCW as a proxy for the managed object. The role of CCW is to marshal calls between managed and unmanaged code and also to manage object identity and object lifetime of the managed objects they wrap.
The purpose of this article is to create a demo project to show how to call .NET component from COM client and to implement events raised by the .NET component. The example we create uses .NET component and COM client extending the functionality of the .NET component using delegation.
Step 1
Let's start by creating a Strong Name for the .NET component we will be creating in next steps. Signing an assembly with a strong name helps .NET ensure that the code in the assembly has not been changed since the assembly was published. Strong name is a unique name created by hashing a 128-bit encryption key against the name of the Assembly (COMInterOp in our case). To create a strong name, you can use the sn tool.
To create a key file named COMInterOp.snk, you could use this command line:
sn -k COMInterOp.snk
Step 2
Now we are going to create a .NET Assembly. Our assembly has only one class CEmp that exposes properties (FirstName, LastName, DOB) and raises an event (Senior).
Raising event from .NET Component and consuming it in COM component is done by defining event sink interface in managed code and then apply the ComSourceInterfacesAttribute to connect the event sink interface to the managed class.
Here is the command line instruction to create an assembly using the strong name (for vbc switches info see MSDN)
vbc /out:COMInterOp.dll /t:library /keyfile:COMInterOp.snk CEmp.vb
VB.NET CEmp class code
Imports System
Imports Microsoft.VisualBasic
Imports System.Runtime.InteropServices
<InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface evtSenior
Sub Senior()
End Interface
<ComSourceInterfacesAttribute("evtSenior")> _
Public Class CEmp
Private mstrFirstName As String
Private mstrLastName As String
Private mdtDOB As Date
Public Event Senior()
Public Property FirstName() As String
Get
FirstName = mstrFirstName
End Get
Set(ByVal Value As String)
mstrFirstName = Value
End Set
End Property
Public Property LastName() As String
Get
LastName = mstrLastName
End Get
Set(ByVal Value As String)
mstrLastName = Value
End Set
End Property
Public Property DOB() As Date
Get
DOB = mdtDOB
End Get
Set(ByVal Value As Date)
mdtDOB = Value
If DateDiff(DateInterval.Year, Value, Now) > 60 Then
RaiseEvent Senior()
End If
End Set
End Property
End Class
|
Step 3
Once the assembly is created we have to create a Type library so the COM client can use the Assembly seamlessly. We have following options of packaging assembly for COM:
- Type Library Exporter
Using the Type Library Exporter (Tlbexp.exe) the classes and interfaces contained in an assembly are converted into to a COM type library. Once the TypeLib is created COM clients can create an instance of the .NET class and call the methods of that object, just as if it were a COM object.
- TypeLibConverter Class
The TypeLibConverter Class of the System.Runtime.InteropServices namespace provides methods to convert an assembly to a TypeLib. This API produces same output as Tlbexp.exe
- Assembly Registration Tool
The Assembly Registration Tool (Regasm.exe), reads the metadata within an assembly and adds the necessary entries to the registry, which allows COM clients to create .NET Framework classes transparently. The Assembly Registration tool can generate and register a type library when you apply the /tlb: option. COM clients require that type libraries be installed in the Windows registry. Without this option, Regasm.exe only registers the types in an assembly, not the type library. Registering the types in an assembly and registering the type library are distinct activities.
- The .NET Services Installation Tool (Regsvcs.exe) (see MSDN for more info)
In our example we will use the RegAsm.exe to create TypeLib from the classes and interfaces definitions in our COMInterOp.dll assembly.
From the command line run
regasm ComInterOp.dll /tlb:ComInterOp.tlb
Step 4
Now the .NET component (COMInterOp.dll) should be installed in the GAC (global assembly cache) to work with the COM code.
The command line instruction to install COMINterOp.dll in GAC is
dir>
Gacutil -i COMInterOp.dll
Step 5
To use the .NET assembly, as it was a COM component, add reference to the TypeLib from the COM component project created in the previous steps and code against it by creating CEmp object and delegation the calls to this object.
The COM Client has two classes CEmp and CEmps, CEmp is a wrapper over our .NET assembly's CEmp and exposes FirstName, LastName and IsSenior properties. The FirstName, LastName properties just delegates to the properties of .NET's CEmp but IsSenior uses the event raised by the .NET assembly to set its value. The CEmps class is a collection of CEmps and exposes methods to test our code.
COM Component
'Class Emps
Option Explicit
Private Emps As Scripting.Dictionary
Private Sub Class_Initialize()
Set Emps = New Scripting.Dictionary
Dim objEmp As CEmp
Set objEmp = New CEmp
objEmp.InitMe "John", "Doe", "01/01/1970"
Emps.Add 0, objEmp
Set objEmp = New CEmp
objEmp.InitMe "Mike", "Edwards", "01/01/1941"
Emps.Add 1, objEmp
Set objEmp = New CEmp
objEmp.InitMe "Debra", "Bunn", "01/01/1930"
Emps.Add 2, objEmp
End Sub
Public Function PrintEmps() As String
PrintEmps = PrintBool(True) & PrintBool(False)
End Function
Public Function PrintBool(ByVal xblnSeniors As Boolean) As String
Dim intCount As Integer
Dim objEmp As CEmp
Dim strPrint As String
For intCount = 0 To Emps.Count - 1
Set objEmp = Emps(intCount)
If xblnSeniors = objEmp.IsSenior Then
strPrint = strPrint & PrintEmp(objEmp) & Chr(13)
End If
Next intCount
PrintBool = strPrint
End Function
Private Function PrintEmp(ByVal xobjEmp As CEmp) As String
Dim strPrint As String
strPrint = xobjEmp.FirstName & Chr(9) & xobjEmp.LastName
PrintEmp = strPrint
End Function
'End Class Emps
'Class Emp
Option Explicit
Private mblnIsSenior As Boolean
Private WithEvents mobjEmp As ComInterOp.CEmp
Public Sub InitMe(ByVal xstrFName As String, _
ByVal xstrLName As String, ByVal xdtDOB As Date)
Set mobjEmp = New ComInterOp.CEmp
With mobjEmp
.FirstName = xstrFName
.LastName = xstrLName
.DOB = xdtDOB
End With
End Sub
Public Property Get FirstName() As String
FirstName = mobjEmp.FirstName
End Property
Public Property Get LastName() As String
LastName = mobjEmp.LastName
End Property
Public Property Get IsSenior() As Boolean
IsSenior = mblnIsSenior
End Property
Private Sub mobjEmp_Senior()
mblnIsSenior = True
End Sub
'End Class Emp
|
Related Articles
.NET Interoperability - Calling COM Components from .NET