COlePropertyPage in ATL server

I have been using ATL extensively since it first appeared. Whilst ATL is
marketed as a lightweight solution of Internet controls, it is also a very
good tool for developing applications that expose their functionality via
interfaces.

I wanted to use the flexibility of the MFC classes along with the
simplicity that ATL lends to writing objects. Specifically, I saw no reason
to use the ATL property page stuff when MFC contains a perfectly acceptable
solution (COlePropertyPage) AND also allows me to add message handlers very
simply using ClassWizard or the WizardBar.

Presented here is my solution to seamlessly add an MFC COlePropertyPage
derived class to an ATL in-process server (DLL) that is marked as using
MFC. Not a trivial exercise.

The explanation is lengthy, but the resulting class really very simple to
use. I only hope my contribution is as useful to others as the information
I have gleaned from your site.

This code has been tested using Visual C++ v5 under Windows NT 4.


Download the project files (36K)


File required to use this feature


MfcAtlPropertyPage.h (1K) MfcAtlPropertyPage.rgs (1K)


Warning


This example use undocumented MFC methods and member variables. These may change in a future release of VC++.



Specifics


You are using ATL to build and in-process server (DLL) that will expose one or more objects and wish to use the MFC COlePropertyPage class as the base for your property pages.


You have elected to use MFC as this server is not going anywhere near the internet so you are not concerned about heaving the MFC DLL down a ‘phone line.


You want to use MFC for your property pages as those nice people at Microsoft must have put in a lot of time so you could use their COlePropertyPage class.




Setting up the project


  1. Create a new project (File | New | Project | ATL COM AppWizard).

  2. Enter the project name (I will call it ATLMFC) and make sure Create new workspace is selected. Click OK.

  3. The ATL COM AppWizard is displayed. Select the Dynamic Link Library (DLL) and Support MFC options. Press Finish, then OK.



Problem


ATL uses an object map to manage the creations all of the objects that your server exposes. This is, by default, in a file that has the same name as your project. In this case, the file is called ATLMFC.cpp. The object map starts with the line BEGIN_OBJECT_MAP(ObjectMap) and end with the line END_OBJECT_MAP().


In between these two lines go all of your object declarations, in ATL notation. An example might be:


OBJECT_ENTRY(CLSID_CTest, CTest)


The macro takes two parameters, the CLSID of your object and the class that will be used by ATL to create an instance of your object (never mind the specifics).


 All classes that go in the object list will be created as CComObject<CYourATLObjectClass>, such as CComObject<CTest>, from the above example (CComObject is an ATL class). This means that only ATL style classes may appear in this list. Specifically, you can not use this method to add your COlePropertyPage derived class directly to the object map. This seemed very restrictive to me, as I wanted to use the ClassWizard to add all of the messages to my property page. I just couldn’t be doing with adding everything by hand (the ATL way).




Solution


The solution is to have a lightweight ATL wrapper class that links up with your COlePropertyPage derived class. To do this, several issues must be addressed:


  • The final class must be easy to use

  • Each property page ATL/MFC class must have a unique CLSID

  • The solution class must be able to register correctly in the system registry

My solution is to have a template ATL class, a special ATL registry file (.rgs) and to use a typedef to declare the final class that is used.




Template class


The file MfcAtlPropertyPage.h defines the template class that implements all of the stuff that ATL requires for the CComObject class and exposes your COlePropertyPage derived class. It also includes code for updating the system registry with the appropriate details.


The following, undocumented, MFC stuff is accessed in the FinalConstruct member function:


  • EnableAggregation

  • m_pOuterUnknown

  • xInnerUnknown

These are required in order to hook the ATL and MFC objects together properly




Registry file


The registry file MfcAtlPropertyPage.rgs contains the details for correct registration of the property page in the system registry. This file is used by CMfcAtlPropertyPage to register each property page that we create.




Creating a COlePropertyPage derived class for use with ATL


This section details, step by step, how to use an MFC COlePropertyPage derived class in your ATL project. These steps also appear in the header file MfcAtlPropertyPage.h.

Once only for the project


