Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles

George Shepherd

MSDN Magazine February 2004

...

Read more!

Stephen Toub

MSDN Magazine June 2004

...

Read more!

As you move forward with your use of ADO. NET, you'll need to know how to approach situations that you previously learned to handle in ADO and now have to tackle with ADO. NET. Just as n-tiered solutions developed using Visual Basic®, C++, and ASP often rely on ADO for their data access needs, Windows® Forms, Web Forms, and Web services rely on ADO.

John Papa

MSDN Magazine August 2004

...

Read more!

The CLR allows seamless interactions between Microsoft .NET applications and COM. But how, exactly? The CLR team knows.

Thottam R. Sriram

MSDN Magazine January 2007

...

Read more!

Exception handling semantics in .NET are based on type, so you can create custom exceptions that have their own properties and methods. In .NET, exceptions are first-class citizens, and since they're the built-in error handling mechanism, all .NET-compliant languages must support exceptions. In addition, COM+ services are available to .NET code as Enterprise Services, so you can leverage exceptions in your Enterprise Services design.In this article the author describes custom exceptions, throwing exceptions across COM interop boundaries, and ...

Read more!

Also by this Author

The CLR allows seamless interactions between Microsoft .NET applications and COM. But how, exactly? The CLR team knows.

Thottam R. Sriram

MSDN Magazine January 2007

...

Read more!

Popular Articles

Now you can perform efficient, sophisticated text analysis using regular expressions in SQL Server 2005.

David Banister

MSDN Magazine February 2007

...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

Kenny Kerr

MSDN Magazine May 2008

...

Read more!

Here is an ASP.NET AJAX data-driven Web application that takes the best features from server- and client-side programming to deliver an efficient, user-friendly experience.

Bertrand Le Roy

MSDN Magazine October 2008

...

Read more!

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

Our Blog

So many factors can affect the performance of a Web page—the distance between server and client, the size of the elements on the page, how the browser loads these elements, available bandwidth. Finding those bottlenecks and identifying the culprits is no easy task.

In the November 2008 issue of MSDN Magazine, Jim Pierson introduces ...

Read more!

Silverlight provides a browser interoperability layer that allows managed code to access the document object model (DOM) of the underlying page. At the same time, JavaScript code running in the page can access the XAML content of the plug-in and even make modifications.

In the November 2008 issue of MSDN Magazine, Dino Esposito discusses the ...

Read more!

C# developers can use the Visual Studio Tools for the Office System (VSTO) Power Tools Office interop API extensions to streamline Office application development. The extensions provide a thin, strongly typed layer over the loosely typed Office object models.

In the December 2008 issue of MSDN Magazine, Andrew Whitechapel, Phillip Hoff, and Vladimir Morozov walk you through developing ...

Read more!

Silverlight and SharePoint provide a simple, yet powerful, infrastructure for building intranet and extranet applications with sophisticated user interface designs and interactions.

In the November 2008 issue of MSDN Magazine, Steve Fox and Paul Stubbs demonstrate how to build a SharePoint Web Part as a wrapper for a Silverlight application.

...

Read more!

Windows Presentation Foundation (WPF) adds functionality to the Microsoft .NET Framework so that you actually can reliably keep bound controls synchronized with their data sources.

In the December 2008 issue of MSDN Magazine, Ken Getz demonstrates how to use the ObservableCollection class provided by WPF to keep bound controls in ...

Read more!

CLR Inside Out
COM Connection Points
Thottam R. Sriram

Code download available at: CLRInsideOut2007_09.exe (252 KB)
Browse the Code Online
Atypical scenario in COM has client objects instantiating server objects and then making calls to those objects. Without a special mechanism, however, it would be very difficult for those server objects to turn around and make calls back to the client objects. COM connection points provide this special mechanism, enabling two-way communication between the server and client. Using connection points, the server can call the client when certain events happen on the server.
With connection points, the server specifies the events that it is capable of raising by defining an interface. Clients that have actions to be taken when these events are raised on the server register themselves with the server. The clients subsequently provide the implementation for the interface defined by the server.
There are standard mechanisms through which clients can register themselves with the server. COM provides IConnectionPointContainer and IConnectionPoint interfaces for this.
The clients for a COM connection point server can be written in C++ and in C# managed code. The C++ client registers an instance of a class that provides the implementation for the sink interface. The managed client registers delegates for individual events, thus creating a single sink per event notification method. In the managed world, there are two ways by which the client can register itself—I detail both of these methods later in this column.
There are very few working samples for eventing and interop on the Web. In this column, I focus on creating an Active Template Library (ATL) connection point server. This involves exposing a COM method, defining an event interface that will be implemented by the client, and implementing the code that raises the events from the server. I also show you a sample C++ client that provides an implementation of the sink plus a sample C# client and the two ways you can register and listen to events from the server. Finally, I talk about the recommended way to implement a managed event sink.

The Sample Scenario
In my scenario, the server exposes a COM method:
HRESULT Add(int nFirst, int nSecond)
The server also defines ConnectionPointContainer and connection points so that clients can register with it. In addition, the server defines an interface, _IAddEvents, which has two methods in it:
HRESULT AdditionStarted()
HRESULT AdditionCompleted(int nResult)
The client provides the implementation for _IAddEvents and invokes the Add method on the server. The server fires the AdditionStarted and AdditionCompleted methods on the client to notify it appropriately. Then the client performs the appropriate actions associated with these events.

Creating a Connection Point on a COM Server
In the January 2007 installment of CLR Inside Out, I described in detail how to create a simple ATL COM server (see msdn.microsoft.com/msdnmag/issues/07/01/CLRInsideOut). Today's column assumes you've already gone through the process of creating an ATL COM server called ATLConnectionPointServer. So if you haven't done so, you may want to read the earlier column before proceeding.
So now you need to define a COM interface implemented by the server and make it a connection point. The process of creating a connection point on top of this COM server is straightforward. To do this, open the Class View in Visual Studio® and create a simple ATL object. Just right-click on ATLConnectionPointServer and add a class, select a simple ATL object, and provide the name of the class as Add. Be sure to select Supports: Connection Points when you walk through the wizard.
Now you have a server interface IAdd that can be called from the client. If you build the server, you will notice that there are two interfaces defined here. One is IAdd, which implements IDispatch, and the other is the dispinterface _IAddEvents.
Next add a new method, called Add, to the interface IAdd. This takes in two integers and returns an HRESULT. To do this, right-click on IAdd and select Add Methods. The signature of the methods will be:
HRESULT Add([in] int nFirst, [in] int nSecond)
Now open ATLConnectionPointServer.idl and add methods AdditionStarted and AdditionCompleted to the _IAddEvents interface as shown in Figure 1.
library ATLConnectionPointServerLib
{
    importlib("stdole2.tlb");
    [
        uuid(7F45FEA6-4D7C-489C-A852-19BA8B29D8AB),
        helpstring("_IAddEvents Interface")
    ]
    dispinterface _IAddEvents
    {
        properties:
        methods:
        [id(1), helpstring("AdditionStarted")]HRESULT AdditionStarted();
        [id(2), helpstring("AdditionStarted")]
            HRESULT AdditionCompleted(int nResult);
    };
    [
        uuid(15B6C26A-0416-4C8F-9533-89F318355E31),
        helpstring("Add Class")
    ]
    coclass Add
    {
        [default] interface IAdd;
        [default, source] dispinterface _IAddEvents;
    };
};

If you compile the project at this point, you'll notice an autogenerated file called _IAddEvents_CP.h. This file, which is generated by ATL, contains an empty CProxy_IAddEvents class. This is the class that provides the firing of the events once the connection point is complete and hooked up.
Go to Class View and right-click on CAdd then select Add | Add Connection Point. In the following wizard, select _IAddEvents. If you open the _IAddEvents_CP.h file now, it will contain auto-generated code for two methods, namely Fire_AdditionStarted and Fire_AdditionCompleted. This is the code that calls back to the client sink objects when they register with the server.
You are now close to completing the implementation of the server. All you have remaining is the implementation of the Add method on the server and the trigger points for firing events from the server.
Open Add.cpp and provide an implementation for the Add method that you added. The implementation looks like this:
STDMETHODIMP CAdd::Add(int nFirst, int nSecond)
{
    // Fire AdditionStarted event
    Fire_AdditionStarted();

    int nResult = nFirst + nSecond;
    Sleep(1000); // simulate the addition taking a long time

    // Fire AdditionCompleted event
    Fire_AdditionCompleted(nResult);
    return S_OK;
}
Now just compile the solution and your server is ready.

