HOW TO Configure DCOM Timeouts

We will also look at how to dispense with the disturbing "Server Not Responding - Switch To" and "Server Busy" dialog boxes.

Introduction

Not long after my first attempts at ATL and DCOM, I developed two pet hates:

1) 'Prefab' dialog boxes that interrupt the application and launch the taskbar

START menu when the client cannot connect to the server.

2) Lack of support for timeouts.

A worst case scenario would be: A Windows 95 LAN client belonging to the domain "engineering.mycompany" attempts to instantiate an object on the server with the NETBIOS name "Einstein". The client can try to locate "Einstein" in a number of ways, including concatenating "Einstein" and "engineering.mycompany" to a single domain name and asking the local name server to resolve this name. Let us further assume that the LAN is connected to the internet via a dial-on-demand link and that the local DNS is not a root server. If "Einstein.engineering.mycompany" cannot be resolved, the DNS server will attempt to contact the next DNS in the hierarchy on the internet.

In the meantime, the user of the DCOM client application is either looking at an hour glass or gets a dialog box encouraging him/her to task-switch to the server application - as if it were possible to task-switch across a network.

My friend Bud Millwood and I, here at Weird Solutions Inc., have been working on a client / server application based on DCOM and devised a relatively straight forward solution to both problems described above. Bud suggested that I post our solution onto the DCOM mailing list to encourage further exploration of the issue and perhaps constructive feedback. So here it is folks....

Solutions

1) Addressing the "Server Busy" dialog problem.

Declare an object, say "MyFilter" of class "COleMessageFilter" in your application, ensure that you are including and call the following methods:


MyFilter.EnableNotRespondingDialog(FALSE);
MyFilter.EnableBusyDialog(FALSE);
MyFilter.Register();

This will have registered a new implementation of the IMessageFilter interface for your application. The IMessageFilter interface is used for concurrency management. Your new IMessageFilter differs from the default in that it does not display the notorious dialog boxes.

2) Contemplating a solution to the timeout problem. The suggested method to make timeouts configurable is to implement one's own "IMessageFilter" interface with the "MessagePending" method. >From within this method you can return PENDINGMSG_CANCELCALL when your patience has expired. This will unblock your application.

Canceling an ongoing DCOM call might well create temporary orphan resources on the server, but this seems a small price to pay compared to an non-responsive application that the user will shut down via task manager. In any event, one is well advised to exercise good judgement in establishing timeouts and perhaps even carry out some statistical analysis of typical response times. I have found, for instance, that starting a local COM service on my coding machine varies between one and three seconds. Clearly then, a client timeout of one second would be inappropriate, while anything in the excess of - say - 10 seconds would provide an ample safety margin.

The question that presents itself is whether one should implement an "IMessageFilter" interface from scratch or reuse what already ships with MFC. The MFC class "COleMessageFilter" almost meets the requirements; it even has a virtual function "COleMessageFilter::OnMessagePending" that you can override in a derived class of your own. It does, however, have one serious flaw. You might wish to examine the source code for this class in the file "....\vc\mfc\src\olemsgf.cpp".

Two methods are of interest here:

a) "MessagePending"

This maps to the COM interface IMessageFilter::MessagePending method and is called by DCOM to process pending messages during an otherwise blocking DCOM call. Its simplified structure is as follows:


MessagePending()  {

    //do something useful

	//call virtual function OnMessagePending

	return PENDINGMSG_WAITNOPROCESS
}

b) "OnMessagePending" This is the virtual function called by MessagePending(). You may override this function in a derived class to customize the behaviour of your application.

Now here is the problem: Microsoft has already decided for us what will be returned by MessagePending(). The return value of your implementation of OnMessagePending() is ignored completely in the parent function MessagePending(). Hence, whatever you return from OnMessagePending() is inconsequential.

What now?

We could of course modify the source of "COleMessageFilter" and rebuild MFC. For our purposes, this was inappropriate.

The MS Visual C++ 5.0 help files state:

"You will probably want to implement your own message filter.The default implementation provided by COM offers only minimal message filtering capability."

Rather than building the message filter from scratch, I have opted to take over the code base from "COleMessageFilter" to create my own base class with an identical interface and named it "COleMsgFilter". It exists parallel to and independent from the MFC class "COleMessageFilter". The minor - but crucial - change in the implementation of COleMsgFilter::MessagePending() is to actually return the value from the function call to COleMsgFilter::OnMessagePending(). This will allow returning PENDINGMSG_CANCELCALL from within OnMessagePending() and thus allow controlling the DCOM timeout. 3) Implementing the solution to the timeout problem. Prepare the class header file. ------------------------------

a) Open the file "../vc/mfc/include/afxole.h". "afxole.h" is a collection header file for a number of OLE classes. Look for the definition of the "COleMessageFilter" class. It is prefixed with the following header:


   ///////////////////////////////////////////////////////////////////
   ////////// // COleMessageFilter (implements IMessageFilter)
