超出的COM服务器卡住了

我正在使用out-proc COM服务器(使用DECLARE_CLASSFACTORY_SINGLETON实现的COM单例“引擎”),它在STA(CComSingleThreadModel,_ATL_APARTMENT_THREADED)中工作。

COM服务器客户端:

  1. ActiveScript(JScript),(我使用AddNamedItem传递引擎引用)。
  2. 两个独立的IE BHO。

BHOs定期调用Engine :: dispatchEvent,Engine调用ActiveScript的JavaScript函数。 这个架构工作完美,直到我同时打开两个BHO。

如果我打开两个BHO,当我调用ActiveScript的函数(使用IDispatch / Invoke)时发生卡住。 我不创build任何额外的线程。

一些说明:

  • 如果我不把从BHO检索到的对象传递给ActiveScript(或者用引擎中创build的同一对象replace它),一切正常。
  • 只有当JScript垃圾收集器试图释放从BHO检索到的对象(callstack中的IUnknown_Release_Proxy)时才会发生阻塞。

调用堆栈:

> 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 

实施细节:

 // Engine (out of process COM singleton) class ATL_NO_VTABLE CEngine : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CEngine, &CLSID_Engine>, public IDispatchImpl<IEngine, &IID_IEngine, &LIBID_EngineLib, /*wMajor =*/ 1, /*wMinor =*/ 0> { 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<CComSingleThreadModel>, public CComCoClass<CPropertyStore, &CLSID_NULL>, public IDispatch { BEGIN_COM_MAP(CPropertyStore) COM_INTERFACE_ENTRY(IUnknown) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() BOOL SetProperty(CString 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<CComSingleThreadModel>, public CComCoClass<CBHO, &CLSID_BHO>, public IObjectWithSiteImpl<CBHO>, public IDispatchImpl<IBHO, &IID_IBHO, &LIBID_Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IDispEventImpl<1, CBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 0> { void onEvent(...) { if(m_pEngine == NULL && SUCCEEDED(m_pEngine.CoCreateInstance(CLSID_Engine))) { CComObject<CPropertyStore> *pEvent = NULL; HRESULT hRes = CComObject<CPropertyStore>::CreateInstance(&pEvent); CComVariant varEvent(pEvent); CComVariant varName(L"EventName"); CComVariant varResult; m_pEngine.Invoke2(L"dispatchEvent", &varName, &varEvent, &varResult); } } } 

您的BHO(浏览器帮助程序对象)是在一个单独的线程公寓。 每个在另一个STA(不同的线程)上对一个对象进行的COM调用,在一个方法调用中被“转换”之前,会被一个消息队列中的消息排序。

这通常不是问题,因为大多数时间调用是由单线程的GUI触发的。 COM调用等待轮到WM_LBUTTONUP消息等。

你的情况会发生什么呢,就是在服务onEvent ,你把BHO对象发送给另一个线程,在另一个进程中,你的进程外COM对象。 当您尝试从您的MTA公寓回调到原始对象时,Windows消息将发布到您的BHO STA线程在承载它的Internet Explorer进程中。 但消息队列仍在忙于处理原始请求。

这解释了你的死锁,以及为什么传递一个字符串的作品。