Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM Macro Architecture Topology - Clients

0.00/5 (No votes)
24 Jul 2001 2  
An article about COM Architecture, COM Clients and the Registry

Purpose

This article shows how to implement a simple Client application. It will be developed in C++ using MFC architecture and ATL.

Requirements

All the code in these samples have been written in C++, using Visual C++ 6.0 (sp4) under Windows 2000.
They also use the Active-X Template Library (ATL v3.0).

Other articles in this series

This article is part of a series called "COM Macro-Architecture Topology". Here are the links to other articles:

Simple Client Application

What we want in the end

The final Client application will be a simple MFC Dialog based application. It will use the Afx???() functions from the MFC (Microsoft Foundation Classes) framework. In addition, it will look like that (or something similar):

Client / At the end ...

Creating the Project

In order to create the client application, you have to follow the steps below:
  • Start the Visual C++ IDE and Select File, New and fill in like below:
Client / New
  • You want to create a Dialog based application:
  • Click on Next.
Client / MFC Appwizard - Step 1 of 4
  • Uncheck the ActiveX Controls support option.
  • You can change the Title.
  • Click on Next.
Client / MFC Appwizard - Step 2 of 4
  • Keep with the default settings.
  • Click on Next.
Client / MFC Appwizard - Step 3 of 4
  • Click on Finish.
Client / MFC Appwizard - Step 4 of 4

After that, the MFC wizard will create these files :
  • Workspace: macrotopoclient.dsw
  • Project: macrotopoclient.dsp
  • Application code in macrotopoclient.h/cpp
  • Dialog box code in macrotopoclientDlg.h/cpp

COM Initialisation and Uninitialisation

You have to Initialise and Uninitialise COM in your client application. Here because we are building a MFC application we will use the Afx???() functions:
  • AfxOleInit() to initialise COM.
  • AfxOleTerm() to uninitialise COM.

Find the InitInstance() method of our Client MFC application (e.g. CMacrotopoclientApp) and add these lines:
BOOL CMacrotopoclientApp::InitInstance()
{
    // Initialise OLE libraries and COM.

    if (!AfxOleInit())
        {
        AfxMessageBox("Error when Initialising COM.");
        return(FALSE);
        }
...

    CMacrotopoclientDlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
...

    AfxOleTerm();

    // Since the dialog has been closed, return FALSE so that we exit the

    //  application, rather than start the application's message pump.

    return(FALSE);
}

Of course you can use as well the normal COM API functions: CoInitializeEx() and CoUninitialize(). However, you need to use the pre-processor definition _WIN32_DCOM.

The User Interface

In order to make life easy, we will create push buttons and bind them to methods of our Dialog box class. These methods will make a call to the COM Server.
Use the resource editor to add a button like that:
  • Click on ResourceView in the workspace view.
  • Open the Dialog folder and double-click on the dialog resource for your application (e.g. IDD_MACROTOPOCLIENT_DIALOG).
Client / Open the dialog box in the resource editor.

At the beginning, your Dialog box resource only has 2 push buttons (OK and Cancel) and a static text control (TODO: Place dialog controls here.):

Client / The dialog box resource - As the beginning.

Remove the static text control by selecting it and press the Delete keyboard.

Select the push button control tool in the Controls toolbar:

Client / Controls toolbar - pushbutton.
Create a new push button on the dialog box:
  • right-click anywhere in the dialog box,
  • maintain your right mouse button down and move your mouse in order to initialise the push button size,
  • then release your right mouse button.
Change the caption and the ID of your button like that:
  • left-click over your push button,
  • select Properties from the popup menu,
  • then:
    • Change the ID: IDBUT_MACROTOPOEXE_DISP_SHOWHELLO,
    • Change the Caption: ShowHello()...
If you build and run your Client application at this stage, the result could be like that:

Client / The dialog box resource - As the beginning.

To bind the push button to a method double-click in it. A "Add member function" dialog box will appear. If you want you can change the name of the member function, else just click OK.
This dialog box shows the member function name (OnMacrotopoexeDispShowhello()) that will be used when the push button control (ID: IDBUT_MACROTOPOEXE_DISP_SHOWHELLO) will receive a control message BN_CLICKED.

Calling the COM Server

Now we have a push button in our dialog box and its Click event is bound to a method in our Dialog box class (e.g. CMacrotopoclientDlg). Next step is to call our COM Server. To write this code you will use the "#import" statement:
/////////////////////////////////////////////////////////////////////////////

// MacroTopoExe - Disptach & Custom.

#import "../bin/macrotoposerver_exe.exe"

void CMacrotopoclientDlg::OnMacrotopoexeDispShowhello() 
{
 try
    {
    using namespace MACROTOPOSERVER_EXELib;
    IMacroTopoDispPtr    pDisp(__uuidof(CoMacroTopo));
    pDisp->ShowHello();
    }
 catch(_com_error &)
    {
    ::MessageBox(GetSafeHwnd(),
                 _T("DCOM exception."), 
                 _T("OnMacrotopoexeDispShowhello()"),
                 MB_OK | MB_ICONSTOP);
    }
}
The code above uses the #import statement to get Type information from the COM Server (e.g. from macrotoposerver_exe.exe, macrotoposerver_dll_psdll.dll or macrotoposerver_dllmerged.dll).

It tries to obtain an Interface pointer of IMacroTopoDisp from a COM Object CoMacroTopo, which should implement it. If not, a COM exception (using the _com_error class) will be raised.

This code is very short, but in fact the #import directive has generated wrap code for you. Look in your ".\Debug" directory (if you are working in Debug build), you will find 2 files:
  • macrotoposerver_exe.tlh it is a header file containing a C++ version of the Type information in the Type Library stored (in general) inside your COM Server.
  • macrotoposerver_exe.tli it is the file containing inline Interface member functions that serve as wrappers to the remote interface methods implemented by the COM object.
Now, if you want to have a push button for each method of each interface of your COM object you have to:
  • repeat the creation of a push button,
  • bind its click event to a new member function of your Dialog box
  • use the snippet code above as a template to call another function.
At the end, just build your application, select a case in the main article, follow the instruction and run your application.

The #import statement

The #import statement is a Visual C++ compiler directive, and like many other Microsoft tools it works very well with COM. This directive could be see as a COM appWizard, in that sense it helps C++ developers to get the C++ representations of what has been describe in the Type Library (inside the COM Server).

The Type Library and its information (in fact the COM Server metadata information) can reside in files with extensions such as: TLB, EXE, DLL and OCX.

The #import statement will generate 2 files in your current build working directory (e.g. ".\Debug" if you are working in Debug build):
  • TLH file extension is a header file that contains a C++ version of the Type information from your Type Library COM Server.
    All the Type information resides in a C++ namespace for avoiding collision between logical names (or readable names).
    Smart pointers on Interface are declared in this file.
    At the end of this file, a #include "filename.tli" statement is defined to include the second file generated by the #import statement.
  • TLI file extension is the file that contains implementation of inline Interface member functions. These member functions serve as wrappers to remote interface methods implemented by the COM object.

To go further

If you want to go deeper in ATL look at [Bi7] and [Bi10].

If you are more interested in the different techniques for using COM Objects in client code look at [Bi3] chapter 7.

The #import statement has a number of attributes such as no_namespace / rename_namespace for managing the namespace of the imported Type information, look the Visual C++ documentation.

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