0

The background is that I have an external C# dll distributed as a private nuget package from colsultants, this library should be consumed by C# .net 4.x projects (and here no problem at all) but also by C++ projects written in Visual Studio.

Having already discarded approaches like interprocess communication with pipes/sockets/mailslots/memory sharing and to include the .net runtime in C++ projects in order to invoke the C# methods I thought that COM Interop would be the simplest solution to implement.

Following the samples on Microsoft Learn portal and other sources I've started declaring a simple interface and explicit class like this:

using System;
using System.Runtime.InteropServices;


namespace OrionLibCOM.Interfaces
{
    [ComVisible(true)]
    [Guid("E394732D-E4C3-4D82-BC5F-1B41CD897D66")]
    public interface IOrionLicensingCOM
    {
       void Initialize();
       void Activate(string ActivationCode);
       void Deactivate();
       void PullRemoteState();
       bool Refresh();
    }
}

using OrionLibCOM.Interfaces;
using System.Runtime.InteropServices;

namespace OrionLibCOM
{
[ComVisible(true)]
[Guid("03FC9138-468B-4B4A-A978-FDEC40C833BB")]
[ClassInterface(ClassInterfaceType.None)]
public class OrionLicensingCOM : IOrionLicensingCOM
{

    private ExternalLibFromNuget.MainClass _instance;

    public OrionLicensingCOM()
    {
        _instance = new ExternalLibFromNuget.MainClass();
    }

    public void Initialize()
    {
        _instance.Initialize();
    }
    public void Activate(string ActivationCode)
    {
        _instance.Activate(ActivationCode);   
    }
    
    public void Deactivate()
    {
        _instance.Deactivate();    
    }

    public void PullRemoteState()
    {
       _instance.PullRemoteState();
    }

    public bool Refresh()
    {
        return _instance.Refresh();
    }
}
}

Then, after succesfully registering the compiled dll with the command "RegAsm.exe mybinpath\OrionLibCOM.dll /codebase /tlb" I've made a small C++ client in order to test the function calls:

#import "<mybinpath>\OrionLibCOM.tlb"
#include <iostream>
#include <atlstr.h>

inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };


int main()
{
    try
    {
        TESTHR(CoInitialize(0));
        OrionLibCOM::IOrionLicensingCOMPtr LicPtr = nullptr;
        TESTHR(LicPtr.CreateInstance("OrionLibCOM.OrionLicensingCOM"));

        LicPtr->Initialize();
        LicPtr->Activate("asadsjfidsjfisd");
        bool status = LicPtr->Refresh();

         CoUninitialize();// Uninitialize COM
    }
    catch (const _com_error& e)
    {
        CStringW out;
        out.Format(L"Exception occurred. HR = %lx, error = %s", e.Error(), 
        e.ErrorMessage());
        MessageBoxW(NULL, out, L"Error", MB_OK);
    }


    return 0;
}

The problem is that I always get an exception when I try to create the instance of "OrionLibCOM.OrionLicensingCOM", the exception says that the class is not registered. About this the class not registered is the one coming from the external lib since, if I remove "new ExternalLibFromNuget.MainClass();" from "OrionLicensingCOM" class the code runs fine.

Now, since for obvious reasons I cannot mark the provided external lib as "ComVisible" is there a way to use this library, another way to call the COM instance or another way to use COM in order to make it work and use it from C++ code or it's simply not possible to achieve that?

user1809065
  • 105
  • 8
  • 1
    Yes it is possible, somewhere in the C# project settings there should be a register output flag. If not do a `xyz.dll /RegServer` on the command line. Using COM is a robust solution, another one is writing a c++/cli wrapper. – Pepijn Kramer May 25 '23 at 09:12
  • Hi, I've tried "xyz.dll /RegServer" but it says it works only with executables, soI tried "regsvr32 xyz.dll" but it says that entrypoint DllRegisterServer was not found so... a possible solution would be to implement a "DllRegisterServer" method in the COM class? – user1809065 May 25 '23 at 09:41
  • 1
    in 99% cases, "class not registered" means you've not registered the class (!) or you have registered in the wrong registry (32-bit registry vs 64-bit registry), ie: your client runs as 32bit and your COM object is registered in 64bit registry or the reverse. Use the proper (32 vs 64) regasm to register your .NET 4.x COM objects https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool – Simon Mourier May 25 '23 at 09:45
  • COM object and the C++ client are both x86 and the COM object was registered with regasm under "C:\Windows\Microsoft.NET\Framework\v4.0.30319", at this point I have doubts on the bitness of the external NuGet dll – user1809065 May 25 '23 at 10:12
  • 1
    A bitness mismatch of external nuget would exhibit as another type of problem, like BadImageFormatException or something like that, not class not registered. Check the progid "OrionLibCOM.OrionLicensingCOM" is ok or better, specify it using ProgidAttribute https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.progidattribute And to check everything use Procmon from sysinternals filter by file & registry and your exe https://learn.microsoft.com/en-us/sysinternals/downloads/procmon – Simon Mourier May 25 '23 at 10:26
  • I'll try later and let you know... for now thanks for the hints! – user1809065 May 25 '23 at 10:32
  • Hi, I've checked the progid of class "OrionLibCOM.OrionLicensingCOM" and it's ok, meanwhile I've found another Stackoverflow post https://stackoverflow.com/questions/7197506/how-to-repair-comexception-error-80040154 which says that if the library is compiled in AnyCPU mode and it runs on a x64 system very likely you'll get this error so, in the end I've asked the colsultants if they can provide a version of their dll compiled in x86 mode. – user1809065 May 26 '23 at 07:52
  • 1
    Compiling as AnyCpu is the best to do since you can only distribute one binary. But if you want to use it with 32-bit client you still have to register it in 32 bit registry, and if you want to use it with 64-bit client you still have to register it in 64 bit registry. – Simon Mourier May 26 '23 at 18:47
  • Hi, lately I found the problem and it wasn't the bitness of the library, in brief was because I've signed the assembly with a strong name key while the library provided was not signed, so I removed the signature and now it works. – user1809065 May 26 '23 at 21:14

1 Answers1

0

Finally I found the issue. The reason is that I've signed "OrionLibCom" assembly with a strong name key, and in this case I found that all dependant assemblies need to be signed too in order to be registered correctly by RegAsm, and that was not the case of the 3rd party dlls I got in the NuGet package which are in early development phase so, in the end I removed the signature from the assembly and got them working with COM Interop.

user1809065
  • 105
  • 8