COM servers are distributed to the external users and developers. Developers can utilize COM server functionalities by accessing the interfaces. Microsoft Office, Internet explorer, Adobe Photoshop and many other components are reused in these ways. COM server is packaged in a DLL/OCX/EXE file and accompanied with .tlb type library file. C++ header interface files are given to C++ developer. COM server binaries are copied in System32 folder or Program Files/ folder. Visual Basic, Java Script, C#, VC++ can use the component or type library directly. C++ developer can optionally use the C++ header interfaces.

There are two approaches to develop a COM client in VC++/C++. interface header files can be used from server package or type library header can be generated from the component. We have used a COM server called MATHLIBLib and ICalculate is a basic calculator interface. Below is the interface detail for better understanding our client code.

interface ICalculate : IDispatch
{
  [propget, id(1), helpstring("Operand1 of calc")] HRESULT Operand1([out, retval] long *pVal);
  [propput, id(1), helpstring("Operand1 of calc")] HRESULT Operand1([in] long newVal);
  [propget, id(2), helpstring("Operand2 of calc")] HRESULT Operand2([out, retval] long *pVal);
  [propput, id(2), helpstring("Operand2 of calc")] HRESULT Operand2([in] long newVal);
  [id(3), helpstring("main operation method")] HRESULT DoOperation([in] char operation);
  [propget, id(4), helpstring("Result of calc")] HRESULT Result([out, retval] long *pVal);
}

COM Client with C++ header

This approach is simple a often known as raw approach. We need C++ header file of the COM server. This header file provides the C++ function prototypes of the com server components. We have below example written with raw functions.

  1. Collect the header (.h) file from COM server distribution and place in the same folder.
  2. Include this header file in C++ source and include client wrapper library in the project(optional).
  3. Call CoInitialize or CoInitializeEx to initialize the COM library on the current thread and identifies the concurrency.
  4. Provide Class ID and Interface ID and Call CoCreateInstance and obtain Interface pointer or Call CoGetClassObject and obtain more than one interfaces and call CreateInstance individually.
  5. Now call the interface methods. Example here we are calling a function put_OperandX() for setting the two operands of the calculator and DoOperation() to do the calculation. Later we are calling get_Result() for get the result.
  6. Release the pointer with Release() call.
  7. Call CoUninitialize at the end of all task.

COM Client with C++ demo

Note: We are not using any wrapper library in this example. All calls are to raw interfaces.

/* ComClientRaw.cpp : Defines the entry point for the console application. */

#include "MathLib.h"
#include "MathLib_i.c"
#include <objbase.h>
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
  long lOperand1, lOperand2, lResult;
  unsigned char cOperation;
  ICalculate * MyCalculator;
  HRESULT hr;
  
  CoInitialize(NULL);

  hr = CoCreateInstance(CLSID_Calculate,
                        NULL,CLSCTX_INPROC_SERVER,IID_ICalculate,(void**)&MyCalculator);
  if(hr != 0){
    cout << "CoCreateInstance failed error code %d" << hr;
    CoUninitialize();
    return hr;
  }
  cout << "MathLib.Calculate demo using raw C++ interface" << endl;
  do {
    cout << "Operand 1 : ";
    cin >> lOperand1;
    MyCalculator->put_Operand1(lOperand1);
    
    cout << "Operand 2 : ";
    cin >> lOperand2;
    MyCalculator->put_Operand2(lOperand2);

    cout << "Operation : ";
    cin >> cOperation;
    
    MyCalculator->DoOperation(cOperation);
    
    MyCalculator->get_Operand1(&lOperand1);
    MyCalculator->get_Operand2(&lOperand2);
    MyCalculator->get_Result(&lResult);  
    cout << lOperand1 << " " << cOperation << " " << lOperand2 << " = " << lResult << endl;

    cout << "Do you want to perform more calculation (y/n) ? ";
    cin >> cOperation;

  } while(cOperation == 'y');

  MyCalculator->Release();
  CoUninitialize();
  return 0;
}

COM Client with C++ demo output

MathLib.Calculate demo using raw C++ interface
Operand 1 : 1
Operand 2 : 2
Operation : +
1 + 2 = 3
Do you want to perform more calculation (y/n) ? y
Operand 1 : 2
Operand 2 : 3
Operation : -
2 - 3 = -1
Do you want to perform more calculation (y/n) ? y
Operand 1 : 3
Operand 2 : 4
Operation : *
3 * 4 = 12
Do you want to perform more calculation (y/n) ? y
Operand 1 : 4
Operand 2 : 2
Operation : /
4 / 2 = 2
Do you want to perform more calculation (y/n) ? n
Press any key to continue

