Turn a Simple C# Dll into a Com Interop Component

Expose dll for COM Interop

There could be a few different things at play here. First, you'll want to use the regasm tool with the /codebase /tlb switch from an elevated command prompt (assuming Windows Vista, 7 or Windows Server 2008). Something like:

regasm "Path to Smurf.dll" /codebase /tlb

Once you have registered the dll using regasm you should be able to invoke it using VBS, VBA or VB6.

I was able to use early binding and late binding from VBA to call the Explode method. However, when I tried from VBScript I received the "ActiveX can't create object error as you did."

I'm running on Windows 7 64 bit, and I recalled that this can cause problems when compiling to 32 bit dlls and running them on 64 bit operating systems. On a whim, I fired up a command prompt and entered:

C:\Windows\SysWow64\CScript.exe "Path to VBScript"

The result was that the script ran correctly and displayed "Pop" on screen.

Here's the somewhat simplified C# code I used as well as the contents of the VBScript file.

namespace Smurf
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface IPants
{
string Explode(bool Loud);
}

[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPantsEvents
{
string Explode(bool Loud);
}

[ComVisible(true)]
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IPantsEvents))]
public class Pants : IPants
{

[ComVisible(true)]
public string Explode(bool Loud)
{
string result;
if (Loud)
result = "BANG";
else
result = "pop";
return result;
}
}
}

VBScript:

Dim x 
Set x = CreateObject("Smurf.Pants")
MsgBox (x.Explode(False))
Set x = Nothing

C# COM library cannot be referenced

The reason for inactive reference was found after examining compiler warnings and it was in explicit prohibition for .Net client to reference .Net COM server.
There is such a possibility, though, as described here:
https://learn.microsoft.com/en-us/samples/dotnet/samples/out-of-process-com-server/

I've checked, and this really worked fine, and it became possible for a .Net client to reference a Server.Contract.tlb from the example.
As a bonus, Microsoft added the possibility for .Net Core targeting COM server to be loaded into COM Surrogate or to be run as out-of-process (standalone exe).
This works indeed just for netcoreapp3.1 server, as when I've changed target framework to net48, no DllServer.comhost.dll (containing, most probably, proxy/stub code) was autogenerated as it did for netcoreapp3.1, the project property <EnableComHosting>true</EnableComHosting> obviously didn't have any effect.

Find COM DLL path from Com Interop Assembly

Once you've created an object from the respective COM server, its DLL must have been loaded. Assuming that the underlying COM server is implemented in "mycomserver.dll", you could use P/Invoke and call GetModuleHandle( "mycomserver.dll" ) -- that gives you the path of the DLL.

How does a C# DLL marked for COM interop work in a vb6 application

In your case, VB instantiates a COM Callable wrapper class (CCW) which lives inside the .NET assembly. The usual COM-type things happen here. First of all, COM looks up the GUID for the class in the registry, and finds the assembly DLL, which it loads into the process of the VB component. COM tries to find a function which retrieves a pointer to a standard COM interface, which you use to instantiate the COM class. You now have a COM object.

But that is not the whole story. When you instantiate the CCW, it also ensures that the .NET runtime is loaded, and then creates an instance of the .NET class. Your COM object has an interface which is based on the .NET interface. The CCW essentially forwards all calls from the COM interface to the .NET interface, converting the COM data types to .NET data types, and back again if you have return values and out parameters.

As for your second point - in this particular case, don't bother. When VB gets to the end of the procedure (or Exit Sub, or raises an error), it jumps to a subroutine which clears down all procedure level variables. If object variables are cleared, the reference count to the COM object is decremented. If the reference count is zero, the COM instance kills itself.

In your case, when the COM class kills itself, it takes extreme measures to ensure that the .NET object is destroyed, but you cannot rely on this behaviour, as with all .NET objects.



Related Topics



Leave a reply



Submit