Click here to Skip to main content
15,881,588 members
Articles / Desktop Programming / ATL
Article

An introduction to callbacks and connection points with ATL

Rate me:
Please Sign up or sign in to vote.
2.33/5 (8 votes)
23 Jan 20054 min read 61K   25   11
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:

    VB
    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.

    C++
    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:

C++
dispinterface _IServerEvents
{
properties:
methods:
};

In the coclass tag, add the following lines:

C++
[
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


Written By
Engineer
United States United States
Doing MS in Computer Science at State University of New York at Stony Brook.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Member 11204449-Dec-08 8:05
Member 11204449-Dec-08 8:05 
Questionhi Pin
Najmuddin Khan24-Dec-07 4:41
Najmuddin Khan24-Dec-07 4:41 
GeneralNeed the client in C++ Pin
nigs_krec21-Nov-06 21:01
nigs_krec21-Nov-06 21:01 
GeneralGreat Article! Pin
vextant28-Oct-06 12:38
vextant28-Oct-06 12:38 
GeneralImplent Interface Problem Pin
Setesh8218-Sep-06 12:03
Setesh8218-Sep-06 12:03 
AnswerRe: Implent Interface Problem Pin
Mayur Mahajan18-Sep-06 13:43
Mayur Mahajan18-Sep-06 13:43 
The first two steps seem fine. In step 3, I assume you are _not_ using wizard, since doing so will automatically implement an empty function for you inside the CoClass (Provided you are using the 'add method' for the interface and not the CoClass). Correct me if I am getting it wrong. Now you hand code a method inside the IDL file. Make sure you right click on the IDL file and compile it. THIS STEP IS IMPORTANT. Now manually add the COM_INTERFACE_ENTRY to the BEGIN_COM_MAP. Also add the method as appropriate STDMETHOD. Provide a suitable definition for the function too.
If you do not want to get down to this dirty hand-coding, make sure you are using context menu for the interface to add method. Doing so should give a function skeleton ready for you.
Hope I was clear enough. If you still have some problems, send me the code snippet and I will have a look at it for you. Hope it helps!! Roll eyes | :rolleyes:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

GeneralRe: Implent Interface Problem Pin
dhanu_kapp11-Dec-06 23:44
dhanu_kapp11-Dec-06 23:44 
Questionfire event inside thread Pin
nskwok16-Feb-06 8:07
nskwok16-Feb-06 8:07 
GeneralOne question Pin
paulstandard12-Dec-05 23:21
paulstandard12-Dec-05 23:21 
GeneralPassing an interface Pointer Pin
anindya_sengupta5-Jun-05 19:40
anindya_sengupta5-Jun-05 19:40 
GeneralRe: Passing an interface Pointer Pin
Mayur Mahajan6-Jun-05 2:23
Mayur Mahajan6-Jun-05 2:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.