Some organizations might wish to start taking advantage of the .NET platform by developing (or redeveloping) certain key middle-tier components in managed code. However, they might still be using Visual Basic 6 or Active Server Pages for the user-interface tier. In this situation, you would want to develop new .NET components, which are visible to both COM-based client applications and managed .NET applications.
There are a few considerations for doing this. The first one is providing an interface that the COM components can understand. All classes and class members that should be exposed to COM should be marked as Public; they will be available by default. If there are certain classes in your components or members of a class that should not be used by COM clients, you can restrict which are available by applying the ComVisibleAttribute, as shown in the following code. Another important requirement is that you must provide a constructor method that does not require parameters (a default constructor), which is the only type of constructor that COM can use.
Imports System.Runtime.InteropServices <ComVisible(False)> Public Class Account ' Insert class members here. End Class
It is possible to mark your assembly or classes with the ClassInterfaceAttribute, with the ClassInterfaceType option set to AutoDual, and have an interface generated automatically for you by the runtime, as shown in the following code.
Imports System.Runtime.InteropServices <ClassInterface(ClassInterfaceType.AutoDual)> _ Public Class Account ' Insert class members here. End Class
COM components communicate through interfaces and they expect these interfaces to always be the same (immutable is term you will find in the documentation). .NET Framework components do not require that the members of other components stay consistent, because the CLR enables components to discover available methods at runtime.
To keep a consistent interface for COM callers, you should create an explicit interface for your managed class (this can be done with the Type Library Exporter utility, tlbexp.exe), rather than relying on the automatically generated class interface. The automatically generated interface will reflect any changes that have been made to the managed component/class and will most likely cause an error for the COM component. The explicit interface will always look consistent to the COM component. You should also set the ClassInterfaceType option to None when providing an explicit interface. The other option for ClassInterfaceType is AutoDispatch. Use this option if you are creating components that will be used only by scripting clients, which communicate through the standard COM IDispatch interface.
Here is an example of how to use the Type Library Exporter from the command line:
C:\>tlbexp myComponent.dll /out:myComponent.tlb
The runtime creates a COM Callable Wrapper (CCW) class. This runtime always creates one instance of the CCW object, even if there is more than one caller accessing the underlying .NET-managed object. The managed object itself is subject to CLR garbage collection; the CCW is not. The CCW, like any standard COM object, maintains a count of all the references held on it by callers, and when the reference count reaches zero, the CCW releases the reference it holds on the managed object. The managed object can then be garbage collected. The runtime provides implementation for IUnknown and IDispatch, the standard interfaces that all COM components must implement.
In Exercise 2.3, you will create a component in Visual Studio .NET and then call methods from that component by using a Visual Basic 6 client application.
Exercise 2.3: Creating a COM Component by Using Visual Studio .NET
Create a new Visual Studio .NET project by using the Class Library project template. Name this project InteropComponent. Change the name of class file to InteropAccount.vb and the class name in the code editor to InteropAccount.
Add an Imports statement for System.Runtime.InteropServices. All the methods of your class that are marked as Public will be available to COM clients. Make sure your component has a Public Sub New constructor that does not expect any parameters. This default constructor is required for use with COM.
Your code should look like this:
Imports System.Runtime.InteropServices Public Class InteropAccount Public Sub New() 'default constructor End Sub Public Function AddTwoNumbers(ByVal firstNumber As Double,_ ByVal secondNumber As Double) As Double Return firstNumber + secondNumber End Function End Class
Compile your component. This will create InteropComponent.dll in the project’s \bin directory.
Create a Visual Basic 6 client application to test your component. Name this project InteropTester.
Copy InteropComponent.dll file to the project directory of the Visual Basic 6 test client.
Open a Visual Studio .NET command prompt and navigate to the Visual Basic 6 project directory.
Use the command-line utility tlbexp.exe to export type library which will be called InteropComponent.tlb, from your .NET component:
c:\path> tlbexp InteropComponent.dll
Use the regasm.exe command-line utility to register the component for use by COM:
c:\path> regasm InteropComponent.dll
Use the Project Ø References menu item in Visual Basic 6 to set a reference in the test client project. Use the Browse button to locate the InteropComponent.tlb file which was created in step 7, and is located in the Visual Basic 6 project directory.
Add the following Visual Basic 6 code to execute the AddTwoNumbers method from the InteropComponent:
Private Sub btnTest_Click() Dim objInterop As InteropComponent.InteropAccount Set objInterop = New InteropComponent.InteropAccount Msgbox CStr(objInterop.AddTwoNumbers(2, 2)), vbOKOnly, "Interop Test" End Sub
Use the File Ø Make InteropTester.exe menu item in Visual Basic 6 to compile the project.
This application will not run inside the Visual Basic 6 IDE; you must run InteropTester.exe from the Windows Explorer to test the InteropComponent.