COM server vendors often provide client wrapping library. Client raw interface functions are wrapped inside this client side library. This library is optional but needed to make client code modular and it also adds the capability of managed error handling. Client calls these library calls and handles errors if needed. This makes easy to write the flow. Read more about client wrapper.

COM Client with type library/smart pointers

COM server can be written with type library support. Type library is a TLB extension file which is auto generated during compilation of com server. However a COM DLL or EXE can also contain a type library inside it. Visual C++ compiler can generated client wrapping library from type library(.tlb file) or directly from DLL/OCX/EXE component.

These wrapping library has an extension .tlh which means type library header and .tli which means type library implementation. Type library header file can be included in C++ and <Interface Name>Ptr classes can be used. These classes are called smart pointers class.

Smart pointer classes can handle errors and this makes CreateInterface and other functions more robust and manageable. Smart pointer classes are widely used nowadays and it replaces the use of client wrapper libraries. Below example code is written with the help of smart pointers. Logic and interface is same as previous example.

  1. #import COM DLL/EXE or the type library (.tlb) file and compile
  2. Compiler will generate type library header .tlh file and include it in C++ source.
  3. Call CoInitialize or CoInitializeEx to Initializes the COM library on the current thread and identifies the concurrency
  4. Provide Class ID and Interface ID and Call CoCreateInstance and obtain Interface pointer.or Call CoGetClassObject and obtain more than one interfaces and call CreateInstance individually.
  5. Now call the interface methods. Example here we are calling a function PutOperandX() for setting the two operands of the calculator and DoOperation() to do the calculation. Later we are calling GetResult() for get the result.
  6. Release call is not needed. Smart pointer object auto released when it is out of scope.
  7. CoUninitializeat the end after releasing all objects.

COM Client with type library demo

/* ComClientTlh.cpp : Defines the entry point for the console application. */

#include <windows.h>
#include <stdio.h>
#ifdef IMPORT_TLH
#import "MathLib.tlh"
#else
#import "MathLib.dll"  /* Default compile with import */
#else
#include <objbase.h>
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
  long lOperand1, lOperand2;
  unsigned char cOperation;

  CoInitialize(NULL);
  MATHLIBLib::ICalculatePtr MyCalculator;

  if(MyCalculator.CreateInstance("MathLib.Calculate") != 0) {
    cout << "MyCalculator.CreateInstance failed.";
    CoUninitialize();
    return -1;
  }
  cout << "MathLib.Calculate demo using smart pointer" << endl;
  do {
    cout << "Operand 1 : ";
    cin >> lOperand1;
    MyCalculator->PutOperand1(lOperand1);

    cout << "Operand 2 : ";
    cin >> lOperand2;
    MyCalculator->PutOperand2(lOperand2);

    cout << "Operation : ";
    cin >> cOperation;
    MyCalculator->DoOperation(cOperation);

    cout << MyCalculator->GetOperand1() << " " << cOperation 
          << " " << MyCalculator->GetOperand2() << " = "
          << MyCalculator->GetResult() << endl;

    cout << "Do you want to perform more calculation (y/n) ? ";
    cin >> cOperation;

  } while(cOperation == 'y');

  MyCalculator.Release();
  CoUninitialize();
  return 0;
}

COM Client with type library demo output

MathLib.Calculate demo using smart pointer
Operand 1 : 1
Operand 2 : 2
Operation : +
1 + 2 = 3
Do you want to perform more calculation (y/n) ? y
Operand 1 : 2
Operand 2 : 3
Operation : -
2 - 3 = -1
Do you want to perform more calculation (y/n) ? y
Operand 1 : 2
Operand 2 : 3
Operation : *
2 * 3 = 6
Do you want to perform more calculation (y/n) ? y
Operand 1 : 4
Operand 2 : 2
Operation : /
4 / 2 = 2
Do you want to perform more calculation (y/n) ? n
Press any key to continue

About our authors: Team EQA

You have viewed 1 page out of 67. Your COM/DCOM learning is 0.00% complete. Login to check your learning progress.

#