Using Containment In ATL - A Complete Application

This article is another in the series of tutorials which I did a couple of months ago ( and This article focuses on how to use containment for containing small COM objects inside one COM object. The application, which I have used for this project, has been previously posted in System category ( I have added some new information to that application. I would be briefly discussing the various API calls used for the new information presented.

The interfaces (projects) included in the application are as follows:

ISystemInfo - Outer COM object containing al other small COM objects
IOSInformation - Interface implementing OS information
ICPUInformation - Interface implementing information relating to CPU
IHDiskInformation - Interface implementing Hard disk (partition) information
IStorageInformation - Interface implementing information about all storage devices on system
IMemeoryInformation - Interface implementing information about memory of system
IMultiMediaInformation - Interface implementing information audio device on system
IMiscInformation - Interface implementing information not covered by other interfaces

All the inner objects and inner object is automation compliant. I will discuss only one of the inner ones and the outer object.

IHDiskInformation Interface:

This interface gathers the information about all the disk partitions on the system. The steps followed are:
1. In the work space, add new project.
2. Choose ATL COM Appwizard. Give name to the project. In this case it was given SystemHDisk. Click on Add To Current Workspace radio button. Click OK.
3. In next step, choose all the default options i.e. we want to make DLL. And finish.
4. This will add the project to the workspace.
5. Click on Insert menu option at the top.
6. Click on New ATL Object.
7. From the different options available, choose Simple Object. Since we are not making a full ActiveX control, Simple Object will serve our purpose.
8. On Names property page of ATL Object Wizard Properties, fill in a meaningful name for the interface. In this case, I used HDiskInformation. This will automatically suffix the short name with "I" to give the interface name (e.g. IHDiskInformation in this case).
9. On the Attributes page, keep all the default options and in addition check the Support ISupportErrorInfo check box. Checking this option means that the interface will implement the error-reporting interface too.
10. After filing all the information, click OK and this would add the interface to our project.

Now we are ready for implementation of the interface. Add properties and method definitions to the interface. For IHDiskInformation, the IDL file looks like this:

	helpstring("IHDiskInformation Interface"),
interface IHDiskInformation : IDispatch
[propget, id(1), helpstring("property NumberOfPartitions")] HRESULT NumberOfPartitions([out, retval] long *pVal);
[propget, id(2), helpstring("property Bootable")] HRESULT Bootable([out, retval] VARIANT *pVal);
[propget, id(3), helpstring("property Letter")] HRESULT Letter([out, retval] VARIANT *pVal);
[propget, id(4), helpstring("property PartitionType")] HRESULT PartitionType([out, retval] VARIANT *pVal);
[propget, id(5), helpstring("property PartitionNumber")] HRESULT PartitionNumber([out, retval] VARIANT *pVal);
[propget, id(6), helpstring("property PartitionLength")] HRESULT PartitionLength([out, retval] VARIANT *pVal);
[propget, id(7), helpstring("property HiddenSectors")] HRESULT HiddenSectors([out, retval] VARIANT *pVal);
[id(8), helpstring("method GetHDiskInformation")] HRESULT GetHDiskInformation(long *plNumberOfPartitions, VARIANT *pbstrDriveLetterArr, VARIANT *pbBootableArr, VARIANT *pbstrTypeArr, VARIANT *plPartitionNumberArr, VARIANT *plLengthArr, VARIANT *plHiddenSectorsArr);

As is clear from the IDL file that interface implements 7 properties (NumberOfPartitions, Bootable, Letter, PartitionType, PartitionNumber, PartitionLength, HiddenSectors) and 1 method (GetHDiskInformation). All the properties give the different types of partition information individually. And if the user wants all the information about all the partitions with one call, the method call will be very useful. I had my reasons to implement it like this. If I want to run these interfaces over DCOM then I would like to reduce the number of calls over the network. And getting all the information with one function call definitely serves that purpose. For the information types, I used VARIANT data type to make it automation compatible e.g. Bootable property, the argument VARIANT has an array of VT_BOOL type. You can look at the code what different VARIANT data types for this interface correspond to.

To extract information for each partition, first I made GetLogicalDrives API call to get all the logical drives on the system. Then I used GetDriveType API call to get the type of drive associated with the drive letter. If the drive type is DRIVE_FIXED, it means it's a hard disk partition. Then for this drive make DeviceIoControl API call with dwIoControlCode argument set to IOCTL_DISK_GET_PARTITION_INFO. This will give all the information about the disk pertition. For all the argument types, check on the help for this call. After we have obtained all the information with the appropriate API calls, fill the VARIANT arrays with corresponding information.

ISystemInfo Interface:
This is the outer interface, which contains all the inner COM objects/interfaces. We follow the same steps as we did for IHDiskInterface interface. We will not have any properties for this interface because this interface doesn't implement anything by itself. If you look in SystemInfo.idl file, it only has definition for 10 methods. And each method corresponds to their counterpart in individual inner COM objects. E.g. we have GetHDiskInformation method in IHdiskInformation interface. This interface is contained ISystemInfo interface. Therefore ISystemInfo interface has the same method with same arguments. It redirects the call to the inner object to get the method implementation.


To implement containment, the outer object has to implement FinalConstruct of CComObjectRootEx Class. This is the place where all the contained objects will be constructed. For the application attached, the implementation looks like this.

CSystemInformation::FinalConstruct ()

hr = CoCreateInstance (CLSID_OSInformation, 0, CLSCTX_INPROC_SERVER, IID_IOSInformation, (void **) &m_pOSInfo);
	if (FAILED (hr)) {

	hr = CoCreateInstance (CLSID_MouseInformation, 0, CLSCTX_INPROC_SERVER,
		IID_IMouseInformation, (void **) &m_pMouseInfo);
	if (FAILED (hr)) {

	hr = CoCreateInstance (CLSID_MemoryInformation, 0, CLSCTX_INPROC_SERVER,
		IID_IMemoryInformation, (void **) &m_pMemoryInfo);
	if (FAILED (hr)) {
	m_pOSInfo->AddRef ();
	m_pMouseInfo->AddRef ();

FinalConstruct is called before the outer object completes its construction. Don't forget to call AddRef for each constructed object. Since we have constructed and AddRef'd the inner interfaces, we need some place where we can release all of these. ATL provides FinalRelease call in CcomObjectRootEx which gets called on outer object before its release and unloading. The outer object will have to provide its implementation. In the aplication the implementation looks like this.

CSystemInformation::FinalRelease ()
	if (m_pOSInfo != NULL) {
		m_pOSInfo->Release ();

	if (m_pMouseInfo != NULL) {
		m_pMouseInfo->Release ();

	if (m_pMemoryInfo != NULL) {
		m_pMemoryInfo->Release ();

As you can see that Release being called on each inner interface to counter each AddRef for these.

Client Application:

I hace created an MFC project to for implementation of the application. The application has been created as Dialog based application with Automation support. The dialog box encapsulates a property sheet that contains 8 property pages for each inner COM object of the ISystemInfo interface. This interface pointer gets created when the application is launched. It is being done in OnInitDialog function of CsystemApplicationDlg class. It calls CreateInterfacePointer function. The implementation of this function looks like this.

void CSystemApplicationDlg::CreateInterfacePointer ()

	hr = CoCreateInstance (CLSID_SystemInformation, NULL, CLSCTX_INPROC, IID_ISystemInformation,
		(void **) &m_pSystemInfo);
	if (FAILED (hr) || m_pSystemInfo == NULL) {
		AfxMessageBox (_T ("Application Failed to create SystemInformation Intrface"),

	m_pSystemInfo->AddRef ();

After successful creation of interface pointer we need to call AddRef on it to maintain reference count. And before the application terminates we will have to call Release on this interface. This has been done in the dialog box destructor.

	// If there is an automation proxy for this dialog, set
	//  its back pointer to this dialog to NULL, so it knows
	//  the dialog has been deleted.
	if (m_pAutoProxy != NULL)
		m_pAutoProxy->m_pDialog = NULL;

	// Release the interface pointer.

	if (m_pSystemInfo != NULL) {
		m_pSystemInfo->Release ();
		m_pSystemInfo = NULL;

The final outcome of the application looks like:

Demo Project

The first page is showing information about CPU of my system.

Project Compilation:

Although I have set all the depencies for the attached application code, but here is what it should like. Some of the projects need some special lib files to be included.

Project dependcies:


Lib inclusions:

winmm.lib - SystemMultiMedia

Header Includes:

#include <winioctl.h> - 

#include <mmsystem.h>
#include <mmreg.h>

Project has been compiled with VC++5 (SP 3) on WinNT 4.0 (SP 3). Some of the API calls used are not available on Win95.

Download source - 158 KB


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Entire organizations suffer when their networks can't keep up and new opportunities are put on hold. Waiting on service providers isn't good business. In these examples, learn how to simplify network management so that your organization can better manage costs, adapt quickly to business demands, and seize market opportunities when they arise.

  • Enterprises today must focus on digital transformation to remain competitive or disrupt their industries. The foundation for successful transformation is the adoption of a cloud-first mindset. However, IT organizations must first address legacy infrastructure and fragmented management tools that were not designed for the speed and flexibility of the cloud and digital era. Read this IDC Technology Spotlight paper to explore: Why digital transformation is driving a shift to a cloud-centric enterprise Key …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date