Created COM object with .Net 8. It just works (TM) but I am struggling with deps.json and consuming with Native code

I have a .Net 4.72 DLL, exposed to COM using ComVisible(true) and the rest. I’ll put example in a sec. I have been modernizing this DLL to .Net 8. This DLL consumes some registered ocx files via Interops that are 32 bit, so I build the DLL always as x86. In .Net 4.72 you could use “Any CPU” for this. In .Net Core+ you have to explicitly target x86, which I think has to do with the comhost.dll that’s created now. This DLL is a bit of an abomination, consumed by an application that is entirely single threaded (PowerBuilder), so it is actually written as net8.0-windows with UseWindowsForms set to true to allow for a message pump so the UI doesn’t freeze entirely. Truly an old school Windows only application. And, it works. The PowerBuilder app is able to load the DLL just fine as I kept the GUID intact, as well as the ProgId.

However, because I’m never entirely satisfied when something doesn’t completely make sense to me, I modified (and also upgraded to net8.0-windows because it IS a Winforms app) the C# test app we install alongside the DLL to allow the user to load the DLL as a project reference, or as a COM object. It’s a quick way to verify things on the client where this package is installed. Here is where I hit problems two different ways.

What I found is that if I delete the deps.json file of the DLL, I can load it via it’s ProgId doing this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> var dynamic? _myDll = null;
var readerType = Type.GetTypeFromProgID("MyProgId");
_myDll = Activator.CreateInstance(readerType);
</code>
<code> var dynamic? _myDll = null; var readerType = Type.GetTypeFromProgID("MyProgId"); _myDll = Activator.CreateInstance(readerType); </code>
    var dynamic? _myDll = null;
    var readerType = Type.GetTypeFromProgID("MyProgId");
    _myDll = Activator.CreateInstance(readerType);

In the debugger this works every time. Installed, this throws an exception:
80040111 ClassFactory cannot supply requested class (Exception from HRESULT: 0x80040111 (CLASS_E_CLASSNOTAVAILABLE))
If I delete the generated deps.json file, however, it works via COM just fine from my C# app. I have exhaustedly gone through the generated file and verified every dependency is installed alongside the DLL, including dependencies of dependencies. I have verified the COM registration (though really that’s obvious because it does work.)

In frustration, I decided to go even older school and write a tiny console app in C++/Win32 to load the DLL via COM, CoInit the object, pull in the exposed interface, and call one method. I can’t get this to work either. I can load the COM object and I can instantiate it, but I cannot seem to get the interface to load properly. It’s been a long time since I’ve done C/C++ and longer since I’ve dealt with COM.