The Client
Now you can move onto the client. I will start by looking at a C++ client and then I'll move on to a managed client.
The client is responsible for five main tasks:
  • It must provide you with the implementation for the _IAddEvents interface.
  • It must provide an interface pointer to the Add interface on the server.
  • It must get the ConnectionPoint of ConnectionPoinContainer of the Add interface and add the sink interface.
  • It must call the Add method and waits for the events to be fired from the server.
  • It must close cleanly and exit.
To implement the client, open a new C++ project called ConnectionPointClient and add a new C++ source file to the project. Add the ATLConnectionPointServer.h and ATLBase.h file to the project. The sink implements the _IAddEvents that the server has defined. There are two methods in this interface: AdditionStarted and AdditionCompleted. An implementation for these two methods is shown in Figure 2.
class CSink : _IAddEvents
{
    private:
    DWORD       m_dwRefCount;
    public:

    CSink::CSink() {m_dwRefCount = 0;}
    CSink::~CSink()    {}

    HRESULT STDMETHODCALLTYPE AdditionStarted()
    {
        printf("C++ SINK: Addition started event fired ... \n");
        return S_OK;
    };

    HRESULT STDMETHODCALLTYPE AdditionCompleted(int nResult)
    {
        printf("C++ SINK: Addition completed event fired ... \n");
        printf("C++ SINK: Addition result: %d \n",nResult);
        return S_OK;
    };
...

For simplicity, I have implemented the dispinterface on the client; the sample code provides an ATL client that does this automatically. The sink in this implementation merely prints the fact that it has been called and the result when the addition is completed. Your sink is now implemented and ready to go.

Get the Add and ConnectionPoint Interface
Now that the sink has been implemented, let's work on the client that will register this sink with the server. The client handles three main tasks.
  • It gets the interface pointer to the server Add interface.
  • It gets the ConnectionPoint of ConnectionPointContainer from the Add interface.
  • It registers the Sink interface with the server.
First, you get the interface IAdd to the server as follows:
CoInitialize(NULL);
    
hr = CoCreateInstance(
    CLSID_Add, NULL, CLSCTX_ALL, IID_IAdd, (void **)&pAdd);
if(hr != S_OK) { return; }
Next, you must get the connection point on the server so you can register the sink implementation with it. To do this, get the ConnectionpointContainer from the IAdd interface as follows:
// Using the interface for add, 
// query for IConnectionPointContainer interface
hr = pAdd->QueryInterface(
    IID_IConnectionPointContainer,(void **)&pCPC);
if ( !SUCCEEDED(hr) ) { return; }
Now, you can get to the ConnectionPoint:
// Using the IConnectionPointContainer, 
// get the IConnectionPoint interface
hr = pCPC->FindConnectionPoint(DIID__IAddEvents,&pCP);
if ( !SUCCEEDED(hr) ) { return; }
The client now has to create an instance of its sink implementation and register it with the server. To do this, the client creates an instance of the sink and gets its IUnknown interface pointer as follows:
// Create an instance of the sink object to pass 
// to the server
pSink = new CSink();
if ( NULL == pSink ) { return; }

// Get the interface pointer to CSink's IUnknown pointer, which you
// will pass to the server
hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);
if(!SUCCEEDED(hr)) { return; }
You are close to completing the client. All that remains is to register the sink with the server, call the server, and clean up. The client registers the instance of the sink with the server:
// Pass the sink interface to the server through the Advise
hr = pCP->Advise(pSinkUnk,&dwAdvise); 
if(!SUCCEEDED(hr)) { return; }
You have now registered the client sink interface with the server.
The client calls the Add method on the server and passes it two arguments as required by the method. The result of the addition is returned through the AdditionCompleted event, not directly from the Add call. Now call the Add method on the IAdd interface pointer you obtained:
pAdd->Add(1, 5);
This call should trigger the firing of the events which in turn should call into the client. At this point, you can clean up the client by releasing all the interfaces you've obtained (see Figure 3).
// Release the IConnectionPointContainer interface. 
if(pCPC != NULL) pCPC->Release();

