我使用了进程外COM服务器(COM单例"引擎"使用DECLARE_CLASSFACTORY_SINGLETON实现),它在STA中工作(CComSingleThreadModel,_ ATL_APARTMENT_THREADED)。
COM服务器客户端:
BHO定期调用Engine :: dispatchEvent,Engine调用ActiveScript的JavaScript函数。 这个架构非常有效,直到我同时打开两个BHO。
如果我打开两个BHO,当我调用ActiveScript函数(使用IDispatch / Invoke)时会发生卡住。 我没有创建任何额外的帖子。
一些注意事项:
调用堆栈:
> 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);
}
}
}
答案 0 :(得分:0)
您的BHO(浏览器帮助程序对象)位于单个线程区域中。在对另一个STA(不同线程)上的对象进行的STA中的每个COM调用在方法调用中“转换”之前由消息队列中的消息排序。
这通常不是问题,因为大多数时间调用都是由单线程的GUI触发的。 COM调用等待WM_LBUTTONUP
消息等等。
在您的情况下发生的情况是,在服务onEvent
时,您将BHO对象发送到另一个线程,在另一个进程中,您的进程外COM对象。当您尝试从MTA公寓回叫原始对象时,Windows邮件将发布到托管它的Internet Explorer进程中的BHO STA线程。但是消息队列仍在忙于处理原始请求。
这解释了你的死锁,以及传递字符串的原因。