- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Using IDL to Describe Custom COM Objects
- A Custom COM Class Example
- Summary
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
A Custom COM Class Example
As an example of how custom COM classes are created with IDL and the Active Template Library, the CD-ROM that accompanies this book includes OsVersionInfo, a custom COM class that is a wrapper around the Win32 GetVersionEx function.
The OsVersionInfo project creates an EXE module that includes a COM class named OsVersion created using the ATL Object Wizard. Table 30.1 lists the values used in the Names property sheet for the OsVersion ATL class.
Table 30.1. Names Property Sheet Values for OsVersion
| Field | Name |
| Short Name | OsVersion |
| Class | COsVersion |
| H File | OsVersion.h |
| CPP File | OsVersion.cpp |
| CoClass | OsVersion |
| Interface | IOsVersion |
| Type | OsVersion Class |
| Prog ID | OsVersionInfo.OsVersion |
Table 30.2 lists the values used on the Attributes property page for OsVersion.
Table 30.2. Attributes Property Sheet Values for OsVersion
| Field | Name |
| Threading Model | Both |
| Interface | Custom |
| Aggregation | Yes |
| ISupportErrorInfo | Unchecked |
| Connection Points | Unchecked |
| Free Threaded Marshaler | Unchecked |
Defining the IOsVersion Interface
The IOsVersion interface has a number of methods, as shown in the IDL provided in Listing 30.1. Lines that were added to the original code generated by the wizard are shown in bold.
Example 30.1. OsVersionInfo IDL File
import "oaidl.idl";
import "ocidl.idl";
typedef enum tagOsType
{
OsUnknown = 0,
OsWin32s,
OsWin95,
OsWin98,
OsWinNt,
}OsType;
typedef struct tagOsVerNum
{
long Major;
long Minor;
}OsVerNum;
[
object,
uuid(414AE921-EAFA-11D3-8E67-00C04F8DC7A5),
helpstring("IOsVersion Interface"),
pointer_default(unique)
]
interface IOsVersion : IUnknown
{
HRESULT GetVersionInfoString([out,retval]BSTR* pBstrInfo);
HRESULT GetVersion([out]OsVerNum *pVerNum);
HRESULT GetBuildNumber([out]long* pBuild);
HRESULT GetOsType([out]OsType* pType);
};
[
uuid(414AE913-EAFA-11D3-8E67-00C04F8DC7A5),
version(1.0),
helpstring("OsVersionInfo 1.0 Type Library")
]
library OSVERSIONINFOLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
struct OsVerNum;
enum OsType;
[
uuid(414AE922-EAFA-11D3-8E67-00C04F8DC7A5),
helpstring("OsVersion Class")
]
coclass OsVersion
{
[default] interface IOsVersion;
};
};
Listing 30.1. contains two typedefs:
- The OsType enumeration contains values that represent different flavors of Windows operating systems.
- The OsVerNum structure is a simple container for the major and minor version numbers for an operating system release.
The OsType and OsVerNum types are referenced in the library section of the IDL file. This causes the MIDL compiler to include these two types in the type library when the IDL is compiled.
Modifications to the COsVersion Declaration
The definition for the COsVersion class is located in the OsVersion.h header file, portions of which are shown in Listing 30.2. Modifications to the wizard-generated source are shown in bold.
Example 30.2. Definition of the COsVersion Class
class ATL_NO_VTABLE COsVersion :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<COsVersion, &CLSID_OsVersion>,
public IOsVersion
{
public:
COsVersion()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_OSVERSION)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(COsVersion)
COM_INTERFACE_ENTRY(IOsVersion)
END_COM_MAP()
HRESULT FinalConstruct();
// IOsVersion
public:
STDMETHOD(GetOsType)(OsType* pType);
STDMETHOD(GetBuildNumber)(long* pBuild);
STDMETHOD(GetVersion)(OsVerNum* pVerNum);
STDMETHOD(GetVersionInfoString)(/BSTR* pBstrInfo);
protected:
OSVERSIONINFO m_info;
};
The FinalConstruct method declared in Listing 30.2 is called by the framework after the COM object is constructed. This enables you to perform initialization work, such as calling a virtual function that isn't safe in a constructor. It also allows you to bypass the constructor completely—a technique used when omitting the C runtime library from your component.
Implementing the COsVersion Class
The implementation of the COsVersion class is located in the OsVersion.cpp source file. A partial listing of this file is provided in Listing 30.3. The accompanying CD-ROM includes the complete source for this file.
Example 30.3. Implementation of the COsVersion Class
HRESULT COsVersion::FinalConstruct()
{
ZeroMemory(&m_info, sizeof(OSVERSIONINFO));
m_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&m_info);
return S_OK;
}
STDMETHODIMP COsVersion::GetVersionInfoString(BSTR *pBstrInfo)
{
CComBSTR bstrTemp(m_info.szCSDVersion);
if(!bstrTemp)
return E_OUTOFMEMORY;
*pBstrInfo = bstrTemp.Detach();
return S_OK;
}
STDMETHODIMP COsVersion::GetVersion(OsVerNum *pVerNum)
{
pVerNum->Major = m_info.dwMajorVersion;
pVerNum->Minor = m_info.dwMinorVersion;
return S_OK;
}
STDMETHODIMP COsVersion::GetBuildNumber(long *pBuild)
{
// If the OS is Windows 9x, the build is in the lower
// word of dwBuildNumber. If the OS is Windows NT/2000
// the build is the entire DWORD. For Win32s, the build
// number is cleared.
if(m_info.dwPlatformId == VER_PLATFORM_WIN32_NT)
*pBuild = m_info.dwBuildNumber;
else if(m_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
*pBuild = LOWORD(m_info.dwBuildNumber);
else
*pBuild = 0;
return S_OK;
}
STDMETHODIMP COsVersion::GetOsType(OsType *pType)
{
switch(m_info.dwPlatformId)
{
case VER_PLATFORM_WIN32s:
*pType = OsWin32s;
break;
case VER_PLATFORM_WIN32_WINDOWS:
if(m_info.dwMinorVersion == 0)
*pType = OsWin95;
else
*pType = OsWin98;
break;
case VER_PLATFORM_WIN32_NT:
*pType = OsWinNt;
break;
default:
*pType = OsUnknown;
break;
}
return S_OK;
}
When a COM object is constructed, the ATL framework calls the FinalConstruct method . Inside COsVersion::FinalConstruct, the Win32 GetVersionEx function is called to collect version information from the operating system and store it in the m_info member variable. Other method calls return information to the client based on the contents of m_info.
Build the OsVersionInfo project. After the OsVersionInfo module is compiled successfully, the project takes steps to register the component on your system. You won't be able to use the component from a client, however, until you build and register the proxy/stub DLL, as described in the next section.
Compiling and Registering the Standard Proxy/Stub DLL
ATL projects include a command-line makefile used to build the proxy/stub DLL for each ATL project. This makefile is the only file in the project directory with an .mk filename extension. For the OsVersionInfo project, the name of the makefile is OsVersionInfoPs.mk. To build the DLL, use the NMAKE utility, like this:
nmake OsVersionInfoPs.mk
After the proxy/stub DLL is built successfully, you can register the DLL using the RegSvr32 utility:
regsvr32 OsVersionInfoPs
You should run both of these commands in a DOS command window from the project directory. In order to compile the proxy/stub DLL from a command window, you may need to run the vcvars32.bat file located in the Visual C++ bin directory. The default path to this file is:
\Program Files\Microsoft Visual Studio\VC98\bin\vcvars32.bat
If the DLL is registered successfully, a message box appears, as shown in Figure 30.4.
Figure 30.4 The RegSevr32 utility is used to register the proxy/stub DLL.
Creating Test Clients for OsVersionInfo
To facilitate testing of the OsVersionInfo COM object, the CD-ROM that accompanies this book includes OsInfoClient, a console-mode test driver. OsInfoClient calls each of the IOsVersion interface functions and displays the results to the console window.
The OsInfoClient project was built as a Win32 console application. Unicode compatibility macros are used to enable the project to be built as either a Unicode or ANSI project. The project uses the osversioninfo_i.c and osversioninfo.h files from the OsVersionInfo project directory. The MIDL compiler generates these two files when the IDL file is compiled; they contain information about the GUIDs and interface declarations used by the OsVersionInfo COM object.
The OsInfoClient project contains the main.cpp source file, portions of which are provided in Listing 30.4. The accompanying CD-ROM includes the complete source for this project.
Example 30.4. OsInfoClient Test Driver
#define _WIN32_DCOM
#include <windows.h>
#include <tchar.h>
#ifndef UNICODE
#include <stdio.h>
#endif
#include <atlbase.h>
#include "osversioninfo_i.c"
#include "osversioninfo.h"
// Display a dialog box that contains an error message
void HandleError(LPCTSTR pszTitle, HRESULT hr);
// Macro that tests an HRESULT - if an error exists, a dialog box
// with the error message is displayed, and zero is returned.
#define TESTHR(hr,str) if(FAILED(hr)){HandleError(str,hr);return 0;}
// Utility class that ensures that CoInitialize and CoUninitialize are
// called at the appropriate times.
struct CComInit
{
HRESULT hr;
CComInit(DWORD dwInit)
{
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
}
~CComInit()
{
CoUninitialize();
}
};
CComInit init(COINIT_MULTITHREADED);
int _tmain()
{
// Uses the AFX string conversion macros to convert between
// OLE/BSTR strings and T strings.
USES_CONVERSION;
CComQIPtr<IOsVersion> pVersion;
HRESULT hr = pVersion.CoCreateInstance(CLSID_OsVersion);
TESTHR(hr, _T("CoCreateInstance"));
OsVerNum ver;
hr = pVersion->GetVersion(&ver);
TESTHR(hr, _T("GetVersion"));
// Get the OS type, and convert the enumerated type into a
// string.
OsType ost;
hr = pVersion->GetOsType(&ost);
TESTHR(hr, _T("GetOsType"));
CComBSTR bstrOsType;
switch(ost)
{
case OsWin32s:
bstrOsType = _T("Win32s");
break;
case OsWin95:
bstrOsType = _T("Windows 95");
break;
case OsWin98:
bstrOsType = _T("Windows 98");
break;
case OsWinNt:
bstrOsType = _T("Windows NT/2000");
break;
default:
bstrOsType = _T("Unknown");
}
long build;
hr = pVersion->GetBuildNumber(&build);
TESTHR(hr, _T("GetBuildNumber"));
CComBSTR bstrVersionInfo;
hr = pVersion->GetVersionInfoString(&bstrVersionInfo);
TESTHR(hr, _T("GetVersionInfoString"));
// Display results to the console - use W2T to convert from
// wide strings to T strings.
_tprintf(_T("Windows OS Type: %s\n")
_T("Version : %d.%d\n")
_T("Build Number : %d\n")
_T("Version Info : %s\n"),
W2T(bstrOsType),
ver.Major,
ver.Minor,
build,
W2T(bstrVersionInfo));
return 0;
}
Listing 30.4 includes the CComInit class, which guarantees that CoInitialize and CoUninitialize are called at the appropriate times. By declaring a global variable of CComInit, you can be sure that CoInitialize is called before any smart pointers have been initialized, and CoUninitialize is called after all of the COM smart pointer instances are destroyed.
Build the OsInfoClient project. After OsInfoClient is run from the command line, it displays information about the version of the operating system, as Figure 30.5 shows.
Figure 30.5 The OsInfoClient project uses OsVersionInfo to collect version information about the operating system.
Summary | Next Section

Account Sign In
View your cart