How to create and pass a COM IDispatch pointer as callback from a C++ Console Client to a C++ WinService successfully? -
in advance sorry long post, given there many parts involved, , not sure can making mistake, hence, need post parts involved. please not assume anything, can making type of error here.
problem:
1) real problem: learning com.
2) need pass idispatch pointer console client (consoleclient) in c++ parameter com function in windows service (winservice). idispatch pointer acts callback. winservice knows name of function called. attempts have been unsuccessulf. getting different errors like: no implemented, rpc unavailable, etc..
note: following error correct! edited after @igor pointed out error: have do-while (which removed main()) in sta thread. added message pump (getmessage, translatemessage , dispatchmessage) in main() thread (instead of do-while) , problem fixed!
i have winservice exposing following com interface (winservice's idl file):
[ object, uuid(dbe8bc31-9d2b-4f4b-903a-b40473408de9), dual, nonextensible, pointer_default(unique) ] interface iwinservice : idispatch { ///here there more functions.. [id(4), helpstring("interface help")] hresult winservicecomfunction( [in] variant vcallback, [out, retval] long* preturn ); //here there 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; }; }
the winservice internally expects vcallback.vt = vt_dispatch, i.e., callback pointer need pass in when invoke winservicecomfunction consoleclient.exe app. winservice knows name of function acts callback. i.e., winservice calls idispatch-pointer-callback getidsofname "functioncallback" parameter. however, here getting different errors in winservice like: no implemented, rpc unavailable, etc.. no callback executed (the consoleclient not receive back).
what have done far (all following source code found in consoleclient.exe) consoleclient.exe main.cpp:
int _tmain(int argc, _tchar* argv[]) { coinitialize(null); //to brief: let's think have idispatch pointer winservice interface iwinservice: ccomptr<idispatch> piwinservice; //piwinservice //here queried iwinservice interface...it 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; }
all main succesfully executed. however, never callback. when winservice tries queryinterface teh idispatch pointer callback, throws erros like: no implemented, rpc unavailable, among others...
template class create consoleclient idispatch interface from: 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; };
the actual idispatch implementation in 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 never printed (for error trying figure out) hresult hr = functioncallback( pdispparams->rgvarg[1].intval, pdispparams->rgvarg[0].parray ); // break; // } return hr; } hresult functioncallback( long longvalue, lpsafearray safearray ) { //so simple, want value printed in consoleclient's console! unfortunately not happening! std::cout << longvalue << std::endl; return s_ok; } };
and 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 lib") ] library consoleclientlib { importlib("stdole2.tlb"); [ uuid(eff9ec78-3031-4558-9ba3-5b2641ccb304), helpstring("coconsoleclient class") ] coclass consoleclientinterface { [default] interface iconsoleinterface; }; };
i have created:
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
also have registered tlb using regtlibv12.exe
sorry long post, com forces me this, in particular new com , not know can making mistake.
note: sure invoke (from consoleclient.exe) winservice com function (winservicecomfunction) successful (i debugged , hits inside function after invoke).
note2: sure winservice using callback works. there other implementations (in different programming languages) making use of mechanism via same function (winservicecomfunction) in winservice.
any help? in advance!