Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An introduction to callbacks and connection points with ATL

0.00/5 (No votes)
23 Jan 2005 1  
Article giving a general perspective on implement callback interfaces and general notion of connectable objects and connection points.

A general callback scenario

Introduction

Programming COM with ATL and MFC has always been somewhat cryptic considering the ease with which Visual Basic creates them for us. Perhaps one of the most used but feared is the callback features. This article tries to understand a bit more about callbacks rather than jeopardize the readers with mighty names like source and sinks.

Background

I assume that most of the readers favour ATL for COM development and are comfortable with interfaces, editing IDL file and fiddling with methods and properties of an interface without much help from the wizard!

Description

Consider a very typical client server scenario with the server being the COM component (may be anything in process, out of process, service, etc.) and the client being a VC++ or a VB program that wants to make use of the services provided by your COM server. Consider that your client program calls the method of the server, consider for example add() (Wow!!!). Suppose that the server starts with the time consuming job of 'adding'. Your client application may go on with it's work and when it requires the result back from the server...now that's a real problem! Conventional interfaces provide for one way communication from the client to the server. Now, how will the server notify the client that it is ready with the result. In a typical COM terminology, we call the server interface as "SOURCE" and client side event handler as "SINK". We will use these terms where they are more appropriate, for remaining parts of discussions we can well stick with our 'client-server' common names. Now we have a wide range of alternatives. If you are good in Computer Organization and Architecture, you may be knowing of things like polling, interrupt based I/O, etc.

  1. The first choice at our disposal is similar to the polling technique. The client continuously checks with a property or a boolean method that will indicate if the operation is complete. The client is stuck in a loop till a 'true' is returned flagging the completion (success or failure) of the operation at the server side. However simple, this definitely is not an efficient or elegant solution. A better option would be for the server to be able to notify the client that it is done with its job and the results may be taken back by the client.
  2. This method is similar to the interrupt based I/O (no direct comparison expected). Add an interface to your server's .idl file. This interface declares the function that is to be executed once the results are available with the server. The server's IDL file declares the interface but does not implement it.

    The job of implementation of this interface is left to the client. Such a client needs to provide the function definition to handle the result given by the server. Consider that the server is providing an interface called _IServerEvents, the VB6 client code can be written as:

    Option Explicit
    
    Implements _IServerEvents
    
    Private Sub _IServerEvents_CallbackFunction()
      ...
    End Sub

    Similarly, for a C++ client assume that we have a class CMyClient available. This class is inherited from your _IServerEvents interface. The class overrides the CallbackFunction() to handle the post callback handling.

            class CMyClient: public _IServerEvents
            {
            public:
                STDMETHODIMP CallbackFunction()
                {
                    //Your code goes here
    
                    ...
                }
            };
  3. Although the solution in previous step is satisfying enough, it puts an added restriction on your client to implement the provided interfaces. A much transparent solution is offered by ATL wizard when we add a class to the project.

    Enable connection points in out CoClass

Now open your project's IDL file and add the following to it:

        dispinterface _IServerEvents
        {
        properties:
        methods:
        };

In the coclass tag, add the following lines:

        [
        uuid(xxxxxxxxx),
        helpstring("...")
        ]
        [default,source] dispinterface _IServerEvents;

Add a method to the dispinterface just like any conventional interface. In our case let it be CallbackFunction. Now the most important thing, in the class view, right click on the CoClass and choose 'Implement Connection Point...'. A single dialog will pop up. Press OK and you are done.

Implement the connection point

You will see that the function Fire_CallbackFunction(...) is implemented for you. Whenever you need to notify the client, you call this function with the required parameter. Remember that the IDL file should have attribute [in] for the parameter of the callback function.

The last part is to implement a client in VB6. This is real easy. While declaring the variable for the server class, use 'WithEvents' to enable your application to receive events from the server. Now that makes things really easy in VB.

However, the code for implementing the client in VC++ is not that straight forward. As this article is just to give you a taste of callbacks, I will prefer not to delve into unnecessary details. However, if any one is determined enough to code the client in VC++ I will be more than happy to help out. Please post me a message and I will answer you or even post a separate article describing the process.

My observations

While writing the code for a project, I needed to pass back a variant through the callback function. When I compiled my code (VC++ 6.0 with SP5 !!!), I got a warning: conversion to bool, possible loss of data. On running my client I found that it was actually getting value as 'True'. The callback function worked perfectly fine for BSTR, double, int, etc! Ultimately I figured out that, I had to use the InternalCopy() method of CComVariant class to copy the parameter value to the CComVariant array. Please be careful and never assume that wizard generated code is never error prone :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here