Here’s the gyst of what I have:
C#

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ProgId("MyProgId")]
[Guid("NewGuidAfterUpgrade")]
public class MyDll : IMyDll
{
//example
public int Connect()
{
//do stuff
return 0;
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("OldGuidBeforeUpgrade")]
public interface IMyDll
{
int Connect();
}
project file attributes:
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<PlatformTarget>x86</PlatformTarget>
<Platforms>x86</Platforms>
<RuntimeIdentifiers>win-x86</RuntimeIdentifiers>
<UseWindowsForms>true</UseWindowsForms>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>MyDll.snk</AssemblyOriginatorKeyFile>
<EnableComHosting>true</EnableComHosting>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
</code>
<code>[ComVisible(true)] [ClassInterface(ClassInterfaceType.AutoDispatch)] [ProgId("MyProgId")] [Guid("NewGuidAfterUpgrade")] public class MyDll : IMyDll { //example public int Connect() { //do stuff return 0; } } [ComVisible(true)] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("OldGuidBeforeUpgrade")] public interface IMyDll { int Connect(); } project file attributes: <PropertyGroup> <TargetFramework>net8.0-windows</TargetFramework> <PlatformTarget>x86</PlatformTarget> <Platforms>x86</Platforms> <RuntimeIdentifiers>win-x86</RuntimeIdentifiers> <UseWindowsForms>true</UseWindowsForms> <SignAssembly>true</SignAssembly> <AssemblyOriginatorKeyFile>MyDll.snk</AssemblyOriginatorKeyFile> <EnableComHosting>true</EnableComHosting> <EnableDynamicLoading>true</EnableDynamicLoading> </PropertyGroup> </code>
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ProgId("MyProgId")]
[Guid("NewGuidAfterUpgrade")]
public class MyDll : IMyDll
{
    //example
    public int Connect()
    {
        //do stuff
        return 0;
    }
}

[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("OldGuidBeforeUpgrade")]
public interface IMyDll
{
    int Connect();
}

project file attributes:
<PropertyGroup>
    <TargetFramework>net8.0-windows</TargetFramework>
    <PlatformTarget>x86</PlatformTarget>
    <Platforms>x86</Platforms>
    <RuntimeIdentifiers>win-x86</RuntimeIdentifiers>
    <UseWindowsForms>true</UseWindowsForms>
    <SignAssembly>true</SignAssembly>
    <AssemblyOriginatorKeyFile>MyDll.snk</AssemblyOriginatorKeyFile>
    <EnableComHosting>true</EnableComHosting>
    <EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>

Here’s one of the iterations of me trying to use in c++:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>#include <iostream>
#include <comdef.h>
#include <atlbase.h>
#include <comutil.h>
#pragma comment(lib, "comsuppw.lib")
static const GUID IID_IMyDll =
{ broken out guid };
interface IMyDll : public IDispatch
{
virtual HRESULT STDMETHODCALLTYPE Connect(int* result) = 0;
};
int main()
{
// Initialize COM
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
std::cerr << "Failed to initialize COM library." << std::endl;
return 1;
}
// Define the ProgID of the COM object
CLSID clsid;
hr = CLSIDFromProgID(L"MyProgId", &clsid);
if (FAILED(hr)) {
std::cerr << "Failed to get CLSID from ProgID." << std::endl;
CoUninitialize();
return 1;
}
// Create an instance of the COM object
CComPtr<IUnknown> pUnk;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk);
if (FAILED(hr)) {
std::cerr << "Failed to create COM object." << std::endl;
CoUninitialize();
return 1;
}
// Query for the IMyDll interface: This is where things go south.
CComPtr<ICardReader> pMyDll;
hr = pUnk->QueryInterface(IID_ICardReader, (void**)&pMyDll);
if (FAILED(hr)) {
std::cerr << "Failed to query ICardReader interface." << std::endl;
CoUninitialize();
return 1;
}
// Prepare the method name
OLECHAR* methodName = (OLECHAR*)L"Connect"; // Cast to OLECHAR*
DISPID dispid;
hr = pMyDll->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) {
std::cerr << "Failed to get method ID." << std::endl;
CoUninitialize();
return 1;
}
// Clean up
CoUninitialize();
return 0;
}
</code>
<code>#include <iostream> #include <comdef.h> #include <atlbase.h> #include <comutil.h> #pragma comment(lib, "comsuppw.lib") static const GUID IID_IMyDll = { broken out guid }; interface IMyDll : public IDispatch { virtual HRESULT STDMETHODCALLTYPE Connect(int* result) = 0; }; int main() { // Initialize COM HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { std::cerr << "Failed to initialize COM library." << std::endl; return 1; } // Define the ProgID of the COM object CLSID clsid; hr = CLSIDFromProgID(L"MyProgId", &clsid); if (FAILED(hr)) { std::cerr << "Failed to get CLSID from ProgID." << std::endl; CoUninitialize(); return 1; } // Create an instance of the COM object CComPtr<IUnknown> pUnk; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk); if (FAILED(hr)) { std::cerr << "Failed to create COM object." << std::endl; CoUninitialize(); return 1; } // Query for the IMyDll interface: This is where things go south. CComPtr<ICardReader> pMyDll; hr = pUnk->QueryInterface(IID_ICardReader, (void**)&pMyDll); if (FAILED(hr)) { std::cerr << "Failed to query ICardReader interface." << std::endl; CoUninitialize(); return 1; } // Prepare the method name OLECHAR* methodName = (OLECHAR*)L"Connect"; // Cast to OLECHAR* DISPID dispid; hr = pMyDll->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_USER_DEFAULT, &dispid); if (FAILED(hr)) { std::cerr << "Failed to get method ID." << std::endl; CoUninitialize(); return 1; } // Clean up CoUninitialize(); return 0; } </code>
#include <iostream>
#include <comdef.h>
#include <atlbase.h>
#include <comutil.h>

#pragma comment(lib, "comsuppw.lib")

static const GUID IID_IMyDll =
{ broken out guid };

interface IMyDll : public IDispatch
{
    virtual HRESULT STDMETHODCALLTYPE Connect(int* result) = 0;
};

int main()
{
    // Initialize COM
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        std::cerr << "Failed to initialize COM library." << std::endl;
        return 1;
    }

    // Define the ProgID of the COM object
    CLSID clsid;
    hr = CLSIDFromProgID(L"MyProgId", &clsid);
    if (FAILED(hr)) {
        std::cerr << "Failed to get CLSID from ProgID." << std::endl;
        CoUninitialize();
        return 1;
    }

    // Create an instance of the COM object
    CComPtr<IUnknown> pUnk;
    hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk);
    if (FAILED(hr)) {
        std::cerr << "Failed to create COM object." << std::endl;
        CoUninitialize();
        return 1;
    }

    // Query for the IMyDll interface: This is where things go south.
    CComPtr<ICardReader> pMyDll;
    hr = pUnk->QueryInterface(IID_ICardReader, (void**)&pMyDll);
    if (FAILED(hr)) {
        std::cerr << "Failed to query ICardReader interface." << std::endl;
        CoUninitialize();
        return 1;
    }

    // Prepare the method name
    OLECHAR* methodName = (OLECHAR*)L"Connect"; // Cast to OLECHAR*

    DISPID dispid;
    hr = pMyDll->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_USER_DEFAULT, &dispid);
    if (FAILED(hr)) {
        std::cerr << "Failed to get method ID." << std::endl;
        CoUninitialize();
        return 1;
    }

    // Clean up
    CoUninitialize();

    return 0;
}

I’ve tried several thing iterations around what happens after getting the pUnk loaded. This is truly successful. I went through quite a lot with the Debugger to verify it successfully initializes everything in the loaded COM object. Afterwards though, depending on the GUID, etc, I either flat out get an error, or I successfully load pMyDll but Connect returns OK without actually calling the method.

These days the latest versions of .Net have abandoned the TLB. It’s possible to roll your own, but that’s a bit outside my experience. Also, as mentioned earlier, this is all 32 bit code, and I am using SYSWOW regsvr32.

I’d love to better understand the C# deps.json issue, as well as what I may need to do above to get the C++ console app working.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật