WM_PAINT and non finished COM calls (ATL OOP Server "Deadlock")

0

We have developed an STA ATL COM OOP Server and all works fine, almost. We are facing a problem: As the COM client internally gets the result of any COM call through a windows message the WM_PAINT message (or any other message i guess) can be processed while waiting for an answer of a COM call. That can be a problem in itself, but the big problem is that until previous COM call finishes all the calls to the server will fail (HRESULT=0x80010005), and this is quite a big issue as its happening really often in our application. We cant remove the COM calls from the paint.

I've been researching quite a bit about this and could not find anything regarding it (aside from this 2006 article that states the problem and some solutions that cant be applied in our case), but for what I see not only the WM_PAINT will be dangerous, any call to the server we perform inside of any windows event (message) will potentially cause the same problem.

So basically we can have different solutions but no clue about which COM methods to use or how: 1- A method that waits until last COM call is processed and value returned 2- A method to know if there is any pending to answer call in the server and a method to process the COM answers (all called from the client) 3- Know if we can implement an IMessageFilter class in the client and how to handle it and process the calls and so.

Thank you!

c++
atl
out-of-process
wm-paint
imessagefilter
asked on Stack Overflow Sep 18, 2013 by Dario Durà Armadans • edited Sep 18, 2013 by Dario Durà Armadans

2 Answers

0

As the COM client internally gets the result of any COM call through a windows message the WM_PAINT message (or any other message i guess) can be processed while waiting for an answer of a COM call.

There is nothing connecting COM calls with WM_PAINT processing. The two tasks exist concurrently, but as you chose STA to be the model, both these tasks run on the same thread and might be blocking one another.

There is little you can do about painting itself, since you eventually will have a message handler to do the painting. However you can alter your COM server, and your paint handler to not interfere with COM.

You don't need to do COM calls right from WM_PAINT. Structure your server so that it posts all UI updates asynchronously and paint handler just uses most recent data available.

Also you can move your server to another apartment, STA or MTA, so that it does not share thread with UI and run in parallel.

answered on Stack Overflow Sep 18, 2013 by Roman R.
0

We have fixed the problem implementing an IMessageFilter class in the client project (C++):

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;
  }
 };
}

And registering it just after CoInitialize(NULL):

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

This will ensure that if we try to call the server from the client and the server is busy, the COM call will be retried until the server processes the previous call, which is exactly what is needed to solve the concurrency problem.


User contributions licensed under CC BY-SA 3.0