Заранее прошу прощения за ТОО Длинный пост, но, учитывая, что в нем задействовано много деталей, и я не уверен, где могу ошибиться, следовательно, мне нужно опубликовать все задействованные детали. Пожалуйста, не предполагайте ничего, я могу сделать любую ошибку здесь.
Проблема:
1) Реальная проблема: просто изучение COM.
2) Мне нужно передать указатель IDispatch из моего консольного клиента (ConsoleClient) в C ++ в качестве параметра для функции COM в службе Windows (WinService). Этот указатель IDispatch действует как обратный вызов. WinService знает имя вызываемой функции. Все мои попытки оказались безуспешными. Я получаю различные ошибки, такие как: не реализовано, RPC недоступен и т. Д.
НОТА: Следующая ошибка верна! Я отредактировал после того, как @Igor указал на мою ошибку: иметь do-while (который я удалил из main ()) в потоке STA. Я добавил насос сообщений (GetMessage, TranslateMessage и DispatchMessage) в поток main () (вместо do-while), и моя проблема была исправлена!
У меня есть WinService, предоставляющий следующий интерфейс COM (файл idl WinService):
[
object,
uuid(DBE8BC31-9D2B-4F4B-903A-B40473408DE9),
dual,
nonextensible,
pointer_default(unique)
]
interface IWinService : IDispatch
{
///Here there are more functions..
[id(4), helpstring("Interface HELP")]
HRESULT WinServiceCOMfunction( [in] VARIANT vCallback, [out, retval] LONG* pReturn );
//Here there even more functions...
};
[
uuid(DEF3BFAE-ADF4-493B-8D01-E47A279225C5),
version(1.0),
helpstring("LIB HELP")
]
library WinServiceLib
{
importlib("stdole2.tlb");
[
uuid(AC290DC9-8CB4-4502-A73C-2BDAEC4B215A)
]
coclass CoWinService
{
[default] interface IWinService;
};
}
WinService внутренне ожидает, что vCallBack.vt = VT_DISPATCH, т.е. это указатель обратного вызова, который мне нужно передать, когда я вызываю функцию WinServiceCOM из моего приложения ConsoleClient.EXE. WinService знает имя функции, которая действует как обратный вызов. Т.е. WinService вызывает из IDispatch-pointer-callback GetIDsOfName с «FunctionCallback«как параметр. Однако здесь я получаю различные ошибки в WinService, такие как: Не реализовано, RPC недоступен и т. д. Затем обратный вызов не выполняется (ConsoleClient не получает ничего обратно).
Что я сделал до сих пор (весь следующий исходный код находится в ConsoleClient.EXE)
ConsoleClient.EXE main.cpp:
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
//To be brief: Let's think that I already have an IDispatch pointer to the WinService interface IWinService:
CComPtr<IDispatch> pIWinService; //pIWinService
//Here I queried the IWinService interface...It was successful!!!
OLECHAR * winServiceNameFunction = L"WinServiceCOMfunction";
DISPID dispid;
hr = pIWinService->GetIDsOfNames( IID_NULL, &winServiceNameFunction, 1, LOCALE_USER_DEFAULT, &dispid );
if ( FAILED( hr ) )
{
wprintf( L"GetIDsOfNames failed" );
return 1;
}
else
{
wprintf( L"GetIDsOfNames succeeded!" );
}
CComPtr<IDispatch> consoleClientCallback( new CConsoleClientInterface() );
CConsoleClientInterface *sanity = dynamic_cast<CConsoleClientInterface *>( consoleClientCallback.p );
if ( nullptr == pAutoCallback )
{
wprintf( L"CConsoleClientInterface pointer failed\n" );
return 1;
}
else
{
wprintf( L"CConsoleClientInterface pointer succeeded\n" );
}
DWORD dwRegister;
hr = CoRegisterClassObject(CLSID_CoConsoleClient, consoleClientCallback.p,
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister);
if(FAILED(hr))
{
wprintf(L"CoRegisterClassObject Failed\n");
return 1;
}
else
{
wprintf(L"CoRegisterClassObject Succeeded\n");
}
CComVariant paramCallback( consoleClientCallback.Detach() );
VARIANTARG varParams[] = { paramCallback };
DISPPARAMS dispparams = { vArgs, NULL, 1, 0 };
hr = pPtr->Invoke(
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&dispparams,
NULL,
NULL,
NULL);
if ( FAILED( hr ) )
{
wprintf( L"Invoke failed" );
return 1;
}
else
{
wprintf( L"Invoke Succeeded" );
}
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CoRevokeClassObject(dwRegister);
CoUninitialize();
return 0;
}
Все основное успешно выполнено. Тем не менее, я ничего не получаю от обратного вызова. Когда WinService пытается выполнить QueryInterface из обратного вызова указателя IDispatch, он выдает ошибки типа: Не реализовано, RPC недоступен, среди прочего …
Шаблонный класс для создания интерфейса ConsoleClient IDispatch
От: http://blogs.msdn.com/b/oldnewthing/archive/2013/06/12/10425215.aspx
disInterfaceBase.h:
template<typename DispInterface>
class CDispInterfaceBase : public DispInterface
{
public:
CDispInterfaceBase() : m_cRef(1), m_dwCookie(0) { }
/* IUnknown */
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
*ppv = nullptr;
HRESULT hr = E_NOINTERFACE;
if (riid == IID_IUnknown || riid == IID_IDispatch ||
riid == __uuidof(DispInterface))
{
*ppv = static_cast<DispInterface *>
(static_cast<IDispatch*>(this));
AddRef();
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement(&m_cRef);
if (cRef == 0) delete this;
return cRef;
}
// *** IDispatch ***
IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo)
{
*pctinfo = 0;
return E_NOTIMPL;
}
IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid,
ITypeInfo **ppTInfo)
{
*ppTInfo = nullptr;
return E_NOTIMPL;
}
IFACEMETHODIMP GetIDsOfNames(REFIID, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
return E_NOTIMPL;
}
IFACEMETHODIMP Invoke(
DISPID dispid, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS *pdispparams, VARIANT *pvarResult,
EXCEPINFO *pexcepinfo, UINT *puArgErr)
{
if (pvarResult) VariantInit(pvarResult);
return SimpleInvoke(dispid, pdispparams, pvarResult);
}
// Derived class must implement SimpleInvoke
virtual HRESULT SimpleInvoke(DISPID dispid,
DISPPARAMS *pdispparams, VARIANT *pvarResult) = 0;
public:
HRESULT Connect(IUnknown *punk)
{
HRESULT hr = S_OK;
CComPtr<IConnectionPointContainer> spcpc;
if (SUCCEEDED(hr)) {
hr = punk->QueryInterface(IID_PPV_ARGS(&spcpc));
}
if (SUCCEEDED(hr)) {
hr = spcpc->FindConnectionPoint(__uuidof(DispInterface), &m_spcp);
}
if (SUCCEEDED(hr)) {
hr = m_spcp->Advise(this, &m_dwCookie);
}
return hr;
}
void Disconnect()
{
if (m_dwCookie) {
m_spcp->Unadvise(m_dwCookie);
m_spcp.Release();
m_dwCookie = 0;
}
}
private:
LONG m_cRef;
CComPtr<IConnectionPoint> m_spcp;
DWORD m_dwCookie;
};
Фактическая реализация IDispatch в ConsoleClient
ConsoleClient.h
class CConsoleClientInterface : public CDispInterfaceBase<ICONSOLEINTERFACE>
{
public:
CConsoleClientInterface() { }
~CConsoleClientInterface() { }
STDMETHODIMP GetIDsOfNames(REFIID, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
HRESULT hr = E_FAIL;
if(_wcsicmp(*rgszNames, L"FunctionCallback") == 0)
{
*rgDispId = 1;
hr= S_OK;
}
else
{
hr= DISP_E_UNKNOWNINTERFACE;
}
if(FAILED(hr))
{
std::cout << L"FAILED\n";
}
return hr;
}
HRESULT SimpleInvoke(
DISPID dispid, DISPPARAMS *pdispparams, VARIANT *pvarResult)
{
// switch (dispid)
// {
// case 4:
std::cout << L"SimpleInvoke" << std::endl; //This is never printed (for the error that I am trying to figure out)
HRESULT hr = FunctionCallback( pdispparams->rgvarg[1].intVal, pdispparams->rgvarg[0].parray );
// break;
// }
return hr;
}
HRESULT FunctionCallback( LONG longValue, LPSAFEARRAY safearray )
{
//So simple, I just want that this value is printed in the ConsoleClient's console! Unfortunately this is not happening!
std::cout << longValue << std::endl;
return S_OK;
}
};
И ConsoleClient.idl
[
object,
uuid(CD08B160-558A-4251-885C-173A08A461F1),
dual,
nonextensible,
helpstring("ICONSOLEINTERFACE Interface"),
pointer_default(unique)
]
interface ICONSOLEINTERFACE : IDispatch{
[id(1), helpstring("method FunctionCallback")]
HRESULT FunctionCallback([in] LONG longValue, [in] LPSAFEARRAY safeArray );
};
[
uuid(F3445A9E-555B-4729-952B-8B72B8DB2E37),
version(1.0),
helpstring("ConsoleClientLib Help Lib")
]
library ConsoleClientLib
{
importlib("stdole2.tlb");
[
uuid(EFF9EC78-3031-4558-9BA3-5B2641CCB304),
helpstring("CoConsoleClient Class")
]
coclass ConsoleClientInterface
{
[default] interface ICONSOLEINTERFACE;
};
};
Я создал:
HKEY_CLASSES_ROOT\CLSID\{EFF9EC78-3031-4558-9BA3-5B2641CCB304} //CLSID
(Default) REG_SZ = C:\Program Files\Common Files\ConsoleClient.EXE
HKEY_CLASSES_ROOT\CLSID\{EFF9EC78-3031-4558-9BA3-5B2641CCB304}\LocalServer32
ThreadingModel REG_SZ = Both
Также я зарегистрировал tlb, используя regtlibv12.exe
Извините за сообщение TOO LONG, но COM вынуждает меня сделать это, в частности, что я новичок в COM, и я не знаю, где я могу ошибиться.
ПРИМЕЧАНИЕ. Я уверен, что Invoke (из ConsoleClient.EXE) для COM-функции WinService (WinServiceCOMfunction) выполнен успешно (я отладил его и попал в эту функцию после Invoke).
ПРИМЕЧАНИЕ 2. Я уверен, что WinService, использующий обратный вызов, работает. Существуют другие реализации (на разных языках программирования), использующие этот механизм через ту же функцию (WinServiceCOMfunction) в WinService.
Любая помощь? Заранее спасибо!
Задача ещё не решена.