15

プロセス外の COM サーバー (DECLARE_CLASSFACTORY_SINGLETON を使用して実装された COM シングルトン「エンジン」) を使用しています。これは STA (CComSingleThreadModel、_ATL_APARTMENT_THREADED) で動作します。

COM サーバー クライアント:

  1. ActiveScript (JScript) (AddNamedItem を使用してエンジン参照を渡します)。
  2. 2 つの独立した IE BHO。

BHO は定期的に Engine::dispatchEvent を呼び出し、エンジンは ActiveScript の JavaScript 関数を呼び出します。このアーキテクチャは、2 つの BHO を同時にオンにするまでは完全に機能していました。

2 つの 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 によってトリガーされるため、これは通常は問題になりません。WM_LBUTTONUPCOM 呼び出しは、メッセージなどとともに順番を待ちます。

あなたの場合、サービス中onEventに BHO オブジェクトを別のスレッド、別のプロセス、アウトプロセス COM オブジェクトに送信します。MTA アパートメントから元のオブジェクトにコールバックしようとすると、それをホストする Internet Explorer プロセスの BHO STA スレッドに Windows メッセージがポストされます。しかし、メッセージ キューは元の要求の処理でまだビジー状態です。

これは、デッドロックと、文字列を渡すことが機能する理由を説明しています。

于 2015-04-29T20:08:16.067 に答える