|
Table of Contents
- Page 1
- Introduction
- A Brief History of COM and VB6
- Multiple Interfaces
- Almost to .NET!
- Where Are My Methods?!
- Page 2
- Taking Control
- Attributes
- Controlling Your GUIDs
- Page 3
- Deployment
- Local Deployment or the GAC?
- Summary of Best Practices
- Add Versioning
- About the Author
- download sample code
Introduction
Microsoft realized that the very first version of .NET needed a way to work with the existing Windows technology used to develop applications over the past 8+ years: COM. With that in mind, Microsoft added support in the .NET runtime for interoperating with COM - simply called "COM Interop". The support goes both ways: .NET code can call COM components and COM code can call .NET components.
This article is geared towards VB6 programmers who are familiar with developing COM components and familiar with the concept of an interface. I'll review some background on COM, explain how VB6 interacts with COM, and then show how to design .NET components to smoothly interact with COM. In a future article, I'll discuss how to use an existing COM component in .NET applications.
For those die-hard COM experts, there will be some things in this article that are oversimplified, but the concepts, as presented, are the important points to know for those developers supplementing their COM code with .NET components.
A Brief History of COM and VB6
One of the key concepts of COM is that everything is called through an interface. There is no such thing as simply creating a COM object and calling a method. You must use an interface.
You're probably thinking "Wait a minute! I've coded hundreds of classes in VB6 and never needed an interface". That's right - you didn't need one because VB6 provided it for you in the background. Whenever you defined a public method on a class, VB6 made that method part of a COM interface and made your class implement the interface.
For example, consider the following VB6 class from an ActiveX DLL project. The class is called "Robot":
Option Strict
Public Sub MoveForward()
...
End Sub
Public Sub FindCar()
...
End Sub
When you compile this into a DLL, VB6 creates an interface called "_Robot" (note the leading underscore) and a "coclass" called "Robot", which implements the interface "_Robot" (a "coclass" is the actual COM creatable object).
The Microsoft utility OLE View is a tool used to examine the types defined in a COM component. OLE View should be installed under "Start, Programs, Microsoft Visual Studio 6.0, Microsoft Visual Studio 6.0 Tools, OLE View". Using the ActiveX DLL compiled above, I started up OLE View, selected "View TypeLib..." from the "File" menu and selected the DLL. Here are the important parts of the output:
interface _Robot : IDispatch {
...
HRESULT MoveForward();
...
HRESULT FindCar();
};
coclass Robot {
[default] interface _Robot;
};
The first item to point out is that there is an interface called "_Robot". This is the interface VB6 creates for us. It defines the two methods I added to the class (MoveForward and FindCar). Next, you'll see a "coclass" called "Robot". This represents the actual, creatable class. Since everything in COM is called through an interface, the "coclass" lists all interfaces supported by this object. Right now, there is only the "_Robot" interface - and it's marked with the "default" modifier.
There are a lot of COM details about the default interface, but for this article, the importance of a default interface in terms of VB6 components is that of all the interfaces a class may implement, only the default interface supports late binding. And since scripting clients like VBScript only do late binding, the methods on the default interface are the only ones they can see.
Multiple Interfaces
COM will allow you to implement more than one interface. Take the ActiveX DLL from the previous section and add a new class module called "IMaid" (VB6 doesn't support directly creating a COM interface - more on that below). Here's the IMaid code:
Option Explicit
Public Sub CleanKitchen()
...
End Sub
Public Sub WashCar()
...
End Sub
Now add "Implements IMaid" to the Robot class:
Option Explicit
Implements IMaid
Public Sub MoveForward()
End Sub
Public Sub FindCar()
End Sub
Private Sub IMaid_CleanKitchen()
End Sub
Private Sub IMaid_WashCar()
End Sub
If I compile this DLL now and examine it with OLE View, I see the following structures (again, only the relevant portions are shown below):
interface _Robot : IDispatch {
...
HRESULT MoveForward();
...
HRESULT FindCar();
};
coclass Robot {
[default] interface _Robot;
interface _IMaid;
};
interface _IMaid : IDispatch {
..
HRESULT CleanKitchen();
..
HRESULT WashCar();
};
coclass IMaid {
[default] interface _IMaid;
};
As noted earlier, VB6 does not support creating a true COM interface. However, since VB6 creates a COM interface with every class (prefixed with the underscore), the VB6 compiler will actually use the auto-generated interface when compiling the code when you use the "Implements" keyword on a class in VB6. So above, the Robot coclass implements the interface _Robot (automatically generated by VB6) and the _IMaid interface (the automatically generated one from the IMaid class).
Also note that the _Robot interface created by VB6 is marked as the default interface. If you were going to use this object in a scripting environment (such as an ASP page), you could only access the MoveForward and FindCar methods. The methods implemented by the IMaid interface are not accessible since they are not on the default interface.
Almost to .NET!
If you've stuck around this long, the answer is yes, you're getting close to some .NET code! But it's important to know these concepts before you move into making .NET components that you expose to COM. Think of it as having a blueprint before starting to build a house. Sure, you can work without a blueprint, but the house will come out much nicer if you start off right.
A quick recap of what happens in VB6 when it creates COM objects:
- COM is interface based. Everything in COM must be called through an interface.
- VB6 doesn't require that you implement a specific interface when creating a COM class. Instead, it will create an interface for you. Its name will be defined as your class name prefixed with the underscore (_) character.
- The interface created by VB6 is always marked as the default COM interface and therefore only those methods are available to scripting clients (in the example above, VBScript can not access the IMaid methods of the Robot object).
Where Are My Methods?!
Many people who first expose a .NET object to COM notice that when they try and use the object in VB6, none of their methods are listed. Create a quick .NET class library that will be exposed to COM to see why that happens:
[VB.NET]
Option Strict On
Option Explicit On
Namespace QuickNET
Public Class Bee
Public Sub FindFlowers()
End Sub
Public Sub MakeHoney()
End Sub
End Class
End Namespace
[C#]
using System;
namespace QuickNET
{
public class Bee
{
public void FindFlowers()
{
}
public void MakeHoney()
{
}
}
}
Now create a COM type library from this .NET component to see what it looks like (this doesn't actually register it for COM - it just creates a COM type library). Go to a Visual Studio .NET Command Prompt (Start, Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, Microsoft Visual Studio.NET Command Prompt), change to the directory of the above compiled .NET component and enter the following command:
TLBEXP.EXE QuickNET.dll /out:Com.QuickNET.tlb
The TLBEXP.EXE utility generates a COM type library from the .NET assembly. You can name the type library anything you want, but, by convention, it usually has a .tlb extension. I prefix my exported type libraries with "Com.". Now load up OLE View and open the type library "Com.QuickNET.tlb". Below are the relevant parts:
coclass Bee {
[default] interface _Bee;
interface _Object;
};
interface _Bee : IDispatch {
};
This looks very similar to VB6 COM type libraries. You can see that an interface called "_Bee" was created for the "Bee" class and it's also the default interface. However, there are no methods on the interface. If you were to start up VB6 and add a reference to this tlb to your project, you'd notice by looking at the Object Browser that the Bee class has no methods (VB6 always looks at the "default" interface to see what methods are on the class).
NOTE: The _Object interface isn't important for our discussion. Since everything inherits from the Object class in .NET and that class exposes a _Object interface, there's a _Object interface added to all exported types.
So why didn't TLBEXP.EXE put all of the methods on the _Bee interface? Since the layout of a COM interface is a binding contract, adding new methods to a .NET class and then regenerating the COM type library might change that layout - thus breaking existing COM clients compiled against the old layout. By defining an empty interface, all clients will do late-bound calls and new versions of the .NET component (and its COM wrapper) will work without recompiling the COM clients.
Taking Control >>
|