Use C++ ATL COM without registration for communication between 32-bit and 64-bit processes

178 Views Asked by At

I need to call into a 64-bit COM server from a 32-bit client. Both C++.

In order to avoid registration, I use the ROT to publish the class factory.

The IDispatch interface is used from the client to avoid marshaling issues.

I get an error at the first attempt to call a server method:

Error loading type library/DLL

CheckHR(driver.Invoke1(L"SetValue", &ret, &arg), "SetValue");

Why do I need a type library or proxy if I only use IDispatch?

All the code is below.

IDL:

import "unknwn.idl";
[
    object,
    uuid(C3498C72-22F8-477A-B8BF-A6BCE9DE0596),
    dual,
    nonextensible,
    helpstring("IZebraServer Interface"),
    pointer_default(unique)
]
interface IZebraServer : IDispatch
{
    [id(1), helpstring("method GetValue")] HRESULT GetValue([out, retval] int* pValue);
    [id(2), helpstring("method SetValue")] HRESULT SetValue(int value);
};

[
    uuid(1632C43C-7C08-451F-A341-A6486F0CE517),
    version(1.0),
]
library ZebraServer
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
        uuid(C2E745B1-6DA1-4DD4-AF10-5860182B66EF),
        helpstring("CZebraServer Class"),
    ]
    coclass CZebraServer
    {
        [default] interface IZebraServer;
    };
};

Here is the server code:

#include <SDKDDKVer.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <comdef.h>

#include <sstream>
#include <iostream>

using namespace ATL;

#include "ZebraServer_i.h"

inline void CheckHR(HRESULT hr, const char* msg)
{
    if (SUCCEEDED(hr))
        return;

    USES_CONVERSION;
    _com_error error(hr);
    std::ostringstream ss;
    ss << msg << "::" << W2A(error.ErrorMessage());
    throw std::exception(ss.str().c_str());
}

DWORD RegisterObjectInROT(
    IUnknown* pUnknown)
{
    // create binding context object
    CComPtr<IBindCtx> pBindCtx;
    CheckHR(CreateBindCtx(0, &pBindCtx), "CreateBindCtx");

    // Get a pointer to the ROT
    CComPtr<IRunningObjectTable> pRot;
    CheckHR(pBindCtx->GetRunningObjectTable(&pRot), "Getting ROT");

    // Create a moniker for the object
    CComPtr<IMoniker> pMoniker;
    CheckHR(CreateClassMoniker(CLSID_CZebraServer, &pMoniker), "CreateClassMoniker");
    // Register the object in the ROT with the specified display name
    DWORD dwRegisterCookie{};
    CheckHR(pRot->Register(
        ROTFLAGS_REGISTRATIONKEEPSALIVE, 
        pUnknown, 
        pMoniker, 
        &dwRegisterCookie), 
        "Register object");
    return dwRegisterCookie;
}

class ATL_NO_VTABLE CZebraServer : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CZebraServer, &CLSID_CZebraServer>,
    public IDispatchImpl<IZebraServer, &IID_IZebraServer, &LIBID_ZebraServer, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
    CZebraServer() {}

    DECLARE_NO_REGISTRY()

    BEGIN_COM_MAP(CZebraServer)
        COM_INTERFACE_ENTRY(IZebraServer)
        COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()

    // IZebraServer methods
    STDMETHOD(GetValue)(int* pVal)
    {
        *pVal = m_value;
        return S_OK;
    }

    STDMETHOD(SetValue)(int newVal)
    {
        m_value = newVal;
        return S_OK;
    }

private:
    int m_value = 0;
};

OBJECT_ENTRY_AUTO(__uuidof(CZebraServer), CZebraServer)

class ATL_NO_VTABLE CZebraServerFactory :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CZebraServerFactory, &CLSID_CZebraServer>,
    public IClassFactory
{
public:
    CZebraServerFactory() {}

    DECLARE_NO_REGISTRY()

    BEGIN_COM_MAP(CZebraServerFactory)
        COM_INTERFACE_ENTRY(IClassFactory)
    END_COM_MAP()

    // IClassFactory methods
    STDMETHOD(CreateInstance)(IUnknown* pUnkOuter, REFIID riid, void** ppv)
    {
        return CComCreator<CComObject<CZebraServer>>::CreateInstance(pUnkOuter, riid, ppv);
    }

    STDMETHOD(LockServer)(BOOL fLock);

};

