2013-02-05 28 views
15

Tôi đang sử dụng máy chủ COM out-of-proc (COM singleton "Engine" được triển khai bằng DECLARE_CLASSFACTORY_SINGLETON), nó hoạt động trong STA (CComSingleThreadModel, _ATL_APARTMENT_THREADED).Máy chủ COM out-of-proc bị kẹt

máy chủ COM khách hàng:

  1. ActiveScript (JScript), (tôi vượt qua tham khảo Engine sử dụng AddNamedItem).
  2. Hai BHO IE độc lập.

BHOs ​​gọi định kỳ Engine :: dispatchEvent, Các cuộc gọi cơ động Các chức năng JavaScript của ActiveScript. Kiến trúc này hoạt động hoàn hảo cho đến khi tôi bật hai BHO cùng một lúc.

Nếu tôi bật hai BHO, bị kẹt xảy ra khi tôi gọi chức năng của ActiveScript (sử dụng IDispatch/Invoke). Tôi không tạo bất kỳ chủ đề bổ sung nào.

Một số lưu ý:

  • Nếu tôi không vượt qua đối tượng lấy từ BHO để ActiveScript (hoặc thay thế nó với cùng một đối tượng được tạo ra trong Engine) tất cả mọi thứ hoạt động tốt.
  • Bị kẹt chỉ xảy ra khi bộ thu gom rác JScript cố gắng giải phóng đối tượng được truy xuất từ ​​BHO (IUnknown_Release_Proxy trong callstack).

callstack:

> [email protected]() + 0x15 bytes  
[email protected]() + 0x15 bytes  
[email protected]() + 0x100 bytes  
[email protected]() + 0x8e bytes  
[email protected]() + 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++ 
[email protected]@4() + 0xe bytes  
rpcrt4.dll!_NdrClientCall2() + 0x144 bytes  
ole32.dll!ObjectStublessClient(void * ParamAddress, long Method) Line 474 + 0x8 bytes C++ 
[email protected]() 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++ 
[email protected]() + 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 

chi tiết thực hiện:

// 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

Có, bế tắc do công tắc ngữ cảnh chủ đề bắt buộc khi Javascript cố gắng giải phóng đối tượng của bạn. Bạn cần phải xem xét các chủ đề mà ban đầu tạo ra các đối tượng và xem lý do tại sao nó không được đáp ứng. Nếu nó bị chặn thì bạn cần MsgWaitForMultipleObjectsEx để cho phép cuộc gọi này được hoàn thành. –

+0

Chủ đề BHO không phản hồi vì nó đang chờ kết quả dispatchEvent (oleaut32.dll! _IDispatch_Invoke_Proxy, user32.dll! _RealMsgWaitForMultipleObjectsEx trong callstack). – KAdot

+0

Tôi đã thêm chi tiết triển khai. – KAdot

Trả lời

0

bạn BHO (Browser Helper Object) là trong một chung cư chủ đề duy nhất. Mỗi cuộc gọi COM trong một STA được thực hiện cho một đối tượng trên một STA (thread khác nhau) được sắp xếp theo một tin nhắn trong một hàng đợi tin nhắn trước khi nó được "chuyển đổi" trong một cuộc gọi phương thức.

Điều này thường không phải là một vấn đề, bởi vì hầu hết các cuộc gọi thời gian được kích hoạt bởi GUI là đơn luồng. Các cuộc gọi COM đợi đến lượt của họ cùng với các thông báo WM_LBUTTONUP và như vậy.

Điều gì xảy ra trong trường hợp của bạn là khi phục vụ onEvent, bạn gửi đối tượng BHO của bạn đến một chủ đề khác, trong một quy trình khác, đối tượng COM không xử lý của bạn. Khi bạn cố gắng gọi lại đối tượng gốc từ ngăn MTA của bạn, một thông báo Windows sẽ được gửi đến chủ đề BHO STA của bạn trong quy trình Internet Explorer lưu trữ nó. Nhưng hàng đợi tin nhắn vẫn bận phục vụ yêu cầu ban đầu.

Điều đó giải thích bế tắc của bạn và tại sao truyền chuỗi hoạt động.

+0

Điều này không hoàn toàn chính xác. Chỉ có các cuộc gọi chéo đến một đối tượng STA bắt đầu bằng xử lý hàng đợi thông báo. Ngoài ra, STA thông thường (so với STA với tùy chỉnh 'IMessageFilter' hoặc WInRT's ASTA) xử lý các thông báo khi các cuộc gọi chéo được tạo từ nó, do đó, nó reentrant và nó không nên bế tắc như thế. Tôi nghĩ rằng các op đã không cung cấp đủ chi tiết để giải quyết vấn đề, bế tắc là ở nơi khác. – acelent

+0

Bạn đang nói đúng rằng chỉ có các cuộc gọi căn hộ qua sử dụng tin nhắn Windows, tôi sẽ cập nhật câu trả lời của tôi. Nhưng tôi nhớ đã "vấp ngã" khi một khóa chết và không reentrancy của Windows tin nhắn là vấn đề. Tôi thừa nhận nó đã được hơn 10 năm trước đây. [Bài đăng này] (http://blogs.msdn.com/b/timng/archive/2006/09/06/743795.aspx) nói điều tương tự như bạn, bộ nhớ của tôi phải thất bại với tôi ... – ixe013

Các vấn đề liên quan