Client Library

C++ Usage Example

Introduction

This article explains how to use SMPP protocol of SMSC client/server .NET as COM component in native (unmanaged) Microsoft Visual C++. Interface between the code and the component DLL is done entirely in bare C++ so it is not bound to any framework like MFC or ATL and can easily be used in GUI or command line (e.g. windows service) application. High performance of the solution proposed is guaranteed through use of COM early binding which skips the need to marshal parameters for every method or event call. Resulting solution is compact and elegant since it only consists of application executable and component DLL library without need for any design time generated wrappers, translators etc.

Installation with COM support

Step number one to use the component as COM object in VC++ is to install it with Register as COM library option checked:

This will register the component as system wide COM library and copy type library file (smscc.tlb) alongside with the component. This type library will be required in a moment to import the COM component to the project. Typically after installation files are located in:

c:\Program Files\Common Files\Tops\

Component can also be registered as COM using command line regasm.exe utility. The command syntax is as follows:

regasm smscc.dll /codebase /tlb:smscc.tlb

Above shown is registration of SMSC client .NET. To register SMSC server .NET component one should use smscs.dll library location. The /codebase flag is important because it will register the path to the library so that it can be found even if it is not located next to the application executable.

New project if needed

If you already have a project that is supposed to use SMSC client/server .NET components then you may skip this point. If you do not - then now you can start Visual C++ and create a new C++ project, on the basis of any framework that is MFC, ATL or even a command line application. The only condition is that the application can process event functions from other threads. This means that the application cannot block main thread like the way command line application does when waiting for keyboard input etc.

Import Type Library

Next, in one of the project files you have to import the type library which can be done using #import pre-processor directive:


  #import "smscc.tlb"

Once you compile the project you can see that among project files there are two new files smscc.tlh and smscc.tli in case of SMSC client .NET or smscs.tlh and smscs.tli in case of SMSC server .NET component. If you would care to open them the you could see that the .tlh file contains all library classes and types in a form of a C++ header file. #import directive also includes the files which means that first you do not have to do this on your own and second that you can place identical #import directive in more than one of your project files.

Next, most naturally right below the #import directive, you should indicate which namespace to use with the following directive:


  using namespace smscc;

Now you are done importing the component to your project.

Create component instance & use it

To create an instance of the component inside your code you should first create a variable (field) as follows:


  ISMSCclientSMPPPtr smsccSMPP;

And then you can initialize in in the place where you want with code like follows, just remember to call OLE initialization first:


  // Initialize OLE
  CoInitialize(NULL);

  // [...]

  // Create SMSC client .NET instance here
  HRESULT hr = smsccSMPP.CreateInstance(__uuidof(SMSCclientSMPP));

From this moment on you can get and set properties and call methods, clean and elegant way:


  // Set some property 
  smsccSMPP->KeepAliveInterval = 10;

  // [...]

  // Call smppInitializeSession
  r = smsccSMPP->smppInitializeSession(GetBSTR(mSystemID),
    GetBSTR(mPassword), GetInt(mInitTON), GetInt(mInitNPI),
    GetBSTR(mSystemType));

Events

To grab events that SMSC client/server .NET components fire a COM object that implements events interface of selected component has to be created and then connected to the instance using COM ConnectionPoint mechanism. Best way to find out what is the class the object should inherit from is to have a look at earlier described smscc.tlh file created during import of the component. For example for SMPP protocol the class we should make instance of is ISMSCclientSMPPEvents. Now to implement the class we do the following:


  class CSMSCclientSMPPEvents: public ISMSCclientSMPPEvents
    {
    private:
      DWORD m_dwRefCount;
    public:
      CSMSCclientSMPPEvents(void): m_dwRefCount(0) {};
      ~CSMSCclientSMPPEvents(void) {};

Now because the event object has to implement IUnknown interface we implement it:


      // IUnknown 
      HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
        void **ppvObject)
      {
        *ppvObject = NULL;
        if (iid == __uuidof(ISMSCclientSMPPEvents))
        {
          m_dwRefCount++;
          *ppvObject = (void *)this;
          return S_OK;
        }
        if (iid == IID_IUnknown)
        {
          m_dwRefCount++;
          *ppvObject = (void *)this;
          return S_OK;
        }
        return E_NOINTERFACE;
      }

      ULONG STDMETHODCALLTYPE AddRef()
      {
        m_dwRefCount++;
        return m_dwRefCount;
      }

      ULONG STDMETHODCALLTYPE Release()
      {
        ULONG l;
        l  = m_dwRefCount--;
        if ( 0 == m_dwRefCount)
        delete this;

        return l;
      }

Last step is to copy from the prototype file (smscc.tlh) event function prototypes. It is just important is to copy only RAW prototypes which are early binding event methods.


      // RAW event prototypes
      HRESULT __stdcall raw_OnTcpDisconnected (
        VARIANT sender,
        struct ITcpDisconnectedEventArgs * e );

      HRESULT __stdcall raw_OnSmppMessageReceived (
        VARIANT sender,
        struct ISmppMessageReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppStatusReportReceived (
        VARIANT sender,
        struct ISmppStatusReportReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppSubmitResponseAsyncReceived (
        VARIANT sender,
        struct ISmppSubmitResponseAsyncReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppQueryResponseReceived (
        VARIANT sender,
        struct ISmppQueryResponseReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppDeleteResponseReceived (
        VARIANT sender,
        struct ISmppDeleteResponseReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppRAWReceived (
        VARIANT sender,
        struct ISmppRAWReceivedEventArgs * e );

      HRESULT __stdcall raw_OnSmppTraceProtocol (
        VARIANT sender,
        struct ISmppTraceProtocolCallbackArgs * e )
      {
        return S_OK
      };
    };

If events are imported as purely virtual (abstract) with = 0 after event prototype then you may have to remove = 0 when events are implemented.

Of course not all of the events have to be implemented some of them may be left empty. You can see how to leave event empty e.g. in OnSmppTraceProtocol implementation in the code section above.

Now to create the instance of the class that will handle events:


  // Create object to receive messages
  CSMSCclientSMPPEvents* pSMSCclientSMPPEvents =
    new CSMSCclientSMPPEvents;

When the object to receive events is created we need to create find ConnectionPoint and use Advice method to start receiving events:


  DWORD dwCookie;

  // [...]

  // Create ConnectionPointContainer 
  smsccSMPP->QueryInterface(IID_IConnectionPointContainer,
    (void **)&pCPC);

  // Create ConnectionPoint 
  pCPC->FindConnectionPoint(__uuidof(ISMSCclientSMPPEvents), &pCP);

  // Connect to component's ConnectionPoint 
  pCP->Advise((IUnknown *)pSMSCclientSMPPEvents, &dwCookie);

It is important to remember to use Unadvise method to stop receiving messages before closing application and call OLE finalization method:


  // Disconnect ConnectionPoint 
  pCP->Unadvise(dwCookie);

  // UnInitialize OLE 
  CoUninitialize();

Shown above is a way to have a bare C++ implementation of class that will receive messages from the instance of the SMSC client/server .NET component.

Summary

Implementation proposed above is pretty sophisticated way of using COM object in C++. Resulting implementation is not relying on any of the frameworks or wizards to import the component and/or wrap it. The result is also compact and elegant, because it requires to work only application executable and registered SMSC client/server .NET DLL library.