进程外COM服务器卡住了

时间:2013-02-05 08:58:19

标签: windows com marshalling atl active-script

我使用了进程外COM服务器(COM单例"引擎"使用DECLARE_CLASSFACTORY_SINGLETON实现),它在STA中工作(CComSingleThreadModel,_ ATL_APARTMENT_THREADED)。

COM服务器客户端:

  1. ActiveScript(JScript),(我使用AddNamedItem传递引擎引用)。
  2. 两个独立的IE BHO。
  3. 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, 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);
            }
        }
    }
    

1 个答案:

答案 0 :(得分:0)

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

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

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

这解释了你的死锁,以及传递字符串的原因。