Following the Source generation for ComWrappers article from Microsoft, I've created a simple .NET 8 Class Library project in Visual Studio 2022 using the GeneratedComInterfaceAttribute attribute, with the intent of using this library in VB6.
My class is based on a similar class described in this other Stack Overflow question where .NET 5 is used.
This is the code for the default Class1.cs
:
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace ComHostTest
{
[GeneratedComClass]
[Guid("63C60D62-317F-4BAB-BB20-0AB41F34A345")]
public partial class Class1
{
public string SayHello() => "Hello World from .NET " + RuntimeInformation.FrameworkDescription;
}
}
This is my project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x86</PlatformTarget>
<EnableComHosting>true</EnableComHosting>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
</Project>
My build configuration and PlatformTarget are set for x86. The project builds properly and generates a ComHostTest.comhost.dll
:
Using an elevated command prompt, I successfully registered the comhost.dll:
Then, using either the VB6 IDE's References window or the OLEViewer, I do not see the ComHostTest assembly as an available COM type library:
Any idea why?
I have read this very similar question where the person uses .NET 8 also but not the GeneratedComInterfaceAttribute.
I adjusted my code following Simon Mourier's comments:
using System.Runtime.InteropServices;
namespace ComHostTest
{
[ComVisible(true)]
[Guid("72433A8C-941E-4876-8CC2-15F6F4235F32")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IComHostTest
{
public string SayHello();
}
[ComVisible(true)]
[Guid("63C60D62-317F-4BAB-BB20-0AB41F34A345")]
[ClassInterface(ClassInterfaceType.None)]
public class ComHostTest : IComHostTest
{
public string SayHello() => "Hello World from .NET " + RuntimeInformation.FrameworkDescription;
}
}
The project file was not touched since it already included <EnableComHosting>true</EnableComHosting>
.
I downloaded the dscom32 release from GitHub and then, from the Developer PowerShell window in Visual Studio, I executed the following command:
dscom32 tlbexport "D:\.NET Projects\Development\COM host\ComHostTest\bin\x86\Debug\net8.0\ComHostTest.dll"
This generated a ComHostTest TLB file which I included in my .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x86</PlatformTarget>
<EnableComHosting>true</EnableComHosting>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<ComHostTypeLibrary Include="ComHostTest.tlb" Id="1" />
</ItemGroup>
</Project>
Still no luck seeing it in the VB6 IDE but I must be close.
Following Simon Mourier's comments and his answer on this similar question, I have managed to get the library to show up in VB6's References list.
First, the easy part. Here is the code for the interface and class:
using System.Runtime.InteropServices;
namespace ComHostTest
{
[ComVisible(true)]
[Guid("72433A8C-941E-4876-8CC2-15F6F4235F32")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IComHostTest
{
public string SayHello();
}
[ComVisible(true)]
[Guid("63C60D62-317F-4BAB-BB20-0AB41F34A345")]
[ClassInterface(ClassInterfaceType.None)]
public class ComHostTest : IComHostTest
{
public string SayHello() => "Hello World! from .NET " + RuntimeInformation.FrameworkDescription;
}
}
You can use the Create GUID window in Visual Studio (Tools > Create GUID) to generate new values for the Guid() attributes.
The project file (.csproj
):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x86</PlatformTarget>
<EnableComHosting>true</EnableComHosting>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
</Project>
Set the project platform to x86 in the Configuration Manager. This will compile a 32-bit DLL which is what VB6 expects.
You should be able to build this project and get a ComHostTest.comhost.dll
. This is a COM server:
A COM server is any object that provides services to clients; these services are in the form of COM interface implementations that can be called by any client that is able to get a pointer to one of the interfaces on the server object.
Register this DLL using regsvr32
. This writes the IComHostTest
and ComHostTest
GUIDs to the Windows registry. From an elevated Command Prompt:
regsvr32 ComHostTest.comhost.dll
At this point, you can activate your COM class from a .NET project using [ComImport]
and [CoClass]
attributes, but we need to go further for VB6 to recognize the class.
For VB6 to reference your assembly, you first need to create a type library file (TLB) for it. If you don't already have it, get dscom32.exe
from GitHub. You can get the compiled version from dSPACE-group/dscom Releases. This tool will generate a TLB file from your DLL:
dscom32 tlbexport "D:\.NET Projects\Development\COM host\ComHostTest\bin\x86\Debug\net8.0\ComHostTest.dll"
You now have a TLB for your assembly.
To save you from manually running the command, you can set up a Build Task to create the TLB file automatically at compile time. You will need to add the dSPACE.Runtime.InteropServices.BuildTasks
package from NuGet. Please see Build Tasks in the dscom README. Once you've added the package, your .csproj
file will contain a new ItemGroup
with the default settings. You should not have to change anything in there to generate the TLB.
If you want to use/test the assembly on your machine, you can use the dscom32 to register the TLB:
dscom32 tlbregister "D:\.NET Projects\Development\COM host\ComHostTest\bin\x86\Debug\net8.0\ComHostTest.tlb"
This should return:
Type library was registered successfully
To register the TLB using the build task, you can use DsComRegisterTypeLibrariesAfterBuild
in your .csproj
. See the parameters section of the dscom README.
Finally, in VB6, open the References window and locate your library:
You should now be able to see and create an instance of your class in IntelliSense:
Run your VB6 code to make sure everything is working. You should be good to go at this point.
If you get the following error, you have not properly registered the <your project>.comhost.dll
:
Run-time error '-2147221164 (80040154)': Class not registered
If you need to make changes to your DLL:
You can create a setup project using the Microsoft Visual Studio Installer Projects 2022 Extension. Use the 'Extensions > Manage Extensions' menu item to install it if you do not already have it.
These instructions are based on this Visual Studio Installer Projects Extension and .NET article.
Your main concern here is to register the TLB and <your project>.comhost.dll
files as has been done above.
Publish Items
in the 'Add Project Output Group' window and click OK.PublishProfilePath
property to the Publish profile you created in step 1.Register
property to vsdrfCOM
.<your project>.comhost.dll
also, even though it will be included in the Publish profile. This will let you control the Register
property for that file.<your project>.comhost.dll
file and set the Register
property to vsdrfCOMSelfReg
.Two or more objects have the same target location
and Unable to create registration information for file named '<your project>.comhost.dll'
but you can ignore these.In the Publish profile, if you are using Framework-dependent Deployment mode, you can setup Prerequisites in your Setup project to make sure the correct .NET runtime is installed.
<your project>.comhost.dll
does not matter, but they both must happen.<ComHostTypeLibrary Include="ComHostTest.tlb" Id="1" />
line in the project file mentioned in the question text is not necessary.Application.Current.Dispatcher.Invoke
to update WPF view models, you might run into unexpected Null Reference exceptions when running your code in VB6/VBA since Application.Current
is null.All credit goes to Simon Mourier.