0

我的 C++ 应用程序会时不时地使用基于此页面的 MSDN 示例的代码遍历多个应用程序的 MSAA 树:https ://msdn.microsoft.com/en-us/library/windows/desktop/dd317975(v=vs.85 ).aspx

它工作得很好,直到几个月前我开始注意到有时线程在 AccessibleChildren 调用上冻结。

这是我所知道的:

  • 这不会经常发生
  • 它发生在行走不同应用程序的树时。
  • 它绝对没有连接到当前节点的子节点数量,因为我已经调试了底部调用childCount等于 1 的小型转储。
  • 它发生在不同的PC上。
  • 处于这种位置的线程永远不会醒来。一旦发生冻结,线程将保持此状态,直到重新启动应用程序。
  • 有时线程在这样的迭代中简单地死掉了,在这种情况下,我无法捕捉到它的堆栈跟踪。然后应用程序的其余部分继续运行,但转储显示给定线程不再工作,尽管它的任务基本上是无限循环和睡眠。我认为它与冻结有关。不知何故。

我的问题是:有人可以指出这种冻结的原因以及如何防止它们吗?如果没有,有没有办法将递归移动到另一个线程,可以安全地从另一个线程“超时”?

这是此类事件的示例堆栈跟踪,其中 top 是最嵌套的调用。我从这里删除了我的递归以缩短阅读时间。

--> ntdll.dll!_NtWaitForMultipleObjects@20()    Unknown
    ntdll.dll!_NtWaitForMultipleObjects@20()    Unknown
    KERNELBASE.dll!_WaitForMultipleObjectsEx@20()   Unknown
    kernel32.dll!_WaitForMultipleObjectsExImplementation@20()   Unknown
    user32.dll!_RealMsgWaitForMultipleObjectsEx@20()    Unknown
    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    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   C++
    ole32.dll!CAptRpcChnl::SendReceive(tagRPCOLEMESSAGE * pMsg, unsigned long * pulStatus) Line 583 C++
    ole32.dll!CCtxComChnl::SendReceive(tagRPCOLEMESSAGE * pMessage, unsigned long * pulStatus) Line 659 C++
    ole32.dll!NdrExtpProxySendReceive(void * pThis, _MIDL_STUB_MESSAGE * pStubMsg) Line 1932    C++
    rpcrt4.dll!@NdrpProxySendReceive@4()    Unknown
    rpcrt4.dll!_NdrClientCall2()    Unknown
    ole32.dll!ObjectStublessClient(void * ParamAddress, long Method) Line 474   C++
    ole32.dll!_ObjectStubless@0() Line 154  Unknown
    ole32.dll!CStdMarshal::Begin_RemQIAndUnmarshal1(unsigned short cIIDs, _GUID * pIIDs, tagQICONTEXT * pQIC) Line 4551 C++
    ole32.dll!CStdMarshal::Begin_QueryRemoteInterfaces(unsigned short cIIDs, _GUID * pIIDs, tagQICONTEXT * pQIC)    C++
    ole32.dll!CStdMarshal::QueryRemoteInterfaces(unsigned short cIIDs, _GUID * pIIDs, tagSQIResult * pQIRes) Line 4284  C++
    ole32.dll!CStdIdentity::CInternalUnk::QueryMultipleInterfaces(unsigned long cMQIs, tagMULTI_QI * pMQIs) Line 596    C++
    ole32.dll!CStdIdentity::CInternalUnk::QueryInterface(const _GUID & riid, void * * ppv) Line 352 C++
    ole32.dll!IUnknown_QueryInterface_Proxy(IUnknown * This, const _GUID & riid, void * * ppv) Line 1723    C++
    ole32.dll!CoUnmarshalInterface(IStream * pStm, const _GUID & riid, void * * ppv) Line 996   C++
    oleacc.dll!UnmarshalInterface(unsigned char const *,unsigned long,struct _GUID const &,void * *)    Unknown
    oleacc.dll!FreeUpSlot(struct OutstandingObjectEntry *)  Unknown
    oleacc.dll!_ObjectFromLresult@16()  Unknown
    oleacc.dll!NativeIAccessibleFromWindow(struct HWND__ *,unsigned long,struct _GUID const &,void * *) Unknown
    oleacc.dll!_ORIGINAL_AccessibleObjectFromWindow@16()    Unknown
    oleacc.dll!_AccessibleObjectFromWindow@16() Unknown
    oleacc.dll!GetWindowObject(struct HWND__ *,struct tagVARIANT *) Unknown
    oleacc.dll!CClient::Next(unsigned long,struct tagVARIANT *,unsigned long *) Unknown
    oleacc.dll!AccWrap_Base::Next(unsigned long,struct tagVARIANT *,unsigned long *)    Unknown
    oleacc.dll!_AccessibleChildren@20() Unknown
    //my recursion ends here
4

1 回答 1

0

我仍然不知道为什么会发生上述所有事情以及如何在我的代码中保护我的应用程序免受这种情况的影响。我认为自己的唯一解决方案是使用单独的进程并等待它超时。类似于此 Microsoft 示例,但超时设置为恒定值(如半秒)而不是INFINITE.

如果由于过程完成而等待结束,那么我会得到它的标准输出作为响应(一些带有结果的 json 或类似的东西)。如果进程超时,我将简单地终止它以清理其资源。

这不是一个完美的解决方案,但它可以让我对正在发生的事情有一些控制,并且我将以某种方式受到保护,防止内存泄漏等。

由于这只是一半的措施,我很乐意接受任何其他想法来解决我的问题。

于 2015-06-19T08:41:57.967 回答