Leveraging C++ Applications Using COM An example of ATL COM

This article shows how to use ATL to leverage the functionality of a C++ Document/View
application to any other type of application that can use COM. This article is a companion to the
article “Leveraging C++ Applications Using COM An Extension of the Document View
Architecture” which is also posted at www.codeguru.com. Using ATL, a C++ application’s
document engine is converted into a COM server. Following the Open/Closed Principal, this is
done by extending the functionality of the C++ application’s document engine without making
any modification to the original document class.

Background

Assume that an existing Doc/View C++ application has a document class called
DocumentEngine. The document engine manages the information necessary to display
information from a database as a table, chart, or map. In this example, DocumentEngine is
supplied an index to a pre-determined view and returns an SQL statement to a client view. The
client view then renders this SQL as a table, chart, or map (See Figure 1 in the article
“Leveraging C++ Applications Using COM An Extension of the Document View
Architecture.”). The client views can be in the same application or in a completely different
application. In this example, the client view will be a dialog in another C++ ATL application.

ATL Example

This example will create a COM DLL server using the ATL COM AppWizard in Visual C++
Version 5.0. After creating the server, we will use ATL to create a client application that will use
the COM server we created. [1]

Create the COM Server Using ATL

1. Create a new Project by selecting File | New from the Visual C++ Developer’s Studio,
select ATL COM AppWizard on the Projects tab, give it a suitable name such as
DocEngineServer, and an appropriate directory to place the project. Press OK.

2. The ATL COM AppWizard Step 1 of 1 wizard will appear. Make sure that the server
type is Dynamic Link Library (DLL) and click the checkbox for Support MFC (This
assumes that your document uses MFC, if it doesn’t then you do not need to include
MFC support). Press the Finish button.

3. The New Project Information dialog will appear summarizing what the ATL Com
AppWizard is about to do. Press OK.

4. You now have a skeleton COM server. The next step is to add the DocumentEngine class
and to create a coClass that will link the COM server and the DocumentEngine.

5. Add the header and source files for the DocumentEngine class to the project (supplied
below). The DocumentEngine class simply holds a short (m_Index ) that is an index for
the view to be displayed. The protected function CreateSQLForCurrentView() generates
the SQL whenever a new index is set it. The public function SQLForCurrentView()
returns the SQL statement.

6. The next step is to create an interface that will use DocumentEngine. Create a coClass by
running the ATL Object Wizard. Select Insert | New ATL Object, select the Simple
Object icon in the right-hand list box, and click the Next button. On the Names tab of the
ATL Object Wizard Properties dialog enter the name for your object in the Short Name
edit box. For this example, enter the name DocEngine. The entries for all the other edit
boxes will be filled in automatically. You do not need to make any changes under the
Attributes tab. Press OK.

7. The coClass object is now created and a DocEngine header and cpp file added to the
project. A registry script file (DocEngine.rgs) is also added which will handle the
registration for the DocEngine component. Under the Class View tab there is an entry for
the IDocEngine interface.

8. The next step is to make DocEngine inherit from DocumentEngine so that DocEngine
“isa” DocumentEngine. Open the DocEngine.h file and make DocEngine inherit
publically from DocumentEngine. The declaration for CDocEngine should look like:


class ATL_NO_VTABLE CDocEngine :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CDocEngine, &CLSID_DocEngine>,
public IDispatchImpl<IDocEngine, &IID_IDocEngine, &LIBID_DOCENGINESERVERLib>,
public DocumentEngine

9. The next step is to add properties to the IDocEngine so that we can use it as an interface
for our DocumentEngine class. Click on the Class View tab and then right-click on the
IDocEngine interface. Select Add Property and the Add Property to Interface dialog will
appear. Select short from the Property Type drop-down list. Enter a Property Name such
as IndexValue. Make sure the Get Function and Put Function boxes should both be
checked. Press OK.

10. Continuing with the Class View tab, add another property to the IDocEngine interface.
Right-click on the IDocEngine interface and select Add Property. Select VARIANT from
the Property Type drop-down list. Enter a Property Name such as
SQLStatementForView. Uncheck the Set Function box so that only the Get Function box
should be checked. We will use the VARIANT to pass the string for the SQL
statement.[2] Press OK. [3]

11. The next step is to connect up the code for the interfaces to DocumentEngine. Because
CDocEngine “isa” DocumentEngine it has complete access to all the public and protected
functions of DocumentEngine. Open up the file DocEngine.cpp and have the
get_IndexValue function use DocumentEngine’s GetIndex() function, have the
put_IndexValue function use DocumentEngine’s SetIndex( short DocumentEngine’s
SQLForCurrentView(). See the attached code for details.

12. Build the project. You now have a COM server that can use the DocumentEngine class in
the same manner that the original C++ application did.

Creating the COM Client Using ATL

1. Create a new Project by selecting File | New from the Visual C++ Developer’s Studio,
select ATL COM AppWizard on the Projects tab, give it a suitable name such as
ATLApplication, and an appropriate directory to place the project. Press OK.

2. The ATL COM AppWizard Step 1 of 1 Wizard will appear. Make sure that the server
type is Executable (EXE). Press the Finish button. The New Project Information dialog
will appear summarizing what the ATL Com AppWizard will be doing. Press OK and
you will have a skeleton COM application.

3. The next step is to add a view to the application that will use the COM server’s document
engine. Select Insert | New ATL Object, choose Miscellaneous in the left-hand box, and
select Dialog from the right-hand box. Press Next. Enter a Short Name such as
SQLView and press OK. Add an include to the SqlView.h file near the top of the
ATLApplication.cpp file.

4. Switch to the Resource tab and open up the dialog in the resource editor. Change the
label on the OK button to `Submit’ and the label on the Cancel button to `Close’. Add an
edit box and give it an ID of IDC_VIEW. Check the Number box on the Style tab. Exit
the resource editor.

5. Add #include’s so that the CSQLView Dialog can use the IDocEngine interface.
Depending upon where these files are in your directory structure, the includes may look
something like:


#include “..DocEngineServerDocEngineServer.h”
#include “..DocEngineServerDocEngineServer_i.c”

Also add the following include so that we can use _bstr_t

#include <comdef.h>

6. For the sake of the example, the ATL client application will display this dialog on
startup. When the Close button is selected from the dialog, the application will close. You
may want to just use the code attached below because this part of the code is not directly
related to ATL. The _tWinMain function is modified to eliminate code not necessary for
this example (i.e., reading the command line), and to create the dialog just before the
message loop starts. The CSQLView::OnCancel()is modified to close the dialog and
shut-down the application. CSQLView::OnOK button is where the more interesting code
is located. This important parts of this code (out of sequence) are:

  1. IDocEngine* p = NULL;
  2. HRESULT hr = CoCreateInstance( CLSID_DocEngine, NULL, CLSCTX_ALL, IID_IDocEngine, (void**)&p );
  3. If( SUCCEEDED( hr ) ){
  4. hr = p->put_IndexValue( GetDlgItemInt(IDC_VIEW) );
  5. VARIANT vSQLStatement;
  6. hr = p->get_SQLStatementForView( &vSQLStatement )

Line 1) declares an interface to IDocEngine and Line 2) and fills in the pointer with a value. If
the interface exists then Line 3) will let processing continue. (Remember to check return codes
after making COM calls). Line 4) gets the index selected in the dialog and passes it to the COM
Engine through the pointer p. Line 5) declares a VARIANT which will receive the string of the
SQL statement. Line 6) get the SQL statement. In this simple application, the SQL statement is
displayed. The only valid indexes are 0 and 1.

Conclusion

ATL makes it relatively painless to use COM. The example above shows one of many ways to
use ATL with an existing C++ application. The example uses only a bare minimum of what ATL
has to offer. We suggest you read the books referenced below to explore in more detail what
COM and ATL have to offer.

[1] The examples are based on the examples provided in Chapter 2 of Beginning ATL COM
Programming by Grimes, Stockton, Templeman, and Reilly.

[2] To be usable by the widest range of COM clients such as VBA and VB Script, you are
limited to basic data types (short, int, double, etc.) and VARIANTs. VARIANTs are a union that
allow you to pass many different types of data, including strings and arrays.

[3] In a real situation you would probably add one or more methods to the interface that would
allow you more control of the DocumentEngine class. Good design of course emphasizes
minimal interfaces so that you can “do the mostest with the leastest.”

References

  1. Box, Don. Essential COM, Addison-Wesley, 1998.
  2. Grimes, Dr. Richard, Alex Stockton, George Reilly, and Julian Templeman. Beginning ATL
    COM Programming. Wrox Press Ltd., 1998.

Download Demo and Source – [52] KB

More by Author

Must Read