【问题标题】:WM_PAINT and non finished COM calls (ATL OOP Server "Deadlock")WM_PAINT 和未完成的 COM 调用(ATL OOP 服务器“死锁”)
【发布时间】:2013-09-23 04:22:50
【问题描述】:

我们开发了一个 STA ATL COM OOP 服务器,几乎一切正常。我们面临一个问题:当 COM 客户端通过 Windows 消息在内部获取任何 COM 调用的结果时,可以在等待 COM 调用的应答时处理 WM_PAINT 消息(或我猜的任何其他消息)。这本身可能是一个问题,但最大的问题是,在之前的 COM 调用完成之前,对服务器的所有调用都会失败(HRESULT=0x80010005),这是一个相当大的问题,因为它在我们的应用程序中经常发生。我们无法从绘图中移除 COM 调用。

我已经对此进行了大量研究,但找不到任何相关信息(除了 this 2006 文章指出问题和一些无法在我们的案例中应用的解决方案),但我认为没有只有 WM_PAINT 是危险的,我们在任何 Windows 事件(消息)中执行的对服务器的任何调用都可能导致相同的问题。

所以基本上我们可以有不同的解决方案,但不知道使用哪些 COM 方法或如何使用: 1- 等待直到最后一个 COM 调用被处理并返回值的方法 2- 一种知道服务器中是否有待接听电话的方法和一种处理 COM 答案的方法(全部从客户端调用) 3- 知道我们是否可以在客户端实现一个 IMessageFilter 类以及如何处理它和处理调用等等。

谢谢!

【问题讨论】:

    标签: c++ atl out-of-process wm-paint imessagefilter


    【解决方案1】:

    由于 COM 客户端通过 Windows 消息在内部获取任何 COM 调用的结果,因此可以在等待 COM 调用的应答时处理 WM_PAINT 消息(或我猜的任何其他消息)。

    没有任何东西可以将 COM 调用与WM_PAINT 处理联系起来。这两个任务同时存在,但是当您选择 STA 作为模型时,这两个任务都在同一个线程上运行并且可能会相互阻塞。

    您对绘画本身几乎无能为力,因为您最终将拥有一个消息处理程序来进行绘画。但是,您可以更改您的 COM 服务器和您的绘图处理程序以不干扰 COM。

    您无需直接从WM_PAINT 进行 COM 调用。构建您的服务器,使其异步发布所有 UI 更新,并且绘制处理程序仅使用可用的最新数据。

    您还可以将您的服务器移动到另一个单元、STA 或 MTA,这样它就不会与 UI 共享线程并并行运行。

    【讨论】:

    • 好吧,实际上将 COM 调用移到绘图之外是不可能的,因为我们需要几何数据才能在 SolidWorks 绘图上下文中创建 OpenGL 显示列表。
    • 同样,改变结构以使所有对 COM 服务器的调用都在一个单独的线程中运行将非常昂贵(我们有大约 40 个 COM 对象和超过 1000 个调用整个代码) .
    • 所以这两种解决方案都不能应用......以及忽略 WM_PAINT 是不可能的,因为处理它的是solidworks而不是我们。到目前为止,我们正在尝试 IMessageQueue 方法,但仍然无法使其正常工作。无论如何,谢谢您的提示。
    【解决方案2】:

    我们已经解决了在客户端项目 (C++) 中实现 IMessageFilter 类的问题:

    MessageFilter.h:

    #include <atlbase.h>
    #include <atlcom.h>
    
    namespace SDKCOMSrvInterface
    {
     class MessageFilter : public CComObjectRootEx<CComSingleThreadModel>, public IMessageFilter
     {
      BEGIN_COM_MAP(MessageFilter)
       COM_INTERFACE_ENTRY(IMessageFilter)
      END_COM_MAP()
    
      // Implement IMessageFilter methods here
      STDMETHODIMP_(DWORD) HandleInComingCall(
      DWORD dwCallType,
      HTASK threadIDCaller,
      DWORD dwTickCount,
      LPINTERFACEINFO lpInterfaceInfo)
      {
       return E_NOTIMPL;
      }
    
      STDMETHODIMP_(DWORD) RetryRejectedCall(
       HTASK threadIDCallee,
       DWORD dwTimeOut,
       DWORD dwRejectType)
      {
       return SERVERCALL_RETRYLATER;
      }
    
      STDMETHODIMP_(DWORD) MessagePending(
       HTASK threadIDCallee,
       DWORD dwTickCount,
       DWORD dwPendingType)
      {
       return PENDINGMSG_WAITDEFPROCESS;
      }
     };
    }
    

    并在 CoInitialize(NULL) 之后注册它:

    CComObject<MessageFilter>* l_MessageFilter = NULL;
    CComObject<MessageFilter>::CreateInstance(&l_MessageFilter);
    CComPtr<IMessageFilter> l_OldMessageFilter;
    hr = CoRegisterMessageFilter(l_MessageFilter, &l_OldMessageFilter);
    

    这将确保如果我们尝试从客户端调用服务器并且服务器正忙,则将重试 COM 调用,直到服务器处理之前的调用,这正是解决并发问题所需要的。

    【讨论】:

    • 我已经在MFC应用中实现了过滤器注册成功。但它在我身边不起作用,我在函数 MessagePending、RetryRejectedCall 和 HandleInComingCall 上添加了断点,但没有一个断点命中我做错的地方
    猜你喜欢
    • 2011-11-10
    • 2011-06-23
    • 2010-12-17
    • 2013-02-05
    • 2012-05-31
    • 2013-10-30
    • 1970-01-01
    • 2013-09-08
    • 2011-11-11
    相关资源
    最近更新 更多