ATL-Based DLLs

[Previous] [Next]

COM-based DLLs have several distinguishing characteristics. First, COM-based DLLs export a function that the service control manager (SCM) requires in order to locate the class objects within the DLL (DllGetClassObject). COM-based DLLs also maintain a reference count on themselves. Clients have the option of flushing the DLL from its process space by calling CoFreeUnusedLibraries. When a client calls CoFreeUnusedLibraries, COM goes to each DLL in the process space and asks the DLL whether the DLL can be unloaded. COM calls into the DLL's DllCanUnloadNow function, which returns S_OK if the DLL can be unloaded and S_FALSE if the DLL can't be unloaded. (The DLL simply checks its reference count.) Finally, COM-based DLL servers are supposed to self-register and self-unregister themselves in the Registry by exporting two functions—DllRegisterServer and DllUnregisterServer. These are the hooks that COM expects to see in your DLL.

The ATL COM AppWizard's default mode is to create a COM-based DLL. The COM-specific aspects of a DLL include the following four well-known entry points:

  • DllGetClassObject This function is required so that the COM run time can retrieve the class object (often known as the class factory). The ATL COM AppWizard-generated version of DllGetClassObject delegates its work to CComModule, which maintains a list of class objects within the server.
  • DllCanUnloadNow DLLs export this function so that the COM run time knows whether it can unload the DLL. The ATL COM AppWizard-generated version of DllCanUnloadNow delegates its work to CComModule, which maintains the reference count of extant objects.
  • DllRegisterServer DLLs implement this function for inserting appropriate entries into the Registry. The ATL COM AppWizard-generated version of DllGetClassObject delegates its work to CComModule, which uses the ATL Registrar component to read the server's Registry script and insert Registry entries.
  • DllUnregisterServer DLLs implement this function for removing a COM class's entries from the Registry. The ATL COM AppWizard-generated version of DllGetClassObject delegates its work to CComModule, which uses the ATL Registrar component to read the server's Registry script and remove Registry entries.

Selecting a COM-based DLL from the ATL COM AppWizard yields the following files for building a COM-based DLL:

  • <project name>.cpp includes the DllMain entry point as well as the Dllxxx functions COM expects. This file satisfies calls to the well-known functions by delegating to a CComModule class that is part of the project.
  • <project name>.def explicitly exports DllGetClassObject, DllCanUnloadNow, DllRegisterServer, and DllUnregisterServer. These are the hooks that COM expects to find inside your DLL.
  • <project name>.idl represents a blank IDL file awaiting some interface definitions.
  • <project name>.rc includes any resources you want to include in your COM-based DLL.
  • stdafx.h is where general #include statements go.
  • stdafx.cpp compiles the stdafx.h file.
  • resource.h includes any identifiers your resource script requires.
  • <project name>.clw is used by ClassWizard (if you decide you really can't live without MFC in your project).

Compiling the project yields three more files:

  • <project name>.h includes the C++ versions of the COM interfaces found in <project name>.idl.
  • <project name>_i.c includes the GUIDs defined in <project name>.idl.
  • <project name>.tlb is the binary description of your COM-based server (a type library).

In addition to the source code files listed above, you also get project files for a proxy/stub DLL based on the contents of <project name>.idl. The proxy/stub source code consists of three more files:

  • <project name>ps.def
  • <project name>
  • dlldata.c (This file is produced if <project name>.idl has interfaces defined in it.)

As far as clients are concerned, an ATL-based COM DLL is just a regular COM DLL. The difference is that many of the implementation details are handled internally by ATL. Let's take a closer look at the ATL COM AppWizard options.

MFC Support in ATL

One of the most interesting aspects of ATL is that although ATL is meant to be a lightweight alternative to MFC for creating ActiveX controls, you can include MFC in your ATL-based DLL. Just mark the Support MFC check box before building your DLL. Clicking on Support MFC within the ATL COM AppWizard simply inserts two more lines into stdafx.h:

 #include <afxwin.h> #include <afxdisp.h> // stdafx.h includes MFC by including these //  two files if you mark the Support MFC  //  check box. 

Besides adding the include files, the AppWizard links the import library for the MFC run-time DLL into your project.

What can you do once you include MFC in your DLL? You can in fact use almost all of MFC within your COM server. This includes such classes as CString and MFC's collection classes. You also get the wrappers for device contexts (the CDC family), the CMenu class, CCmdTarget, MFC's message-mapping architecture, MFC's window and dialog support, MFC's document/view architecture, and MFC's support for Automation.

If you're a seasoned MFC developer and you're used to having all these goodies, you might find this useful. For example, if you simply must include a dialog inside your COM class, there's no more convenient way than to whip one up using Visual C++'s resource editor and create a CDialog class using ClassWizard. Then just include the dialog header wherever you want to display the dialog, create an instance of the dialog class, and call DoModal. In addition, many MFC developers are quite used to CString and those handy collection classes. It's hard for some developers to give them up. Just mark the Support MFC check box and it's all there for you.

Unfortunately, this MFC support comes at a price. The minute you include MFC support in your ATL-based COM DLL, your COM DLL has to start lugging around the MFC run-time DLL. Wait a minute—that's just the problem COM is trying to solve, isn't it? In addition to this problem, because the MFC AppWizard didn't create the project, there's no Object Description Language (ODL) file. (The MFC AppWizard pumps out an ODL file whenever you check the Automation check box.) Therefore, ClassWizard chokes whenever you want to use it to implement IDispatch (by going to the Automation tab). ClassWizard looks for the <project name>.odl file and displays a warning if it's missing. But you do get all the comforts of the MFC classes, and using MFC in your ATL class is sometimes justified—especially if you want to have a whole lot of legacy MFC source code and the MFC DLL will be loaded into memory anyway.

The next check box available through the AppWizard offers the ability to merge the proxy/stubs into your COM DLL.

Merging Proxy/Stub Pairs

A major goal of COM is for clients to be able to use an object in the same way regardless of its location. To accomplish this feat, COM's remoting architecture uses proxy/stub pairs. Whenever a client calls QueryInterface on an object living in another execution context (perhaps another process or even a remote machine), the remoting layer installs an interface proxy on the client side and an interface stub on the server side. On the client side, the proxy looks, feels, and tastes like the COM interface that the client expects. However, rather than talking to the COM object directly (which would happen if the object executed in the same process and thread), the client talks to the proxy. The proxy in turn talks to the remoting layer to make function calls into the other execution context. On the server side, the stub receives calls from the proxy and sets up the call in the other execution context. Then the stub actually calls in to the object to do the work. The stub sends any results back to the proxy, which in turn gets the results back to the client.

We first discussed merging proxy/stub pairs in Chapter 4. We're repeating some of the same information here in case you just skimmed that chapter.

Normally, proxy/stub pairs are a product of first compiling some IDL code to get some C source code. Then you take that source code and compile it into a DLL containing the proxy/stub pairs. By default, the ATL COM AppWizard generates the C source code for a proxy/stub DLL based on the contents of the project's IDL file. Of course, this produces a separate proxy/stub DLL for your interfaces. If you like, you can choose to store the proxy/stub pairs inside your COM-based DLL. Do this by checking the Allow Merging Of Proxy/Stub Code option in the ATL COM AppWizard.

Checking Allow Merging Of Proxy/Stub Code generates the same files as a regular COM DLL plus two more files: dlldatax.c and dlldatax.h. Also, the file <project name>.cpp is somewhat larger to handle requests for the proxy. Whether or not you want to merge the proxy/stub objects into your DLL is controlled by the precompiler definition _MERGE_PROXYSTUB. To produce a single DLL that has your COM classes and the proxy/stub pair for remoting your interface, simply add dlldatax.c to the project and add _MERGE_PROXYSTUB to the list of precompiler definitions. Depending on your environment (perhaps you're running Windows 95 or Windows 98 without DCOM, for example) you might have to turn off the #define _WIN32_WINNT 0x0400 symbol. If you want a separate proxy/stub DLL, you can run NMAKE by itself to produce a separate DLL. The DLL will produce all the correct Registry entries once it is registered.

The ATL COM AppWizard includes one more option as far as DLLs are concerned—creating a Microsoft Transaction Server (MTS) DLL.

Support MTS

The MTS option adds only a little bit to your project: Microsoft Transaction Server's import library (MTX.LIB) and the GUIDs necessary for doing MTS work. Choosing this option also causes your object to register in the MTS catalog.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: