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 "..\DocEngineServer\DocEngineServer.h"
#include "..\DocEngineServer\DocEngineServer_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


Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • It's no secret what keeps CIOs up at night. Mobile, cloud, data, security, and social have become the "five imperatives," the drivers of business progress, innovation, and competitive differentiation. Business leaders around the world want to hear how other companies are succeeding. How are they applying the latest technologies? How did they get started? What outcomes are they achieving? Read this online magazine for success stories from organizations like the NBA, Pfizer, and San Jose State University as they …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds