15

我正在使用进程外 COM 服务器(使用 DECLARE_CLASSFACTORY_SINGLETON 实现的 COM 单例“引擎”),它适用于 STA(CComSingleThreadModel,_ATL_APARTMENT_THREADED)。

COM 服务器客户端:

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

BHO 定期调用 Engine::dispatchEvent,Engine 调用 ActiveScript 的 JavaScript 函数。在我同时打开两个 BHO 之前,这种架构运行良好。

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

一些注意事项:

  • 如果我不将从 BHO 检索到的对象传递给 ActiveScript(或用引擎中创建的相同对象替换它)一切正常。
  • 仅当 JScript 垃圾收集器尝试释放从 BHO 检索到的对象(调用堆栈中的 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);
        }
    }
}
4

1 回答 1

0

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

这通常不是问题,因为大多数时候调用都是由单线程的 GUI 触发的。COM 调用与WM_LBUTTONUP消息等一起等待。

在您的情况下发生的情况是,在服务时onEvent,您将 BHO 对象发送到另一个线程,在另一个进程中,您的进程外 COM 对象。当您尝试从您的 MTA 公寓回调原始对象时,一条 Windows 消息将发布到托管它的 Internet Explorer 进程中的 BHO STA 线程。但是消息队列仍在忙于为原始请求提供服务。

这解释了你的死锁,以及为什么传递字符串有效。

于 2015-04-29T20:08:16.067 回答