Import the resource file MfcAtlPropertyPage.rgs into your project. Give the resource an ID of IDR_MFCATLPPG (This is the name of the resource that CMfcAtlPropertyPage will use. Ensure the correct path is given in the file view for MfcAtlPropertyPage.rgs and change if neccessary.


Add an include for afxctl.h in the file StdAfx.h


Step by step


  • From the menu, select Insert | Resource.

  • Select Import

  • Change the Files of type to All Files

  • Locate the file MfcAtlPropertyPage.rgs

  • Select Import

  • If prompted, enter the resource type now. Call it REGISTRY

  • Select the resource in the project window (Resource tab), right-click, select Properties and change the ID of the resource to IDR_MFCATLPPG

  • Open the file StdAfx.h and add the line #include <afxctl.h> to it

1. Update your IDL/ODL file


Add a coclass statement to your IDL/ODL file for the new property page and support the interface IUnknown. This must appear within the library section of the file.


Example


[


uuid(E948B670-153F-11d1-8F2D-000000000000),


helpstring(“MFC Property Page”)


]


coclass PpgTest


{


interface IUnknown;


};


Make sure you use the GUIDGEN.EXE utility to generate the CLSID (uuid) for this class (Run GUIDGEN, select registry format then Copy. Paste the resulting text into the uuid entry and remove leading { and trailing }. Do not use the one given in this example.


2. Create the property page class


Create a property page resource and COlePropertyPage derived class as normal using the dialog editor and ClassWizard. Make the necessary change to the source files, as described below.


Step by step


Add the Dialog resource


  • From the menu, select Insert | Resource

  • Open the Dialog item

  • Select the IDD_OLE_PROPPAGE_LARGE resource

  • Select New


  • In the Resource view of the project window, ensure the language for the property page is correct (UK versions will by default create the resource as US). To change the language, select the resource in the project window, right-click and select Properties. Change the language to the correct entry.


  • Design your property page as you wish

Add a new class for the property page


  • Create a new class using ClassWizard (CTRL+W)

  • Select Create a new class

  • Give the class a name, I have called the example class CPpgTest

  • Unfortunately there is no option to derive from COlePropertyPage :-(. Select the next best thing, CPropertyPage

  • Select OK

  • Select OK

Add a new string resource to serve as the caption for the property page


  • Add the string resource

  • Enter the text that will appear in the tab for the property page (I have added the resource IDS_PPGTEST with the text Test)

Alter the source files


  • Open the header file for your property page (in this case CPpgTest)

  • Change the single instance of CPropertyPage to COlePropertyPage

  • Open the source file (in this case CPpgTest.cpp)

  • Change all instances of CPropertyPage to COlePropertyPage. There should be four of them

  • Add the line #include “resource.h” after the line #include “stdafx.h”. ClassWizard expects the line #include “<project>.h” (where <project>.h is the main header file for your project) to include resource.h. For ATL projects this is not the case.

  • Add the second argument to the call to the COlePropertyPage constructor. This is the name of the string resource added above (in this example, IDS_PPGTEST)

3. Create a new header file for the class definition


Create an empty text file and add the following:


  • An include statement for MfcAtlPropertyPage.h

  • An include statement for your COlePropertyPage derived class header file

  • A typedef statement for the CmfcAtlPropertyPage template class using your property page. This is required as the ATL OBJECT_ENTRY macro does not accept template class declarations

Save the file in your project directory


Example


// OlePpgTest.h – Declaration of COlePpgTest typedef


#include “MfcAtlPropertyPage.h”


// Definition of CPpgTest (COlePropertyPage derived) class


#include “PpgTest.h”


// typedef that stops OBJECT_ENTRY macro from crying


typedef Extraview::CMfcAtlPropertyPage<&CLSID_PpgTest, CPpgTest> COlePpgTest;


4. Update the server object map


Open the main ATL server source file that contains the server object map. This file typically has the same name as your project, with the cpp extension.


Add your new include file to the list of includes. Add your typedef class to the object map by inserting a new OBJECT_ENTRY macro entry.


Example


// ATLMFC.cpp : Implementation of DLL Exports.


// <cut>


#include “OlePpgTest.h”


// <cut>


CComModule _Module;


BEGIN_OBJECT_MAP(ObjectMap)


OBJECT_ENTRY(CLSID_PpgTest, COlePpgTest)


END_OBJECT_MAP()


ATL will now use your MFC COlePropertyPage derived class as though it were part of ATL!!




Testing the property page


The workspace includes a project called Test which will display the property page. I have added a simple object to the ATLMFC project called Test (ITest, CLSID_Test) that supports the interface ISpecifyPropertyPages. This returns the details for the property page. The test program itself is a simple console application.


 

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read