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;
}