So how do you get those methods on your .NET component to show up in VB6? You can take what you know about VB6 and COM and apply it to .NET. Basically, you need to:
Have an interface with your methods defined.
Make that interface the default interface.
The first one is easy. Add a .NET interface that includes the methods you want to expose to COM:
[VB.NET]
Public Interface IBee
Sub FindFlower()
Sub MakeHoney()
End Interface
[C#]
public interface IBee
{
void FindFlower();
void MakeHoney();
}
Now you'll simply implement that interface in your Bee class.
[VB.NET]
Public Class Bee
Implements IBee
Public Sub FindFlower() Implements IBee.FindFlower
End Sub
Public Sub MakeHoney() Implements IBee.MakeHoney
End Sub
End Class
[C#]
public class Bee : IBee
{
public void FindFlower()
{
}
public void MakeHoney()
{
}
}
You'll compile this component, run the TLBEXP.EXE utility and check it out with OLE View. What you see may surprise you:
The IBee interface came straight through as a standard COM interface - it even includes the methods. The "Bee" coclass even implements this class (as expected), but there's still that (empty) _Bee interface that is marked as the default interface. And that's where VB6 will look for the methods belonging to the class.
But the IBee interface is exposed as a standard COM interface so you could do everything through that interface and it would work fine. For example, using the type library created above, this VB6 code is perfectly legal:
Dim bee As IBee
Set bee = New Bee
bee.FindFlower
bee.MakeHoney
To make a cleaner integration, however, the IBee interface needs to be the default interface. That is accomplished through attributes.Attributes
If you're not familiar with attributes, here's a quick summary. Attributes are a "descriptive declaration". They're used to "annotate" programming elements such as types, fields, methods, classes, etc... Attributes can have values associated with them and those values, along with the attribute information, are saved with all of the other .NET metadata. They can be used to describe code to the CLR (Common Language Runtime) or to affect application behavior at runtime.
To control how TLBEXP.EXE creates the type library, you can use an attribute to prevent it from creating that "default" interface. The "ClassInterfaceAttribute" can be applied to a class with the "ClassInterfaceType.None" enumeration member. It can also be applied at the Assembly level where it would apply to every public class in the Assembly. Using the example above, you can make a small modification to the Bee class and apply the attribute:
[VB.NET]
Option Strict On
Option Explicit On
Imports System.Runtime.InteropServices
Namespace QuickNET
<ClassInterface(ClassInterfaceType.None)> _
Public Class Bee
Implements IBee
Public Sub FindFlower() Implements IBee.FindFlower
End Sub
Public Sub MakeHoney() Implements IBee.MakeHoney
End Sub
End Class
End Namespace
[C#]
using System;
using System.Runtime.InteropServices;
namespace QuickNET
{
[ClassInterface(ClassInterfaceType.None)]
public class Bee : IBee
{
public void FindFlower()
{
}
public void MakeHoney()
{
}
}
}
After compiling this and re-creating the type library, OLE View shows a much cleaner structure:
Not only do you not have the "_Bee" interface, but your IBee interface is marked as the default interface. Why is that? If the ClassInterfaceAttribute specifies that no automatic interface is to be generated, TLBEXP.EXE will take the first interface implemented by the class and make it the default interface. So if you plan on exposing your .NET class to COM, take into account which interface is the first interface implemented in the source code (either through the VB.NET "Implements" keyword or the first interface listed after the ":" in your C# class definition).
Controlling Your GUIDs
The last thing you need to control is your GUIDs - Globally Unique Identifiers. You've probably seen a GUID before. Here's a sample:
82CC3E6A-148E-4b77-866E-598DBEDC5C74
Every interface in COM and every coclass (creatable class object) is identified by a unique GUID. VB6 controls GUID creation for you. You can "coax" VB6 into using the same GUIDs for classes and interfaces by using the "Binary Compatibility" mode of your VB6 project when recompiling. The .NET TLBEXP.EXE utility will also auto-generate a GUID for every interface and class it exports to COM. But you can use attributes to define the GUID yourself.
Why should you care? Whenever you register a .NET object as a COM component, registry entries are created. Some of those registry entries are the GUIDs used to identify your classes and interfaces to COM. If you don't specify a specific GUID, TLBEXP.EXE will generate a new one every time you re-create your COM type library. COM clients already developed could have references to the old GUID and would no longer work since the GUIDs changed. Likewise, if you do control and define a specific GUID for your classes and interfaces, then the GUIDs aren't changing and your COM clients won't need to be recompiled and will continue to work even with new versions of your .NET component.
You can use attributes to define a GUID for your classes and interfaces. Instead of trying to "make up" your own unique GUID, VS .NET (along with previous versions of Visual Studio) comes with a tool for generating a GUID called (interestingly enough) guidgen.exe. This should be found in the "\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools" directory. Double-click on it and you should see a screen similar to the one below:
GUIDs are used in many different places, so guidgen supports creating a GUID in four different formats. For your purposes, you need the fourth format: Registry Format. You don't need the opening and closing braces, but you can trim those off. Press "New GUID" to generate a new GUID and then "Copy" to copy it to the clipboard.
Now define a GUID for your IBee interface (make sure you've added the System.Runtime.InteropServices namespace to your code). Paste the clipboard into the "Guid" attribute and remove the leading and trailing braces:
[VB.NET]
<Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")> _
Public Interface IBee
Sub FindFlower()
Sub MakeHoney()
End Interface
[C#]
[Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
public interface IBee
{
void FindFlower();
void MakeHoney();
}
Now the class also needs its own GUID. Go back to guidgen, click "New GUID" and then "Copy". Now apply the Guid attribute to the class with the new GUID value (again, trim the braces):
[VB.NET]
<ClassInterface(ClassInterfaceType.None), _
Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")> _
Public Class Bee
Implements IBee
Public Sub FindFlower() Implements IBee.FindFlower
End Sub
Public Sub MakeHoney() Implements IBee.MakeHoney
End Sub
End Class
[C#]
[ClassInterface(ClassInterfaceType.None)]
[Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
public class Bee : IBee
{
public void FindFlower()
{
}
public void MakeHoney()
{
}
}
In this article, Thiru Thangarathinam demonstrates the different classes and features available through the My namespace. By providing a speed-dial that allows you to more quickly and effectively utilize .NET framework functionalities in your application, the My feature provides huge productivity improvements for .NET developers. [Read This Article][Top]
Thiru Thangarathinam discusses taking advantage of the integation between
the .NET CLR and SQL Server 2005 in order to do things like create triggers
using managed code. [Read This Article][Top]
Developers often use brute force coding to marshal data between the GUI and application objects. In this article, Luther Stanton explains how to use .NET's out-of-the box data-binding functionality to make this job much easier. [Read This Article][Top]
Ambrose Little provides the complete source code for his 'Perfect Service'
and explains how the .NET Service Manager enables features such as drag-n-drop deployment. [Read This Article][Top]
There is broad-reaching debate about remoting, Web services, Enterprise Services, and DCOM. In short, it is a debate about the best technology to use when implementing client/server communication in .NET. Rocky Lhotka shares his thoughts on the issue while offering clear explanations of basic application architecture terminology. [Read This Article][Top]
This article provides and excellent foundation for COM Interop. It reviews COM's background, explains how VB6 interacts with COM, and then shows how to design .NET components to smoothly interact with COM. [Read This Article][Top]
The first article in this two-part series shows how to get Ambrose Little's .NET Service Manager running and then how to add plug-n-play services to it using drag-n-drop or XCOPY. [Read This Article][Top]
Although generics are extremely useful, they also seem to have a certain mystique that cannot be readily explained. This article hopes to remove that aura of mystery by showing just how easy it is to use generics and how useful they can be in many common situations. [Read This Article][Top]
When implementing custom components that require access to restricted resources, implicit impersonation must be used. Jay Nathan shows how to create a class that makes using .NET Impersonation a snap. [Read This Article][Top]
Tony Arslan shows how to use VS .NET's custom deployment feature to create configuration files on the target machine during installation. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.