This article attempts to provide a tutorial as well as help with the troubleshooting of your own interop issues.
I will describe the issues with using libraries written in C# or VB.NET in your VB6 applications. The reason,
this is an issue, is because there is a lot of new code available only in C# or VB.NET that access new functionality
of the OS. Or it could simply be the case that there is already existing C# code that's not available as a COM library, that you want to use.
What brought this on is that I needed to integrate the Windows Task Scheduler (Start/Run/Tasks) into one of my applications. I didn't want
to spend time wrapping the Task Scheduler's WinAPI calls, so I looked for a pre-cooked solution on Google.
I saw a bunch of code that didn't work very well
and finally settled on a pretty well debugged library (in its second version) on
the CodeProject web site.
This library is written in C#.
So, what most web sites tell you about Interop can essentially be boiled down to these steps:
- Create a strong key by running sn k mykey.snk from the command line.
- Add the path of this generated file to your AssemblyInfo.vb or .cs file in the following manner:
VB.NET: <Assembly: AssemblyKeyFile("c:\path\mykey.snk")>
C#: [assembly: AssemblyKeyFile(@"c:\path\mykey.snk")]
- After compiling your assembly to a DLL, you must register it for COM Interop from the command line:
regasm c:\path\MyDll.dll /tlb:MyDll.tlb
- Finally, you should install the DLL into the Global Assembly Cache by dragging the .DLL file into the c:\windows\assembly folder
So, by all means, do these steps above as they are necessary to lay the Interop groundwork. But if that's all you are going to do
before calling the library from VB6, you are in a world of hurt. Let's go over each of the issues
you will run into and how to overcome them.
Complaint: I distributed my application to a client, registered the assembly for Interop, but my VB6 application does not see the library
(e.g. getting error 429 - can't create ActiveX component). Or - I recompiled the C# library and now my VB6 app does not see the app.
The issue in both these complaints is that VB6 communicates to the outside libraries via GUIDs that are assigned to an object in a DLL.
These GUIDs are then stored with the DLL information in the registry, which is where VB6 apps look to find the linked libraries. Therein
lies the problem: .NET assemblies don't know anything about GUIDs by default. Your job is to assign them a unique GUID attribute.
You have to do this for each and every public class (at least the ones that you plan to call from VB6). Here is how:
VB.NET: <GuidAttribute("BA713700-522D-466e-8DD4-225884504678")>
C#: [GuidAttribute("BA713700-522D-466e-8DD4-225884504678")]
Now where do you get the GUIDs? In VS.NET go to the Tools menu and select Create Guid. You can copy the GUID from there.
Now when you recompile your .NET assembly or run regasm command on it, the assembly will have the a consistent set of
GUIDs, thus never breaking binary compatibility.
Complaint: I don't see the .NET assembly in the VB6 Object Browser or
I don't get any intellisense on .NET objects.
AFAIK, there are 2 types of COM interfaces. By default, the COM interface that is generated from the .NET assembly is
not the one VB6 knows much about. So, the solution is to ask .NET compiler to generate the interface that VB6 does know about.
To that end, add yet another attribute to all your public classes:
VB.NET: <ClassInterface(ClassInterfaceType.AutoDual)>
C#: [ClassInterface(ClassInterfaceType.AutoDual)]
Recompile your .NET code and you'll now see the .NET objects in your Object browser and get the Intellisense on those entities as well.
Complaint: Ok, I've added the GuidAttribute
and ClassInterface attribute. Now my code doesn't compile.
This one is easy. Add a an Imports/using statement at the top of the classes using these attributes:
VB.NET: Imports System.Runtime.InteropServices
C#: using System.Runtime.InteropServices;
Thanks to Simon Card for pointing this out.
Complaint: I can't instantiate .NET objects in VB6. Dim myObj as new DotNetObject
results in an error.
.NET has a concept of parametrized constructors. For instance:
class Stuff
{
//constructor
public Stuff(string TaskToDo)
{
...
}
}
//call this class
Stuff myStuff = new Stuff("Buy Groceries");
As you can see the only way to create a new Stuff object is to call it with a parameter. However, you can't do this in VB6
because it does not have parametrised constructors.
VB6: Dim myStuff as New Stuff("Buy Groceries") 'will not work - this violates VB6 syntax
VB6: Dim myStuff as New Stuff() 'will not work because the Stuff class in the C# assembly requires a parameter in the constructor.
So the only solution to this problem is to create a constructor in the C# class that takes no parameters. And, for a good measure create
a property which will could replace the TaskToDo parameter. Here is the resulting C# class.
class Stuff
{
string msStuffToDo = "";
//constructor without the parameter
public Stuff()
{
...
}
//constructor with the parameter
public Stuff(string TaskToDo)
{
...
}
public string TaskToDo
{
get { return msStuffToDo;}
set { msStuffToDo = value;}
}
}
//call this class from C# with parametrized constructor
Stuff myStuff = new Stuff("Buy Groceries");
//call this class from C# without parametrized constructor
Stuff myStuff = new Stuff();
myStuff.TaskToDo = "Buy Groceries";
Finally, we can now instantiate the Stuff class in VB6:
Dim myStuff as New Stuff() 'works because our C# class now provides a constructor with no parameters
myStuff.TaskToDo = "Buy Groceries"
Quick note, even thought I used Dim obj as New Object(), VB6 programmers should stick with this syntax, because of well-known issues,
I won't go into here:
Dim myStuff as Stuff()
Set myStuff = New Stuff()
myStuff.TaskToDo = "Buy Groceries"
...
Set myStuff = Nothing
Having addressed all these issues, you can now be sure that the .NET objects will behave as seamlessly as native COM objects.
By the way, these issues apply whether you are calling .NET objects from VB6, ASP or any other COM capable language, such as PHP, Delphi or Python.
Happy Interoping.