Le serveur COM hors de processus est bloqué

J’utilise un serveur COM hors processus (COM singleton “Engine” implémenté en utilisant DECLARE_CLASSFACTORY_SINGLETON), il fonctionne dans STA (CComSingleThreadModel, _ATL_APARTMENT_THREADED).

Clients du serveur COM:

  1. ActiveScript (JScript), (Je transmets la référence du moteur à l’aide de AddNamedItem).
  2. Deux BHOs ​​IE indépendants.

Les BHO appellent périodiquement Engine :: dispatchEvent, Engine appelle les fonctions JavaScript d’ActiveScript. Cette architecture a parfaitement fonctionné jusqu’à ce que je lance simultanément deux BHO.

Si j’active deux BHO, le blocage se produit lorsque j’appelle la fonction d’ActiveScript (en utilisant IDispatch / Invoke). Je ne crée pas de threads supplémentaires.

Quelques notes:

  • Si je ne transmets pas l’object récupéré de BHO à ActiveScript (ou le remplace par le même object créé dans Engine), tout fonctionne correctement.
  • Coincé ne se produit que lorsque le ramasse-miettes JScript tente de libérer l’object récupéré à partir de BHO (IUnknown_Release_Proxy dans callstack).

Callstack:

> ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes KernelBase.dll!_WaitForMultipleObjectsEx@20() + 0x100 bytes kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e bytes user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xe2 bytes ole32.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 1222 C++ ole32.dll!ModalLoop(CMessageCall * pcall) Line 211 C++ ole32.dll!ThreadSendReceive(CMessageCall * pCall) Line 4979 C++ ole32.dll!CRpcChannelBuffer::SwitchAptAndDispatchCall(CMessageCall * * ppCall) Line 4454 + 0x6 bytes C++ ole32.dll!CRpcChannelBuffer::SendReceive2(tagRPCOLEMESSAGE * pMessage, unsigned long * pstatus) Line 4076 C++ ole32.dll!CCliModalLoop::SendReceive(tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus, IInternalChannelBuffer * pChnl) Line 899 + 0x17 bytes C++ ole32.dll!CAptRpcChnl::SendReceive(tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus) Line 583 + 0xd bytes C++ ole32.dll!CCtxComChnl::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 734 + 0xa bytes C++ ole32.dll!NdrExtpProxySendReceive(void * pThis, _MIDL_STUB_MESSAGE * pStubMsg) Line 1932 C++ rpcrt4.dll!@NdrpProxySendReceive@4() + 0xe bytes rpcrt4.dll!_NdrClientCall2() + 0x144 bytes ole32.dll!ObjectStublessClient(void * ParamAddress, long Method) Line 474 + 0x8 bytes C++ ole32.dll!_ObjectStubless@0() Line 154 Asm ole32.dll!RemoteReleaseRifRefHelper(IRemUnknown * pRemUnk, int fReleaseRemUnkProxy, int fProcessingPostedMessage, OXIDEntry * pOXIDEntry, unsigned short cRifRef, tagREMINTERFACEREF * pRifRef, IUnknown * pAsyncRelease) Line 6770 + 0xc bytes C++ ole32.dll!RemoteReleaseRifRef(CStdMarshal * pMarshal, OXIDEntry * pOXIDEntry, unsigned short cRifRef, tagREMINTERFACEREF * pRifRef) Line 6694 C++ ole32.dll!CStdMarshal::DisconnectCliIPIDs() Line 3964 C++ ole32.dll!CStdMarshal::Disconnect(unsigned long dwType) Line 3273 C++ ole32.dll!CStdIdentity::~CStdIdentity() Line 312 C++ ole32.dll!CStdIdentity::`scalar deleting destructor'() + 0xd bytes C++ ole32.dll!CStdIdentity::CInternalUnk::Release() Line 767 C++ ole32.dll!IUnknown_Release_Proxy(IUnknown * This) Line 1773 C++ oleaut32.dll!_VariantClear@4() + 0xac9 bytes jscript.dll!VAR::Clear() + 0x50 bytes jscript.dll!GcAlloc::ReclaimGarbage() + 0xa2 bytes jscript.dll!GcContext::Reclaim() + 0x8e bytes jscript.dll!GcContext::CollectCore() - 0x72f bytes jscript.dll!GcContext::Collect() + 0x34 bytes jscript.dll!CScriptRuntime::Run() - 0x864f bytes jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes jscript.dll!ScrFncObj::Call() + 0x84 bytes jscript.dll!NameTbl::InvokeInternal() + 0x113 bytes jscript.dll!VAR::InvokeByDispID() + 0x73 bytes jscript.dll!CScriptRuntime::Run() + 0x1d89 bytes jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes jscript.dll!ScrFncObj::Call() + 0x84 bytes jscript.dll!NameTbl::InvokeInternal() + 0x113 bytes jscript.dll!VAR::InvokeByDispID() + 0x73 bytes jscript.dll!CScriptRuntime::Run() + 0x1d89 bytes jscript.dll!ScrFncObj::CallWithFrameOnStack() + 0xf3 bytes jscript.dll!ScrFncObj::Call() + 0x84 bytes jscript.dll!NameTbl::InvokeInternal() + 0x12c6 bytes jscript.dll!VAR::InvokeByDispID() + 0x73 bytes jscript.dll!NameTbl::GetVal() + 0x3b bytes 

Détails d’implémentation:

 // Engine (out of process COM singleton) class ATL_NO_VTABLE CEngine : public CComObjectRootEx, public CComCoClass, public IDispatchImpl { DECLARE_CLASSFACTORY_SINGLETON(CEngine) STDMETHOD(dispatchEvent)(BSTR name, IDispatch* pEvent, VARIANT_BOOL* pbSuccess) { // pEvent is CPropertyStore instance ActiveScriptDispatch.Invoke1(L"FuncName", pEvent, &varResult); } } // BHO class CPropertyStore : public CComObjectRootEx, public CComCoClass, public IDispatch { BEGIN_COM_MAP(CPropertyStore) COM_INTERFACE_ENTRY(IUnknown) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() BOOL SetProperty(CSsortingng strName, VARIANT *value) { // Store value in CAtlArray } // IDispatch impl STDMETHOD(GetTypeInfoCount)(UINT *pctinfo); STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,EXCEPINFO *pExcepInfo, UINT *puArgErr); } class ATL_NO_VTABLE CBHO : public CComObjectRootEx, public CComCoClass, public IObjectWithSiteImpl, public IDispatchImpl, public IDispEventImpl { void onEvent(...) { if(m_pEngine == NULL && SUCCEEDED(m_pEngine.CoCreateInstance(CLSID_Engine))) { CComObject *pEvent = NULL; HRESULT hRes = CComObject::CreateInstance(&pEvent); CComVariant varEvent(pEvent); CComVariant varName(L"EventName"); CComVariant varResult; m_pEngine.Invoke2(L"dispatchEvent", &varName, &varEvent, &varResult); } } } 

Votre BHO (Browser Helper Object) se trouve dans un appartement à un seul thread. Chaque appel COM dans un STA qui est fait à un object sur un autre STA (thread différent) est ordonné par un message dans une queue de messages avant qu’il ne soit “transformé” dans un appel de méthode.

Ce n’est généralement pas un problème, car la plupart des appels de temps sont déclenchés par l’interface graphique qui est mono-threadée. Les appels COM attendent leur tour avec les messages WM_LBUTTONUP et autres.

Ce qui se passe dans votre cas, c’est que lors de la maintenance de onEvent , vous envoyez votre object BHO à un autre thread, dans un autre processus, votre object COM hors processus. Lorsque vous essayez de rappeler l’object d’origine à partir de votre appartement MTA, un message Windows est publié sur votre thread BHO STA dans le processus Internet Explorer qui l’héberge. Mais la queue de messages est toujours occupée à traiter la demande d’origine.

Cela explique votre impasse et pourquoi passer une chaîne fonctionne.