2

Having a very strange problem here. I'm attempting to make a .NET 8 library which will be called from Excel VBA via COM interop.

I've been following these two pieces of guidance:
Expose .NET Core components to COM
How to correctly create COM classes with .NET8?

Per that guidance, I've used the DSCOM tool to generate my .tlb file, which Excel can reference.

I've managed to make one working test class (with requisite interface), but I've been struggling to make a second one. My second test class was supposed to be more complicated, but through trial and error I made the second class simplier and simpler until I now have an exact duplicate of the first, working class, but with a different name.

Two classes and two interfaces, exactly the same members, differing only by name (and GUID). One set works, the other does not:

namespace MyTest.COM
{
    [Guid("9d02f386-c90e-4353-964a-d3bdbad2c9f3")]
    public class MyTestCOMDebug : IMyTestCOMDebug
    {
        public int Add(int x, int y) => x + y;
        public string ToUpper(string s) => s.ToUpper();
    }

    [Guid("e6c5eb34-8412-4f17-a8e7-ef5e091841c5")]
    public interface IMyTestCOMDebug
    {
        int Add(int x, int y);
        string ToUpper(string s);
    }

    [Guid("f56b05d8-21aa-4ce8-8dd4-271eb102fea4")]
    public class MyTestCOMDebug2 : IMyTestCOMDebug2
    {
        public int Add(int x, int y) => x + y;
        public string ToUpper(string s) => s.ToUpper();
    }

    [Guid("23b13668-d7a3-4cfa-8a75-4a9063b2edb7")]
    public interface IMyTestCOMDebug2
    {
        int Add(int x, int y);
        string ToUpper(string s);
    }
}

So I have MyTestCOMDebug : IMyTestCOMDebug and MyTestCOMDebug2 : IMyTestCOMDebug2. Same members, in the same file of the same project.

This is the project file:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <EnableComHosting>true</EnableComHosting>
        <EnableRegFreeCom>true</EnableRegFreeCom>
    </PropertyGroup>

    <ItemGroup>
      <Using Include="System.Runtime.InteropServices" />
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="dSPACE.Runtime.InteropServices.BuildTasks" Version="1.15.2">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
    </ItemGroup>

    <PropertyGroup>
        <DsComTypeLibraryEmbedAfterBuild>true</DsComTypeLibraryEmbedAfterBuild>
        <DsComOverideLibraryName>MyTest</DsComOverideLibraryName>
    </PropertyGroup>
</Project>

I also have an AssemblyInfo.cs file with the following assembly-level attributes:

[assembly: ComVisible(true)]
[assembly: Guid("99134f79-a83c-4354-94f0-cfa52ea1ec0e")]
[assembly: ClassInterface(ClassInterfaceType.None)]

The following VBA code shows the test which is throwing the error:

Sub Test1()
    Dim dbg As MyTest.IMyTestCOMDebug
    Set dbg = CreateObject("MyTest.COM.MyTestCOMDebug")
    Dim b As Integer
    b = dbg.Add(2, 2)
    MsgBox b
End Sub

Sub Test2()
    Dim dbg As MyTest.IMyTestCOMDebug2
    Set dbg = CreateObject("MyTest.COM.MyTestCOMDebug2")
    Dim b As Integer
    b = dbg.Add(2, 2)
    MsgBox b
End Sub

Test1 runs without error and correctly outputs 4.

Test2 gives the following error:

Run-time error '429':

ActiveX component can't create object

This error occurs on the line:

Set dbg = CreateObject("MyTest.COM.MyTestCOMDebug2")

Now, why in the world does the first version of the class work, while the second one does not?

As you can see from the project file, I'm using registry-free COM, so there's no worry about having to unregister and re-register the library. I've made double sure that the file I'm testing with is the newly-built version. I've rebooted the computer. I'm testing with a completely new Excel file.

What am I missing here?

7
  • Try adding [ComDefaultInterface(typeof(IMyTestCOMDebug))] above the class declaration for MyTestCOMDebug and [ComDefaultInterface(typeof(IMyTestCOMDebug2))] above the class declaration for MyTestCOMDebug2. You may also consider adding [ComVisible(true)] above all of the class declarations and also above the interface definitions as shown in the Expose .NET Core components to COM that you mentioned in your post. Commented May 31 at 3:20
  • The following may be of interest: ClassInterfaceType and ComInterfaceType Commented May 31 at 3:31
  • Adding ComDefaultInterface to both classes did not change the error. I already have ComVisible(true) set for the entire assembly (I've updated my question to show this now). Similarly, I've set ClassInterface(ClassInterfaceType.None) for the entire assembly. I am intentionally leaving ComInterfaceType with the default value. Commented May 31 at 4:14
  • 3
    Its a classic when using/testing registry-free COM, we can often create issues with mixing registering (regsvr32) and not registering during the developing process. This can end up messing around with the configuration/setup/machine/files/registry where we don't understand what's going on. I'd suggest to create brand new classes and guids, never ever touch regsvr32 or any registration code, and start all over. Or post some reproducing project somewhere so we can have a look. Registry-free is quite difficult (but interesting at the same time) Commented May 31 at 6:29
  • 3
    All that EnableRegFreeCom does is it gets the toolchain to generate a .manifest file with the registration information. Key is what you do next with that file. You're supposed to pass it to the client programmer so they can embed it in their own app and use your library without having to register it. You probably see the problem now, this can't work for an Office app. You'll have to forget about registry-free COM. It probably worked for the first attempt because it relied on registry keys that were written before you tried to make EnableRegFreeCom work. Commented May 31 at 14:27

1 Answer 1

1

For the sake of not leaving the question "unanswered", I'll explain briefly here what the comments on my question helped me to realize.

It's a classic when using/testing registry-free COM, we can often create issues with mixing registering (regsvr32) and not registering during the developing process. This can end up messing around with the configuration/setup/machine/files/registry where we don't understand what's going on.

The reason only one of these objects was working and not the other is because of COM registration shenanigans. I decided to try and make this project using reg-free COM, but only after I had attempted to use normal, registry-based COM with the same project.

I thought I had unregistered everything, but apparently I had not. So while I thought I had gotten that first class working reg-free, in reality it was only working because of the registry entries from my earlier tests. I added the second test class after the registration, which is why that class was not working.

After properly unregistering my COM dll with regsvr32 /u, neither class now work. Not the best outcome, but it does answer this question of "why one works but not the other". Now, onto my next question: how to get regfree COM to actually work for me in the first place.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.