Trace RPC Calls and Notify the COM+ Events to Your Program

Introduction

Some time back, I was reading about Performance Monitor Programming, and found some articles—not many, but some. Then, I read something about Spying the componenets and services that run under COM+ Consoles. In both cases, it was capturing COM+ Events managment internal data, to get it out for Spying, and to add counters in Performance monitor.

All of these work by using two major concepts of COM+ interfaces: Instrumentation interfaces and Administration interfaces. I found less help in understanding their relation, but when I had understood all these, I got a good start to make it simple and useful for all.

But now the question arises: Why should we put in all this effort? Part of ths reason this is that you can get a performance log of your component methods, without changing the component code, like some running application in production that may not have proper logging. Or, it may have a performance issue, but you not able to track which Methods. So, you can write such a small service to log your Production components’ performance by their method name, which would be good for diagnostics—and lots more if you have an idea to more such interfaces.

History

Let me first define both from the Microsoft documentation.

Instrumentation interfaces

The COM+ Instrumentation service enables you to build your own COM+ event management and logging programs when you want to display various performance metrics for your COM+ components. By subscribing to the events published by the system events publisher, clients can implement the COM+ Instrumentation interfaces to receive notifications for a variety of COM+ performance metrics, such as information about specific COM+ objects, COM+ applications, and COM+ services. The metrics are published to the client by using the COM+ Events service, a loosely coupled events (LCE) system that stores event information from different publishers in an event store in the COM+ catalog.

Note: COM+ Instrumentation does not guarantee the delivery of an event.

Administration interfaces

The COM+ administration interfaces enable you to access and manipulate all of the COM+ configuration data accessible through the Component Services administration tool. You can use these interfaces to automate all tasks in COM+ administration. The administration interfaces also enable you to read and write information that is stored in the COM+ catalog, the underlying data store that holds all COM+ configuration data. Information in the catalog is structured as collections, which hold items that expose properties. In the Component Services administration tool, these collections correspond to folders, items in folders, and property sheets for those items.

Note: Not all of the collections and items that are available in the COM+ catalog are available in the Component Services administration tool.

These are the standard definitions as documented in the Microsoft documentation. Let me try to explain both in simple words.

Administration Interfaces are the interfaces by which you can get and set all the information programmatically, what you can see in the component console. Instrumentation interfaces are the interfaces that you can implement programmatically to receive all the events of com+ Event management, even to the components method call, Object pooling, transaction, and so forth.

These are the same interfaces that you can use to spy your COM+ components also if you have to receive information about which method has been called, with which parameters, and when it has been returned with what return value. You can get all these in an another application that could just monitor the components without making any communication and interference or without loading the components in its process.

All this information is available in COM+ Events service, a loosely coupled events system that stores event information from different components or services of COM+, in an event store in the COM+ catalog.

Sample

Now, the efforts come into the picture to make it practical, instead of going on theory. To understand all these, you need to make three simple applications; I’ll explain them by their names (I will continue to use the same names).

  1. Publisher: You can make a simple Component that may have some test method, Or, you can use some existing component’s DLL to configure in a component console.
  2. Subscriber: (attached is the source as well as a DLL) An ATL-based simple component DLL that implements the instrumentation interface. You can make any of the instrumentation interfaces available. I have implemented IComMethodEvents, which is used to get notify on method call, on method return and on method exception for the Publisher methods.
  3. Administrator: (attached is the source as well as an EXE) An MFC application that uses COM+ Admin Interfaces to configure a COM+ catalog to subscribe Subscriber to an COM+ Event service to be notified for the method calls of Publisher. This is the application that does all magic; COM+ Event service sends the notification to your Subscriber whenever there is some activity in the Publisher Component.

Now, move on to the pieces of code that I have written to make it all work.

YOU MUST HAVE INSTALLED MS PLATEFORM SDK TO COMPILE IT.

First the Subscriber component. It is nothing, just a simple ATL Object that implements IComMethodEvents. Here is the class I have made to implement IComMethodEvents; the same class is derived to an ATL Object.

class CComMethodEventsImpl : public IComMethodEvents
{
   public:
      CComMethodEventsImpl() {}

      STDMETHOD(OnMethodCall)(COMSVCSEVENTINFO * pInfo, ULONG64 oid,
                               REFCLSID cid, REFIID rid, ULONG iMeth);
      STDMETHOD(OnMethodReturn)(COMSVCSEVENTINFO * pInfo, ULONG64 oid,
                                REFCLSID cid, REFIID rid, ULONG iMeth,
                                HRESULT hr);
      STDMETHOD(OnMethodException)(COMSVCSEVENTINFO * pInfo,
                                   ULONG64 oid, REFCLSID cid,
                                   REFIID rid, ULONG iMeth);
      //These are the method that are helper methods to get the
      //method name and other information (taken from some of the
      //sample code of the SDK)
      HRESULT GetClsidOfTypeLib2 (IID * piid, UUID * puuidClsid);
      HRESULT GetMethodName (REFIID riid, int iMeth,
                             _TCHAR** ppszMethodName);