Mark and copy the COMPLETE class definition and paste it to a new header file, named "OleMsgFlt.h".

b) Search for every instance of "COleMessageFilter" in the file OleMsgFlt.h and replace it with "COleMsgFilter".

c) Change the return type of OnMessagePending() from BOOL to DWORD.

Prepare the class CPP file.

a) Open the file "../vc/mfc/src/olemsgf.cpp", copy its contents to a new file, and save it in your project directory under "OleMsgFlt.cpp".

b) Search for every instance of "COleMessageFilter" in the file OleMsgFlt.cpp and replace it with "COleMsgFilter".

c) Insert the statement shown below at the top of your CPP file.


     #include "OleMsgFlt.h" 

d) Insert the line shown below at the top of your CPP file


#include  

e) From the file "../vc/mfc/src/afximpl.h" copy the line shown below to the top of your CPP file


#define _countof(array) (sizeof(array)/sizeof(array[0]))   

f) From the file ""../vc/mfc/include/afxpriv.h" copy the line shown below to the top of your CPP file


#define WM_KICKIDLE         0x036A  

g) Find the method "COleMsgFilter::XMessageFilter::MessagePending" and within it, the line where OnMessagePending() is called. It should look like this:


                     pThis->OnMessagePending(&msg);
Prefix this line with "return", like so:

    return pThis->OnMessagePending(&msg);

h) Change the return type of the implementation of OnMessagePending() from BOOL to DWORD.

i) You will find that some of the initialization functions for the class' data members are absent from the class implementation. This is because they are in-lined in the file "AfxOle.inl". Their non in-lined counterparts are shown below. You can just copy and paste them directly into your CPP file.


void COleMsgFilter::SetRetryReply(DWORD nRetryReply) {
  m_nRetryReply = nRetryReply;
}
void COleMsgFilter::SetMessagePendingDelay(DWORD nTimeout) {
   m_nTimeout = nTimeout;
}
void COleMsgFilter::SetBusyReply(SERVERCALL nBusyReply) {
  m_nBusyReply = nBusyReply;
}
void COleMsgFilter::EnableBusyDialog(BOOL bEnableBusy) {
  m_bEnableBusy = bEnableBusy;
}
void COleMsgFilter::EnableNotRespondingDialog(BOOL
bEnableNotResponding ) {
  m_bEnableNotResponding = bEnableNotResponding;
}

Compilation and linking

Insert both files into your project, make sure as well as the standard DCOM / OLE headers are included in your project, and compile...

Usage

Derive from COleMsgFilter and implement your own OnMessagePending() function. Here you can check for timeout and return PENDINGMSG_CANCELCALL when your application has timed out.



