Windows 进程外COM服务器卡住

Windows 进程外COM服务器卡住,windows,com,marshalling,atl,active-script,Windows,Com,Marshalling,Atl,Active Script,我使用的是out-of-proc COM服务器(使用DECLARE_CLASSFACTORY_singleton实现的COM singleton“引擎”),它可以在STA(CComSingleThreadModel,_ATL_公寓_THREADED)中工作 COM服务器客户端: ActiveScript(JScript),(我使用AddNamedItem传递引擎引用) 两个独立的IE BHO BHOs定期调用Engine::dispatchEvent,引擎调用ActiveScript的JavaS

我使用的是out-of-proc COM服务器(使用DECLARE_CLASSFACTORY_singleton实现的COM singleton“引擎”),它可以在STA(CComSingleThreadModel,_ATL_公寓_THREADED)中工作

COM服务器客户端:

  • ActiveScript(JScript),(我使用AddNamedItem传递引擎引用)
  • 两个独立的IE BHO
  • BHOs定期调用Engine::dispatchEvent,引擎调用ActiveScript的JavaScript函数。 在我同时打开两个BHO之前,这种架构工作得非常好

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

    一些注意事项:

    • 如果我不将从BHO检索到的对象传递给ActiveScript(或者用引擎中创建的相同对象替换它),那么一切都正常
    • 只有当JScript垃圾收集器尝试释放从BHO(调用堆栈中的IUnknown_release_代理)检索到的对象时,才会发生卡住
    调用堆栈:

    >    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
    
    实施详情:

    //引擎(进程外COM单例)
    等级ATL\u NO\u VTABLE发动机:
    公共CComObjectRootEx,
    公共课程,
    公共场所
    {
    声明类工厂单例(CEngine)
    STDMETHOD(dispatchEvent)(BSTR名称、IDispatch*pEvent、变体\u BOOL*pbSuccess)
    {
    //pEvent是CPropertyStore实例
    Invoke1(L“FuncName”、pEvent和varResult);
    }
    }
    //BHO
    类别CPropertyStore:
    公共CComObjectRootEx,
    公共课程,
    公共卫生
    {
    开始COM映射(CPropertyStore)
    COM_接口_条目(IUnknown)
    COM_接口_条目(IDispatch)
    END_COM_MAP()
    BOOL SetProperty(CString strName,变量*值)
    {
    //在CAtlArray中存储值
    }
    //伊迪斯帕奇酒店
    标准方法(GetTypeInfoCount)(UINT*pctinfo);
    STDMETHOD(GetTypeInfo)(UINT iTInfo、LCID LCID、ITypeInfo**ppTInfo);
    STDMETHOD(GetIDsOfNames)(refid-riid、LPOLESTR*rgszNames、UINT-cNames、LCID-LCID、DISPID*rgDispId);
    STDMETHOD(Invoke)(DISPID DISPID成员、refid riid、LCID LCID、单词wFlags、DISPPARAMS*pDispParams、,
    变型*pVarResult,EXPEPINFO*pExcepInfo,UINT*puArgErr);
    }
    类别ATL_NO_VTABLE CBHO:
    公共CComObjectRootEx,
    公共课程,
    具有SiteImpl的公共对象,
    公共场所,
    公共IDispenTempl
    {
    void onEvent(…)
    {
    如果(m_pEngine==NULL&&成功(m_pEngine.CoCreateInstance(CLSID_引擎)))
    {
    CComObject*pEvent=NULL;
    HRESULT hRes=ccombject::CreateInstance(&pEvent);
    c变异变数事件(pEvent);
    CComVariant varName(L“EventName”);
    变异结果;
    m_pEngine.Invoke2(L“dispatchEvent”、&varName、&varEvent、&varResult);
    }
    }
    }
    
    您的BHO(浏览器辅助对象)位于单线程装置中。STA中对另一个STA(不同线程)上的对象进行的每个COM调用都由消息队列中的消息排序,然后在方法调用中对其进行“转换”

    这通常不是问题,因为大多数调用都是由单线程GUI触发的。COM调用会随着
    wmlbuttonup
    消息等等待轮到它们

    在您的情况下,当维护
    onEvent
    时,您将BHO对象发送到另一个线程,在另一个进程中,您的进程外COM对象。当您尝试从MTA装置回拨原始对象时,会在承载该对象的Internet Explorer进程中将Windows消息发布到BHO STA线程。但是消息队列仍然忙于处理原始请求


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

    是的,当Javascript试图释放对象时,由于所需的线程上下文切换而导致死锁。您需要查看最初创建对象的线程,并了解为什么它没有响应。如果它被阻止,那么您需要MsgWaitForMultipleObjectsEx来允许此封送调用完成。BHO线程没有响应,因为它正在等待dispatchEvent结果(oleaut32.dll!_IDispatch_Invoke_Proxy,user32.dll!_RealMsgWaitForMultipleObjectsEx,在callstack中)。我添加了实现细节。看起来像脚本引擎(它是垃圾收集器)正在释放属于不同单元的对象。您的引擎是STA,是在MTA中还是在另一个STA中执行脚本操作?如果是MTA,则应重新设计以在调用时不阻止STA(例如,来自工作线程的dispatchEvent,不确定在没有看到代码的情况下是否可以执行此操作).Scripting操作应在STA中,即使用CoCreateInstance(L“JScript”)在引擎中创建的ActiveScript中。这并不完全正确。只有对STA对象的跨单元调用才能通过消息队列处理启动。此外,常规STA(与带有自定义
    IMessageFilter
    或WInRT的ASTA的STA相比)当跨单元调用从它发出时处理消息,因此它是可重入的,不应该像那样死锁。我认为op没有提供足够的细节来解决问题,死锁在其他地方。你说得对,只有跨单元调用使用Windows消息,我会更新我的答案。但我记得“绊倒了”在这样一个死锁和Windows消息不可重入的情况下,问题就来了。我承认这是十多年前的事了。跟你说的一样,我的记忆一定在衰退。。。