// Unadvise the event call back we registered.
if(pCP != NULL) { pCP->Unadvise(dwAdvise); } 

if(pSinkUnk != NULL) { pSinkUnk->Release(); }

// Disconnect from the server.
if(pCP != NULL) { pCP->Release(); }
 
// Release interfaces.
if(pAdd != NULL) { pAdd->Release(); }

CoUninitialize();
return;

You're finally done with your client. You can now compile the client and execute it:
cl COMConnectionPointClient.cpp
On execution, you should see the following output:
C++ SINK: Addition started event fired ...
C++ SINK: Addition completed event fired ...
C++ SINK: Addition result: 6

Managed Client
Now I want to discuss using the same ConnectionPointServer from managed code. The managed client is much simpler than the COM client. There are two ways you can implement the client. First, I'll focus on the recommended approach.
To begin, import the server DLL to managed code to obtain ATLConnectionPointServerLib.dll by using the Microsoft® .NET Framework Type Library to Assembly Converter tool, tlbimp.exe, running the following command:
tlbimp ATLConnectionPointServer.dll
You need to reference the resulting assembly in your managed project and then provide an implementation for the sink interface on the client, such as the one shown in Figure 4. The ManagedSink class implements two methods, AdditionStarted and AdditionCompleted, as defined in the _IAddEvents interface. With that, your sink event handler is now complete and ready. (Seems almost too simple compared to the COM client, doesn't it?)
public class ManagedSink :_IAddEvents
{
    public void AdditionStarted()
    {
        Console.WriteLine("C# SINK: Addition started event fired ...");
    }

    public void AdditionCompleted(int nResult)
    {
        Console.WriteLine("C# SINK: Addition completed event fired ...");
        Console.WriteLine("C# SINK: Addition result: {0}", nResult);
        return;
    }
};

As with the COM client, you have to register the sink with the server so the server can invoke the sink when firing the events. There are, however, some differences in the way the managed client registers itself with the server.
The COM client registered the instance of its sink object that implemented the interface _IAddEvents with the server. As a reminder, the following call registered the COM client:
// Pass the sink interface to the server through the Advise
hr = pCP->Advise(pSinkUnk,&dwAdvise); 
if(!SUCCEEDED(hr)) { return;}
With the managed client, you register individual methods as delegates with the server. To accomplish this, you create an instance of the sink object:
ManagedSink ms = new ManagedSink();
Create an instance of the server object and add the AdditionStarted and AdditionCompleted event handlers separately, like this:
AddClass a = new AddClass();
a.AdditionStarted += ms.AdditionStarted;
a.AdditionCompleted += ms.AdditionCompleted;
The client registers two different interfaces for each of the event handlers with the server. The previous calls to add the delegates on the client add a ref count on the Runtime Callable Wrapper (RCW) on the client. The ref count has to be released by removing the event handler once the call is completed, like so:
a.Add(1, 5);
a.AdditionStarted -= ms.AdditionStarted;
a.AdditionCompleted -= ms.AdditionCompleted;
Finally, compile ManagedClient.cs:
csc /r:ATLConnectionPointServerLib.dll ManagedClient.cs 
And run the executable. You should see the following output:
C# SINK: Addition started event fired ...
C# SINK: Addition completed event fired ...
C# SINK: Addition result: 6

Wrap-Up
Writing a client implementation for dispinterface in ATL can be slightly complicated. The sample I've discussed here deliberately works around the complexities by having its own Invoke implementation.
I want to thank Cosmin Radu, Ladi Prosek, Mason Bendixen, Varun Sekhri, and Claudio Caldato for all their help and suggestions in making this column happen.

Send your questions and comments to clrinout@microsoft.com.


Thottam R. Sriram has a master's degree in computer science from Concordia University, Montreal, Canada. He is currently a Program Manager on the CLR team working on COM interop. Before joining the CLR team, Thottam worked with the Windows team. Visit his blog at blogs.msdn.com/thottams.

Page view tracker