   public:
};

Now, the class is derived to ATL Object. All are just simple required steps to make a component with the implementation of IComMethodEvents. (I expect that you are well familiar with ATL.)

///////////////////////////////////////////////////////////////////
// CComPlusMethodEvent
class ATL_NO_VTABLE CComPlusMethodEvent :
   public CComObjectRootEx<CComMultiThreadModel>,
   public CComCoClass<CComPlusMethodEvent,
                      &CLSID_ComPlusMethodEvent>,
   public IDispatchImpl<IComPlusMethodEvent, &IID_IComPlusMethodEvent,
                        &LIBID_SUBSCRIBERLib>,
   public CComMethodEventsImpl

{
public:
   CComPlusMethodEvent()
   {

   }

DECLARE_REGISTRY_RESOURCEID(IDR_COMPLUSMETHODEVENT)
DECLARE_NOT_AGGREGATABLE(CComPlusMethodEvent)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CComPlusMethodEvent)
COM_INTERFACE_ENTRY(IComPlusMethodEvent)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IComMethodEvents)
END_COM_MAP()

// IComPlusMethodEvent
public:
   STDMETHOD(Test)();
   STDMETHOD(GetInterfaceUnknown)( /*[out,retval]*/ VARIANT
                                   *PVar_IUnknown);
};

Now, the implementation for the pure virtual methods of IComMethodEvents.

STDMETHODIMP CComMethodEventsImpl::OnMethodCall(
             /* [in] */ COMSVCSEVENTINFO *pInfo,
             /* [in] */ ULONG64 oid,
             /* [in] */ REFCLSID guidCid,
             /* [in] */ REFIID guidRid,
             /* [in] */ ULONG iMeth)
{

   TCHAR * pszMethodName = NULL;
   TCHAR * pszMessage    = NULL;
   HRESULT hr;
   hr = GetMethodName(guidRid, iMeth, &pszMethodName) ;
   if(hr == S_OK)
   {
      pszMessage = new _TCHAR[255];
      lstrcpy(pszMessage , L"Method Called------");
      lstrcat(pszMessage , pszMethodName);
      lstrcat(pszMessage , L"(...)");

      MessageBox(0,pszMessage,L"Subscriber Message",MB_OK);
      delete pszMethodName ;
      pszMethodName = NULL ;
      delete pszMessage ;
      pszMessage = NULL ;
   }

   return S_OK;
}

STDMETHODIMP CComMethodEventsImpl::OnMethodReturn(
             /* [in] */ COMSVCSEVENTINFO *pInfo,
             /* [in] */ ULONG64 oid,
             /* [in] */ REFCLSID guidCid,
             /* [in] */ REFIID guidRid,
             /* [in] */ ULONG iMeth,
             /* [in] */ HRESULT hresult)
{
   TCHAR * pszMethodName = NULL;
   TCHAR * pszMessage    = NULL;
   HRESULT hr;
   hr = GetMethodName(guidRid, iMeth, &pszMethodName) ;
   if(hr == S_OK)
   {
      WCHAR wcRet[16];
      wsprintfW(wcRet, L"0x%08X", hresult);
      pszMessage  = new _TCHAR[255];
      lstrcpy(pszMessage , L"Method Returned------");
      lstrcat(pszMessage , pszMethodName);
      lstrcat(pszMessage , L"(...) , ");
      lstrcat(pszMessage , L"Return Code -----");
      lstrcat(pszMessage , wcRet);

      MessageBox(0,pszMessage,L"Subscriber Message",MB_OK);
      delete pszMethodName ;
      pszMethodName = NULL ;
      delete pszMessage ;
      pszMessage = NULL ;
   }



   return S_OK;
}

STDMETHODIMP CComMethodEventsImpl::OnMethodException(
             /* [in] */ COMSVCSEVENTINFO *pInfo,
             /* [in] */ ULONG64 oid,
             /* [in] */ REFCLSID guidCid,
             /* [in] */ REFIID guidRid,
             /* [in] */ ULONG iMeth)
{
   return S_OK;
}

That’s it; that was the all of code for the Subscriber component. Now, move to the next step to get it working, by the Administrator code.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read