OBJECT_ENTRY_AUTO(CLSID_CZebraServer, CZebraServerFactory)

CComObject<CZebraServerFactory>* g_pFactoryObject;

class CZebraServerModule : public ATL::CAtlExeModuleT<CZebraServerModule>
{
public:
    DECLARE_NO_REGISTRY()
    DECLARE_LIBID(LIBID_ZebraServer)

    HRESULT PreMessageLoop(int nShowCmd)
    {
        try {
            
            CheckHR(ATL::CAtlExeModuleT<CZebraServerModule>::PreMessageLoop(nShowCmd), 
                "CAtlExeModuleT<CZebraServerModule>::PreMessageLoop");
 
            std::cout << "Server started" << std::endl;

            // Create an instance of the factory class
            CheckHR(CComObject<CZebraServerFactory>::CreateInstance(&g_pFactoryObject), 
                "Create Class Factory Instance");

            // Query for the IUnknown interface
            DWORD dwRegisterCookie = RegisterObjectInROT(g_pFactoryObject);

        }
        catch (std::exception& ex)
        {
            std::cout << ex.what();
            return S_FALSE;
        }
        return S_OK;
    }
};
CZebraServerModule g_module;

HRESULT CZebraServerFactory::LockServer(BOOL fLock)
{
    if (fLock)
        g_module.Lock();
    else
        g_module.Unlock();
    return S_OK;
}

extern "C" int WINAPI _tWinMain(
    HINSTANCE /*hInstance*/,
    HINSTANCE /*hPrevInstance*/, 
    LPTSTR /*lpCmdLine*/, 
    int nShowCmd)
{
    g_module.WinMain(nShowCmd);
}

Client :

#include <iostream> 
#include <sstream>        
#include <atlbase.h>
#include <atlcom.h>
 
#import "C:\\Sources\\misc\\Zebra\\x64\\Debug\\AAA_ZebraServer.exe"

using namespace std;
using namespace ATL;
  
inline void CheckHR(HRESULT hr, const char* msg)
{
    if (SUCCEEDED(hr))
        return;

    USES_CONVERSION;
    _com_error error(hr);
    std::ostringstream ss;
    ss << msg << "::" << W2A(error.ErrorMessage());
    const auto& str = ss.str();
    throw std::exception(str.c_str());
}

class CComInitializer
{
public:
    CComInitializer()
    {
        CheckHR(CoInitialize(nullptr), "CoInitialize");
    }

    ~CComInitializer()
    {
        CoUninitialize();
    }
};

int main()
{
    try {
        CComInitializer coinit;

        // create binding context object
        CComPtr<IBindCtx> pBindCtx;
        CheckHR(CreateBindCtx(0, &pBindCtx), "CreateBindCtx");

        // Get a pointer to the ROT
        CComPtr<IRunningObjectTable> pRot;
        CheckHR(pBindCtx->GetRunningObjectTable(&pRot), "Get ROT");

        CComPtr<IMoniker> pMoniker;
        CheckHR(CreateClassMoniker(__uuidof(ZebraServer::CZebraServer), &pMoniker), "CreateClassMoniker");

        CComPtr<IUnknown> pUnknown;
        CheckHR(pRot->GetObject(pMoniker, &pUnknown), "pROT->GetObject");

        CComQIPtr<IClassFactory> pClassFactory(pUnknown);
        if (!pClassFactory)
            return 0;

        CComPtr<IDispatch> pDispatch;
        CheckHR(pClassFactory->CreateInstance(
            nullptr,IID_IDispatch,(void**)&pDispatch),
            "pClassFactory->CreateInstance");

        CComDispatchDriver driver(pDispatch);

        CComVariant ret, arg(100);
        CheckHR(driver.Invoke1(L"SetValue", &ret, &arg), "SetValue");
        CheckHR(driver.Invoke0(L"GetValue", &ret), "GetValue");
    }
    catch(...)
    {}
    return 0;
}
0

There are 0 best solutions below