我使用out-of-proc COM服务器(COM单例“Engine”使用DECLARE_CLASSFACTORY_SINGLETON实现),它在STA(CComSingleThreadModel,_ATL_APARTMENT_THREADED)中工作.
@H_301_28@
您的BHO(浏览器助手对象)位于单线程公寓中.在另一个STA(不同的线程)上的一个对象的STA中的每个COM调用在消息队列中被消息排序,然后在方法调用中“转换”.
COM服务器客户端:
> ActiveScript(JScript),(我使用AddNamedItem传递引擎引用).
>两个独立的IE BHO.
BHO定期调用Engine :: dispatchEvent,Engine调用ActiveScript的JavaScript函数.
这个架构完美地工作,直到我同时打开两个BHO.
如果我打开两个BHO,当我调用ActiveScript的功能(使用IDispatch / Invoke)时,卡住了.
我不创建任何额外的线程.
一些注释:
>如果我没有将从BHO检索到的对象传递给ActiveScript(或将其替换为在Engine中创建的相同对象),一切都可以正常运行.
>只有当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,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,DISPID *rgDispId); STDMETHOD(Invoke)(DISPID dispIdMember,REFIID riid,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,/*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); } } }
这通常不是问题,因为大多数时间的呼叫是由单线程的GUI触发的. COM电话等待WM_LBUTTONUP消息等等.
在你的情况下会发生什么是在服务onEvent时,你的BHO对象发送到另一个线程,在另一个进程中,你的进程外COM对象.当您尝试从MTA appartment回调原始对象时,Windows邮件将在托管它的Internet Explorer进程中发布到您的BHO STA线程.但消息队列仍然在忙于服务原始请求.
这解释了你的僵局,以及为什么通过一个字符串的作品.