Comments

  • Using the existing COleMessageFilter

    Posted by Legacy on 03/27/2003 12:00am

    Originally posted by: AchiCastor

    Hi all,
    
    

    After reading the VC6 MSDN help on COleMessageFilter,
    I added the following code and apparently the
    dialog box never show up.

    The MFC client is calling a MTA Ole server,
    registered via the Component Services MMC.

    I think one good practice of inserting the code
    is do it once at the initialization section.
    In fact, I place it in the main dialog's initialization
    and it works as well.

    Try out the result by inserting this line
    before re-register the message filter.
    It shorten the waiting time to 100 msec and
    provide a pretty good environment for the busy dialog
    to show up if it was not disabled.

    pMsgFilter->SetMessagePendingDelay(100);


    The code :


    ASSERT(AfxOleGetMessageFilter() != NULL);
    CWinThread* pThread = AfxGetThread();
    if (pThread != NULL)
    {
    // Obtain the existing message filter.
    COleMessageFilter *pMsgFilter;
    pMsgFilter = AfxOleGetMessageFilter();
    if (pMsgFilter != NULL)
    {
    // Unregister the existing message filter.
    pMsgFilter->Revoke();
    // No status report dialog box.
    pMsgFilter->EnableNotRespondingDialog(FALSE);
    // No busy dialog.
    pMsgFilter->EnableBusyDialog(FALSE);
    // Re-register the message filter.
    pMsgFilter->Register();
    }
    // No deletion of pMsgFilter is required here,
    // the MFC framework will take care.
    }

    Reply
  • A DCOM call after cancel does not return till the server process terminates

    Posted by Legacy on 06/03/2002 12:00am

    Originally posted by: Amit

    Any DCOM call made after the returning PENDINGMSG_CANCELCALL from OnMessagePending Function is not completed.

    Thanx

    Reply
  • Does not work for a thread other than the application main thread

    Posted by Legacy on 10/11/2000 12:00am

    Originally posted by: Sebastien Abras

    Hello,

    thanks for this great code. Unfortunatly, it appears that it
    only work when calls to the dcom are made within
    theApplication Main Thread.

    I tried the following :

    nest the dcom calls within a CWinthread that is not the
    application thread. In that thread, I register a
    OleMessagefilter as described in the article. Everythings
    was accepted and does not returned error. But, when I try
    it, the OnMessagePending() function is never called.

    So I tried declaring the message filter within the main
    application thread (but still keep my DCOM in another
    thread). It does not work either.

    Any Idea on how to have a message filter called from within
    a secondary thread ?

    thanks a lot ,

    sebastien


    Reply
  • Access Violation when I return PENDINGMSG_CANCELCALL

    Posted by Legacy on 09/21/2000 12:00am

    Originally posted by: Mark Jordan

    Any ideas?  I have an MFC client application.  For testing purposes, if the tick count is more than 1500 milliseconds, I return PENDINGMSG_CANCELCALL to indicate a timeout.  Here is the call stack.
    
    

    NTDLL! 77f8fc56()
    USER32! 77e78991()
    _AfxDispatchCmdMsg(CCmdTarget * 0x0012fcbc {CDCOMPortClientDlg hWnd=0x3bc700f8}, unsigned int 1018, int 0, void (void)* 0x004011ea CDCOMPortClientDlg::OnPbSlowroller(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
    CCmdTarget::OnCmdMsg(unsigned int 1018, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
    CDialog::OnCmdMsg(unsigned int 1018, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 97 + 24 bytes
    CWnd::OnCommand(unsigned int 1018, long 17501214) line 2088
    CWnd::OnWndMsg(unsigned int 273, unsigned int 1018, long 17501214, long * 0x0012f760) line 1597 + 28 bytes
    CWnd::WindowProc(unsigned int 273, unsigned int 1018, long 17501214) line 1585 + 30 bytes
    AfxCallWndProc(CWnd * 0x0012fcbc {CDCOMPortClientDlg hWnd=0x3bc700f8}, HWND__ * 0x3bc700f8, unsigned int 273, unsigned int 1018, long 17501214) line 215 + 26 bytes
    AfxWndProc(HWND__ * 0x3bc700f8, unsigned int 273, unsigned int 1018, long 17501214) line 368
    AfxWndProcBase(HWND__ * 0x3bc700f8, unsigned int 273, unsigned int 1018, long 17501214) line 220 + 21 bytes
    USER32! 77e72348()
    USER32! 77e73571()
    USER32! 77e87588()
    USER32! 77e84156()
    USER32! 77e72ba5()
    USER32! 77e72b78()
    CWnd::DefWindowProcA(unsigned int 514, unsigned int 0, long 720938) line 1000 + 32 bytes
    CWnd::WindowProc(unsigned int 514, unsigned int 0, long 720938) line 1586 + 26 bytes
    AfxCallWndProc(CWnd * 0x0012fd70 {CButton hWnd=0x010b0c1e}, HWND__ * 0x010b0c1e, unsigned int 514, unsigned int 0, long 720938) line 215 + 26 bytes
    AfxWndProc(HWND__ * 0x010b0c1e, unsigned int 514, unsigned int 0, long 720938) line 368
    AfxWndProcBase(HWND__ * 0x010b0c1e, unsigned int 514, unsigned int 0, long 720938) line 220 + 21 bytes
    USER32! 77e71268()


    Anyone?

    Reply
  • derive your class

    Posted by Legacy on 02/25/2000 12:00am

    Originally posted by: Steve M

    This was a really great article. Thanks a lot for publishing it. It was very helpful for a problem I am working on. One, more or less improvement I made was to derive from COleMessageFilter instead of recopying the source code. The trickiest part is that you have to (at least I think so! also verified by coworker Eric!!) create your own nested class XMessageFilter using the BEGIN_INTERFACE_PART macros. I admit, this was almost the same amount of work as cut and pasteing the whole class,instead of just the XMessageFilter implementation. I overrode XMessageFilter::HandleIncomingCall() and MessagePending() to return an appropriate value, based on the return values of OnBusyDialog() and OnNotRespondingDialog(). Of course, this was the whole problem in the first place. I created my own MyRevoke(), MyRegister() methods. I did not override Revoke() and Register() because they were not virtual. (per coworker Eric).

    Reply
  • The problem that I face

    Posted by Legacy on 01/20/2000 12:00am

    Originally posted by: V.Madhusudhanan

    If the caller is in an MTA and the callee is in either an MTA or an STA, we have problems. The calls generated from the MTA do not respond to the IMessageFilter implementation whereas the calls originating from STA can be controlled for timeout. Simply to say, there is no control over the MTA thread. Hence the difficulty in controlling the timeout.
    Can Anyone help?.

    Thanks in advance.

    Reply
  • An easier way ... ?

    Posted by Legacy on 06/08/1999 12:00am

    Originally posted by: DJRob

    Thanks for this great article. It really helped point me in the right direction! However, a far, far easier method of implementing "timeout - auto cancel" is to simply enable the dialogs, and override COleMessageFilter's "OnBusyDialog(...)" and OnNotRespondingDialog(...)" member functions. In those, you can simply return -1 to force the message filter to cancel the COM call.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds