In the past, designing COM objects was relegated to the use of large application frameworks such as MFC or to building your own components. Using large application frameworks eases the construction and implementation of COM objects but requires a significant amount of overhead code for the building and distribution of the COM servers. Conversely, building your own COM server framework results in fast, lightweight objects but requires a significant amount of up-front programming to implement the control.
To address this dilemma, Microsoft created the ActiveX Template Library (ATL),
which provides a middle ground between large application frameworks and building
your own COM objects. ATL is a set of template-based C++ classes that simplifies
the programming of COM objects. ATL provides the necessary COM foundation, allowing
the focus to be on programming the functionality of your objects. ATL is shipped
with Visual C++ 5.0 and is backward compatible with Visual C++ 4.1 and 4.2. ATL can
be downloaded separately from the Microsoft Web site at http://www.microsoft.com.
NOTE: Users of Visual C++ 4.0 must upgrade to version 4.1 or higher of the Visual C++ compiler in order to use the ActiveX Template Library.
The goal of ATL is to allow for the easy creation of small, fast COM servers. This goal has been achieved by the following:
Static library and DLL dependencies are removed by providing all of the source code for the ATL libraries. The source code for ATL is a set of C++ class templates. The small set of ATL code gets compiled into the COM server during the building process. The overhead of ATL in an in-process COM server is less than 5K.
The ActiveX Template Library is the first attempt to create a C++ framework with the sole intention of creating COM objects. Since COM object creation was the primary goal of the framework, ATL has been trimmed of unnecessary baggage such as bloated UI components. The use of ATL provides developers with a number of benefits including the following:
One of the biggest benefits of using ATL is the support for the creation of multiple COM server types. When using the ATL wizard, the shell classes for each server type are automatically created during project initialization. ATL provides support for the following types of COM servers.
ActiveX has different threading models that can be utilized by COM servers. The ATL library provides built-in support for these different types of threading. Each model provides different capabilities, and care must be taken when deciding which model will be supported by the COM server.
When implementing free-threading servers, the burden for protecting data within a COM class from simultaneous updates or access by multiple threads falls on the programmer. Multiple threads may be attempting to access local data within the same instance of a COM object. ATL does not provide built-in data access synchronization. The use of Win32 synchronization objects such as events, semaphores, mutexes, and critical sections is needed for protecting COM class data.
A tear-off interface, a new concept introduced in the ATL framework, is an optimization of a regular COM interface in that it doesn't actually exist until it is instantiated by a call to QueryInterface on your object for that interface. Since the interface does not exist until asked for, it does not consume system memory resources.
When the Release method is called on the interface and the reference
count on that interface returns to zero, the interface is removed from memory. Typically,
tear-off interfaces are used only for those interfaces that are expected to be used
less often than others, such as ISupportErrorInfo.
NOTE: Tear-off interfaces should not be used for commonly used interfaces because the overhead of memory allocation and deallocation and memory fragmentation would outweigh the benefits of the interface.
To implement tear-off interfaces, declare a class that inherits from all the interfaces you want to implement in the tear-off, as well as from CComTearOffObjectBase<class Owner>, where Owner is the class of the main object. Then provide a normal BEGIN_COM_MAP...END_COM_MAP() specification of interfaces in the tear-off, and use the COM_INTERFACE_ENTRY_TEAR_OFF macro in the main object's COM map.
You can implement aggregation in ATL servers with very little work. Aggregation is when an object exposes another object's interface pointer as its own. For example, if an application has a pointer to interface A and needs to access interface B, and if interface A supports aggregation, the application can call QueryInterface on interface A to obtain interface B. The only penalty imposed for supporting aggregation is needing a somewhat larger server. The benefit is the flexibility to expose interfaces from objects contained in the server.
In order to make a server aggregatable, use the macro DECLARE_AGGREGATABLE in the COM object's class. If aggregration is not desired, use the macro DECLARE_NOT_AGGREGATABLE to disable aggregration. By default, aggregration is supported by projects created with the ATL COM AppWizard.
ATL supports the OLE error reporting mechanism with the Error() member function in the CComCoClass and CComISupportErrorInfoImpl classes. These classes each have a member, InterfaceSupportsErrorInfo(), that indicates whether returning rich error information is supported. By using this mechanism, custom COM interfaces can provide helpful information to the end user if error situations are encountered.
When using the ActiveX Template Library, the creation of COM servers is a trivial
task. The ATL installation creates an ATL COM AppWizard that can be accessed from
the Visual C++ development environment. The ATL COM AppWizard, like the MFC AppWizard,
presents the user with a step-by-step set of options for the creation of a COM server.
The end result of running the wizard is a ready-to-be-built project with all necessary
class template source code for the COM classes and interfaces that will be implemented
within the project.
NOTE: In Chapter 12, an interface library entitled IFish was created. The IFish and IBass interfaces built in that example will be constructed using the ATL library. The project AtlCustomBass will create a COM class CAtlCustomBass, used for accessing the IFish interfaces. The CAtlCustomBass class will be implemented as an in-process server.
To get started using the ATL COM AppWizard, a new project must be created. A new project can be created by performing the following steps:
Select the ActiveX Template Library COM AppWizard to create an ATL-based COM server.
The ATL COM AppWizard is presented in Figure 13.2.
Choose the COM object options by using the ATL COM AppWizard.
The New Project Information dialog box recaps the ATL COM server options.
After creating the project, a COM object needs to be added to the project. To add a COM object, perform the following steps:
Use the New Class dialog box to create COM classes.
The interface names can be changed from the Edit Interface Information dialog.
All the templates for the ATL COM server are now created. What remains is for the server's specific implementations to be incorporated. Before performing the customizations, you need to examine the results created by the ATL COM AppWizard.
A total of 11 files were created when the ATL COM AppWizard and New Class dialogs were used to create the ATLCustomBass project. Table 13.1 shows the filenames and purpose of each file created.
|Stdafx.cpp||Contains the includes needed globally for the project. This usually generates precompiled headings used by all other c or cpp files.|
|AtlCustomBass.cpp||Contains the COM server entry point implementations and COM class registration function.|
|AtlBass1.cpp||Skeleton cpp file for the AtlCustomBass COM object. All class and interface implementations are placed in this file.|
|AtlCustomBass.def||Export definition file for the COM object server.|
|AtlCustomBassps.def||Export definition file for the interface library. The interface library is used for generating proxy code for parameter marshaling.|
|AtlBass1.h||AtlCustomBass COM object definition file.|
|AtlCustomBass.idl||AtlCustomBass COM class and interface IDL definitions.|
|Resource.h||Standard resource file used for icons, version information, and so on.|
|Stdafx.h||Wrapper include file that includes all needed ATL header files.|
|Atlcustombassps.mk||Makefile used for compiling the interface definition file. This file produces the parameter marshaling code for the COM interfaces.|
Chapter 12 includes information about several access functions that must be exported from the COM server. These functions are accessed by the COM libraries to load, unload, and register the objects within the server. These functions are shown in the definition file for the CAtlCustomBass server (see Listing 13.1).
; AltCustomBass.def : Declares the module parameters.
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE
The ATL library provides all of the necessary code for implementing the DLL access functions. For the CAtlCustomBass project, these functions are implemented in the file (see Listing 13.2).
extern "C" HINSTANCE hProxyDll;
// DLL Entry Point
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
if (!PrxDllMain(hInstance, dwReason, lpReserved))
if (dwReason == DLL_PROCESS_ATTACH)
else if (dwReason == DLL_PROCESS_DETACH)
return TRUE; // ok
// Used to determine whether the DLL can be unloaded by OLE
if (PrxDllCanUnloadNow() != S_OK)
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
// Returns a class factory to create an object of the requested type
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
if (PrxDllGetClassObject(rclsid, riid, ppv) == S_OK)
return _Module.GetClassObject(rclsid, riid, ppv);
// DllRegisterServer - Adds entries to the system registry
HRESULT hRes = PrxDllRegisterServer();
// registers object, typelib and all interfaces in typelib
// DllUnregisterServer - Removes entries from the system registry
All COM classes and interfaces are defined through IDL, the Interface Definition Language. IDL is also used in the creation of RPC interfaces and Automation interfaces. When the AtlCustomBass project was created, the COM AppWizard generated the file AtlCustomBass.idl. This file contains template structures for the COM class and interfaces specified when running the AppWizard. All methods for the custom interfaces must be added to this file. Listing 13.3 illustrates the contents of AtlCustomBass.idl with the methods for custom interface IFish and IBass.
#define MAX_FISH_BSTR_LEN 255
typedef [string] WCHAR FISH_BSTR[MAX_FISH_BSTR_LEN];
// This file will be processed by the MIDL tool to
// produce the type library (AltCustomBass.tlb) and marshalling code.
interface IFish : IUnknown
HRESULT IsFreshwater([out] BOOL *pBool);
HRESULT GetFishName([out, string] FISH_BSTR p);
interface IBass : IUnknown
HRESULT GetLocation([out, string] FISH_BSTR p);
HRESULT SetLocation([in, string] FISH_BSTR p);
HRESULT EatsOtherFish([out] BOOL *pBool);
helpstring("AltCustomBass 1.0 Type Library")
[default] interface IFish;
In the IDL file for AtlCustomBass are definitions for two interfaces, IFish and IBass, and the COM class, CAltBass1. As you can see from Listing 13.3, both the IFish and IBass interfaces are derived from the IUnknown interface. In this example, a new type has been defined, and that type is FISH_BSTR. FISH_BSTR is defined as a wide-character string that is 255 characters in length. The wide-character string is used so that the server can be compiled as either multibyte (default) or UNICODE. The maximum length must be specified in the IDL definition. This is a requirement of the MIDL compiler used to compile the IDL file. In order for the MIDL compiler to produce code that handles parameter marshaling, the absolute length of the data item passed into functions must be known.
The COM class CAltCustomBass1 is a COM Object library that acts as a container for the COM interfaces IFish and IBass. The specified coclass identifies the definition as a COM class. As you can see from the definition, the IFish interface is the default interface. This is the pointer that is returned when the IID_IUnknown interface is queried.
The IDL file is compiled separately from the AtlCustomBass project. When the AtlCustomBass project is built, the AtlCustomBass.idl file is compiled with the MIDL compiler first. This step is automatically done as part of the build process.
The IDL can also be compiled separately outside the IDE. The ATL COM AppWizard generates a separate makefile for the IDL file (AtlCustomBassps.mk). The makefile program nmake can be run from a command prompt to compile the IDL file. The command line would look like this:
Implementing the COM interface using the ATL library is a simple process. For the programmer familiar with C++, this implementation is as simple as adding the interface methods to the COM class. The ATL library takes care of handling all of the IUnknown and IClassFactory interfaces, thus removing this burden from the developer. Even the MFC framework requires that the IUnknown interface must be handled by the programmer.
Listing 13.4 shows the COM class CAtlCustomBass definition file (AtlCustomBass.h). The only modifications needed for the AtlCustomBass COM server is to add the public interface methods and the variables needed by the class.
class CAltCustomBass1 :
// Remove the comment from the line above if you don't want your object to
// support aggregation. The default is to support it
DECLARE_REGISTRY(CAltCustomBass1, _T("AltCustomBass.AltCustomBass1.1"), _T("AltCustomBass.AltCustomBass1"), IDS_ALTCUSTOMBASS1_DESC, THREADFLAGS_BOTH)
BOOL m_bEatsFish; };
To support the IFish and IBass interfaces, the custom methods for each interface have been defined as standard C++ methods.
The ATL Template Library was developed to directly support and work with COM. This is in contrast to the MFC framework, which does not directly use COM but has hooks to manipulate COM. The COM class CAltCustomBass is derived from the classes and interfaces shown in Table 13.2.
|IFish||IFish COM interface, which is one of the custom interfaces supported by the class|
|IBass||Another custom interface specified during class construction|
|CComObjectRoot||ATL class that implements all reference counting and thread model-specific implementations|
|CComCoClass||ATL class that implements the class factory for the object, aggregation, and error handling|
For a more thorough discussion of techniques used for supporting multiple interfaces, refer to Chapter 14, which demonstrates the difference between direct inheritance and nested classes.
Implementing the C++ method that will support the COM interface is a straightforward matter. Listing 13.5 shows the implementation of the IFish and IBass COM interfaces used in the CAltCustomBass1 class.
wcscpy( m_FishName, L"Large Mouth Bass");
wcscpy( m_Location, L"Under Lily Pads");
m_bEatsFish = TRUE;
m_bFreshWater = TRUE;
STDMETHODIMP CAltCustomBass1::GetFishName( FISH_BSTR pStr)
STDMETHODIMP CAltCustomBass1::IsFreshwater( BOOL *pBool )
*pBool = m_bFreshWater;
// CBass:Fish implementation of IFish STDMETHODIMP CAltCustomBass1::GetLocation( FISH_BSTR pStr)
STDMETHODIMP CAltCustomBass1::SetLocation( FISH_BSTR pStr)
STDMETHODIMP CAltCustomBass1::EatsOtherFish( BOOL *pBool )
*pBool = m_bEatsFish;
// return E_BADPOINTER;
Again, note that the ATL Template Library implements the IUnknown interface for you, resulting in much fewer coding requirements for the developer. Now that the method implementation is complete, the COM server can be compiled and built.
The ATL library uses object maps to specify the COM objects that make up a particular ATL server. Object maps are arrays of structures that tell ATL about the objects implemented in a server. The members of an object map include the CLSID of the object and the class of the object.
When a specific interface is requested through the QueryInterface method, ATL uses COM maps to map interface IDs (IIDs) to offsets in the interface. COM maps are used by the class CComObjectRoot. When a user calls QueryInterface(), the ATL library internally calls InternalQueryInterface() to return an interface pointer based on an IID passed in.
Thirteen different types of entries can reside in a COM map (see Table 13.3).
|COM Entry Type||Description|
|COM_INTERFACE_ENTRY||Interface where only the class name needs to be in. The IID is synthesized by prepending IID_ to the class name. This is the basic and popular form of COM interfaces.|
|COM_INTERFACE_ENTRY_IID||Interface where the IID and the class name need to be passed in, for example, COM_INTERFACE_ENTRY_IID (IID_IFish, IFish).|
|COM_INTERFACE_ENTRY2||Interface where it is necessary to distinguish conflicting interfaces. For example, if you have dual interfaces (IFoo and IBar) in an object, specifying COM_INTERFACE_ENTRY (IDispatch) would be ambiguous because both IFoo and IBar derive from Idispatch. However, by specifying COM_INTERFACE_ENTRY2 (IDispatch, IFoo), you can control which interface is returned.|
|COM INTERFACE_ENTRY2_IID||Interface where the IID and the class name need to be passed in and you need to disambiguate interfaces.|
|COM_INTERFACE_ENTRY_TEAR_OFF||Interface is a tear-off interface. Tear-off interfaces are generally created each time a client calls QueryInterface for a particular interface, even if a tear-off for that interface is already instantiated.|
|COM_INTERFACE_ENTRY_CACHED_TEAR_OFF||Interface is a tear-off interface; however, ATL creates an object implementing the tear-off interface only the first time the interface is requested. Subsequent QueryInterface calls will reuse the object previously instantiated.|
|COM_INTERFACE_ENTRY_AGGREGATE||Indicates that a QueryInterface call for a particular interface should go through the aggregate.|
|COM_INTERFACE_ENTRY_AGGREGATE||Indicates that a QueryInterface call for an interface _BLIND should be blindly forwarded to the aggregate.|
|COM_INTERFACE_ENTRY_AUTOAGGREGATE||Automatically creates the aggregate when a client performs a QueryInterface for a particular interface on the aggregate.|
|COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND||Automatically creates the aggregate when a client calls QueryInterface for an interface that cannot be found on the outer object.|
|COM_INTERFACE_ENTRY_CHAIN||Chains to the COM map of a base class.|
|COM_INTERFACE_ENTRY_FUNC||Allows you to programmatically hook the creation of a particular interface pointer.|
|COM_INTERFACE_ENTRY_FUNC_BLIND||Allows you to programmatically hook the creation of a pointer to any interface, thus not found.|
The ActiveX template library offers many advantages to builders of COM interfaces and COM objects. The ATL library offers the advantage of building fast, lightweight COM servers. However, using ATL may not be the best method of implementation depending on the situation.
ATL is focused entirely on the creation of small, fast COM servers in C++. ATL is optimized for the creation of objects that expose custom or dual-interfaces and has absolutely no inherent support for more complex COM-based architectures, such as ActiveX documents.
If you want to create generic COM objects or OLE automation objects with dual-interface support or you want to support COM's free-threading model (available with Windows NT 4.0 and later versions of Windows) and you don't have a significant user interface in your object, ATL is the choice for producing the smallest, fastest code.
If you want to create complex servers that need to support user-interface items, ActiveX controls, or ActiveX documents, then a more robust framework such as MFC should be used.
This chapter has illustrated some of the many benefits gained when using the ActiveX
Template Library. The ATL COM AppWizard was used to create a framework for a COM
server. The New Class dialog was used to create a COM object with multiple custom
interfaces. All that the user must implement is the custom functionality of the server.
Chapter 12 uses the MFC framework to create COM servers. MFC
is a feature-rich application framework that can be used for building COM servers.
Chapter 14 illustrates a custom COM architecture for building
COM servers. The custom architecture is not derived from ATL or MFC.