Virtual Developer Workshop: Containerized Development with Docker
ImplementationAlthough the big picture view of COM technology is straightforward, the actual implementation is intimidating. In the big picture, COM lets a program (the client) safely run binary code in another executable or DLL (the COM server) on your computer or on any computer to which you are linked. In practice this means that the client uses COM to ask a COM server for an interface to an object. If the object supports this interface the server will return to the client a pointer to this interface. The client can then use this pointer to manipulate and exchange information with the object. If the server does not support the interface (or it could be returned for some other reason), then the return code will let you know. What makes this special is that the language used to generate the server's binary code is irrelevant. COM takes care of all the details to ensure that the execution and exchange of data between the client code and the COM server is handled correctly. 
The intimidating part of COM is making sure all these details get handled correctly. Among the details involved include: 1) making sure the client code can locate the COM object to use, 2) ensuring that the client code and the COM object can share information, and 3) making sure that resources used to create the COM object are managed correctly. These items have associated with them legions of new acronyms, data types, and methods. The most important thing to do while learning these new acronyms, data types, and methods is to keep yourself from running from the room screaming. We recommend the books listed below which will help you get up and running with COM.
Fortunately, MFC and ATL have created Wizards, macros, templates, and other tools that automate most of the particularly inscrutable aspects of implementing a COM server or COM client. We have written two companion articles that describe the steps to convert an existing class in a monolithic C++ application into a COM server. Because we needed to access the COM server from an Active Server Page web site and from within Excel using VBA, we needed to provide an Automation interface rather than the more general custom interfaces. Clients such as VBA and VBScript only support automation interfaces and not custom interfaces. Automation uses the IDispatch interface and can only pass automation data such as VARIANTs and not, for example, abstract data types. However, if you are trying to drive the server from VBA or VBScript, being limited to the basic data types (through VARIANT) is probably not a problem. (The server code itself is, free to use whatever data types it needs to use - the data type limitation is only on what can be passed back and forth to the client application.)
ExampleThe attached code and example is not from DBViewer but it illustrates the same concepts. The sample demonstrates how COM technology can enhance well-designed applications using the document-view architecture.
- The attached zip file contains the following:
1. Source code to build a COM server: ComSample.zip.
2. Source code to build the View application: DlgView.zip.
3. Source Code for the Document DLL: DocEngine2.zip.
4. COMDEMO.xls. An Excel 97 spreadsheet file.
5. All the EXEs and DLLs if you simply want to run the code. Please remember to register the ComSample.DLL.
The ViewThe sample takes a very simple application that implements the document view architecture. The view comprises of a single dialog based application that displays some text in a static control. This application is dependent on a document for what it will display but the application does not contain the document. In this sample the document is provided by the DocEngine DLL using COM.
The DocumentThe DocEngine DLL has a single class called CSomethingComplicated. CSomethingComplicated represents a complex class hierarchy of classes representing a document engine for a system. The system's document engine is responsible for manipulating data, performing computations, and any other data related processes. However, in this sample CSomethingComplicated only has a single function HelloWorld() which returns "Hello World" as a CString (MFC). The CSomethingComplicated class is exported from the DLL making it available to the user for use and allowing the user to derive new classes from it.
The COM ServerThe COM server is a very simple class that inherits from CCmdTarget. The COM server accesses the DocEngine DLL and provides an interface to the CSomethingComplicated.HelloWorld() function. In a more realistic world, several COM classes could be built each representing some part of the DocEngine and providing an interface for it. The COM server makes the DocEngine available to other applications/systems such as ASP, MS Excel, Visual Basic etc.
Notice that converting the existing C++ code to be COM compatible was quite easy.
ConclusionThis article shows how easy it can be to a convert a typical monolithic MFC Document/View application into a COM Server. However, this only scratches the surface for what COM can do. For example, the DBViewer application could be broken into a series of COM server chunks to provide even greater flexibility to client applications. Each of the C++ application's views can be turned into ActiveX Controls and used by both the C++ executable and the web server. The views could even be embedded into presentation software to allow clients (i.e., the paying kind) to create spiffy up-to-the minute tables, charts, and maps without the nuisance of manually exporting data to a spreadsheet, manipulating it, and creating a table, chart, or map.
 These fields are for illustrative purposes only. The DBViewer can work with any relational database that supports SQL. The only significant setup required is to create a table that acts like a data dictionary.
 The SQLs must be created dynamically because they generally include criteria such as "get me the last two months of data," or "get me the top ten percent of these values." This means that it is not possible to simple store the actual SQL statement and retrieve it for later use.
 Although this process sounds similar to calling an exported function in a DLL, the COM solution is more robust and allows DLL calls to be correctly made even if the DLL was generated by another vendor's compiler or even in a completely different language. Even calling non-COM DLLs generated in C++ but from different vendors is problematic because different vendors implement the features of C++ differently (e.g., RTTI, exception handling). Chapter 1 of Essential COM by Don Box (Addison-Wesley. 1998) provides an excellent description of these issues.
ReferencesBox, Don. Essential COM, Addison-Wesley, 1998.
Grimes, Dr. Richard, Alex Stockton, George Reilly, and Julian Templeman. Beginning ATL COM Programming. Wrox Press Ltd., 1998.
Templeman, Julian. Beginning MFC COM Programming. Wrox Press Ltd., 1997.
Following is an example of VBA code used to access the DocEngine using the COM server. Similar code can be written in VB, VB Script for ASP, and other applications and/or languages, thus making one library available to a vast array of systems.
Sub Test1() Set dll = CreateObject("ComSample.DocEngineInterface") Dim str As Variant Dim blah As String str = dll.HelloWorld() blah = CStr(str) Worksheets("Sheet1").Range("E7").Select ActiveCell.Value = CStr(str) End Sub