skip to Main Content

I need to create a COM class in .NET8 that needs to be accessible to Excel.

After watching this video, I implemented the following test bed class:

namespace COMTestBedCS
{
    [Guid("26a0aa6d-5aba-458f-92b4-b9a30ae0c65c")]
    [GeneratedComInterface]
    public partial interface ITestBed
    {
        int GetXPTO();
        void SetXPTO(int value);
    }

    [Guid("3e178f98-522e-4e95-8a9c-6d80dc48b7d5")]
    [GeneratedComClass]
    public partial class TestBed : ITestBed
    {
        private int _XPTO = 1024;

        public int GetXPTO() => _XPTO;
        public void SetXPTO(int value)=>_XPTO = value;
    }
}

The project compiles correctly, without errors. However, when I try to reference this test bed in Excel, I get the following error: Can't add a reference to the specified file.

If I try to use regsvr32, I get the following error:

regsvr32 error message

What am I doing wrong?


For completion sake, here’s the project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

</Project>

Update

I was able to compile the code correctly and register it using regsvr32. However, I’m still unable to reference it in Excel.

First, I can’t find it on the reference list. If I try to browse for the dll (either the assembly or the comhost), it fails with the message: Can't add a reference to the specified file.

All that’s left now it to add it as a Reference in the Excel VBA.

New Code

Project

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <RootNamespace>COMTestBedCS</RootNamespace>
    <TargetFramework>net8.0-windows</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AllowUnsafeBlocks>False</AllowUnsafeBlocks>
    <EnableComHosting>true</EnableComHosting>
    <RegisterForComInterop>True</RegisterForComInterop>
    <Platforms>x86</Platforms>
    <RegisterAssemblyMSBuildArchitecture>x86</RegisterAssemblyMSBuildArchitecture>
  </PropertyGroup>

</Project>

Code

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

namespace COMTestBedCS
{
    [Guid("26a0aa6d-5aba-458f-92b4-b9a30ae0c65c")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ITestBed
    {
        int GetXPTO();
        void SetXPTO(int value);
    }

    [Guid("3e178f98-522e-4e95-8a9c-6d80dc48b7d5")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    internal class TestBed : ITestBed
    {
        private int _XPTO = 1024;

        public int GetXPTO() => _XPTO;
        public void SetXPTO(int value)=>_XPTO = value;
    }
}

OBS.: To compile successfully, I had to, for some reason, build the project twice. The first time Visual Studio is unable to successfully build, with the following error:

MSB3217 
Cannot register assembly "<assembly file>" Could not load file or 
assembly 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. 
The system cannot find the file specified.`

Then I have to rebuild it again without cleaning the project.
Only then the project compile successfully.


Update 2

If anyone wants to help, I put this test bed project on GitHub here.

2

Answers


    1. Add the <EnableComHosting>true</EnableComHosting> property into the <PropertyGroup> in the csproj. This will tell dotnet to generate *.comhost.dll.
    2. Make the TestBed class internal instead of public. This will solve the build problem (I don’t know why, but from a general point of view, the implementation (the TestBed class) should not be accessible outside the assembly in any way other than the generated factory).
    Login or Signup to reply.
  1. First, you must follow what’s detailed here Expose .NET Core components to COM, Generate the COM host:

    Open the .csproj project file and add
    <EnableComHosting>true</EnableComHosting> inside a
    <PropertyGroup></PropertyGroup> tag.

    But doing this will raise "The "GenerateClsidMap" task failed unexpectedly." error, as explained here:

    SYSLIB diagnostics for COM interop source generation

    SYSLIB1098 .NET COM hosting with EnableComHosting only supports
    built-in COM interop. It does not support source-generated COM interop
    with GeneratedComInterfaceAttribute.

    GeneratedComInterfaceAttribute is a new to .NET 8 feature, that you can’t use here in this scenario (exposing and hosting your own interfaces), so you’ll have to replace your code by something like this to revert back to "built-in" COM interop:

    [ComVisible(true)]
    [Guid("26a0aa6d-5aba-458f-92b4-b9a30ae0c65c")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public partial interface ITestBed
    {
        int GetXPTO();
        void SetXPTO(int value);
    }
    
    [ComVisible(true)]
    [Guid("3e178f98-522e-4e95-8a9c-6d80dc48b7d5")]
    [ClassInterface(ClassInterfaceType.None)]
    public partial class TestBed : ITestBed
    {
        private int _XPTO = 1024;
    
        public int GetXPTO() => _XPTO;
        public void SetXPTO(int value) => _XPTO = value;
    }
    

    Once the project is build, you can call regsvr32 <myfile>.comhost.dll (which has been generated by .NET Core) with sufficient rights.

    PS: You cannot use the RegisterForComInterop .csproj directive anymore as it’s reserved for .NET Framework (it internally uses the same code as Regasm which you cannot use either).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search