Using Native COM Support in C++

Topic Last Modified: 2006-06-11

If you use Microsoft® Visual C++® 5.0 or later, you can take advantage of its native Component Object Model (COM) support through the #import statement. Using this directive allows you to automatically "import" the type information contained in the component type library to your project. Usually, the files generated have the same name as the component dynamic-link library (DLL) with the extensions .tlh and .tli. Microsoft® ActiveX® Data Objects (ADO) type information is required, so you need to include it as well. You can also use the #import directive to import this information. The following examples illustrate the use of Collaboration Data Objects (CDO):

#import "c:\program files\common files\system\ado\msado15.dll" no_namespace raw_interfaces_only
#import "c:\program files\common files\Microsoft Shared\CDO\cdoex.dll" no_namespace raw_interfaces_only
#import "c:\program files\exchsrvr\bin\cdowf.dll" no_namespace raw_interfaces_only
#import "c:\program files\exchsrvr\bin\cdoexm.dll"   no_namespace raw_interfaces_only

#include "cdoexstr.h"
#include "cdoexerr.h"

void main() {
  CoInitialize(NULL);
  IMessage* pMsg = NULL;
  HRESULT hr =
     CoCreateInstance( __uuidof(Message),
                      NULL,
                      CLSCTX_INPROC_SERVER,
                      __uuidof(IMessage),
                     (void**) &pMsg
                     );
  ...
  CoUninitialize();
}

Note

The #import statement defaults to using namespaces if the no_namespace option is not used or if you do not "import" the namespace using the "using" directive in C++. You must have the ADO 2.5 type information in the global namespace or in the CDO namespace; CDO interfaces make use of ADO types in arguments and they are not qualified by using namespaces. If these types are not in the global namespace or in the CDO namespace, many argument types in methods and properties are undefined and cause a compilation error.

The globally unique identifier (GUID) definitions for ADO and CDO types can be retrieved using the __uuidof operator, which is supported by Visual C++. If you want to, you can use #import flags to specify the CLSID_XXX and IID_XXX definitions.

Consider the following issues when using the #import statement:

  • The #import statement does not include in the header file it generates the constant definitions contained in modules in the type library. CDO provides two separate headers that can be included in your project so that these constants are available. These headers are cdoexstr.h and cdoexerr.h. The first contains the field name constants, and the second includes the CDO custom HRESULT error codes. If you do not include these headers, manually insert these values. For example, the messaging field urn:schemas:mailheader:to has an associated constant called cdoTo in the string constant header file.
  • The #import statement strips the default values for arguments when generating the header files. These defaults can be useful when performing routine operations. If you use #import, explicitly specify the defaults. For example:
//IBodyPart* pBp;
hr = pBp->AddBodyPart();
//  This works if you have cdosys.h or cdoex.h.
//  The C++ definition for AddBodyPart
//  is HRESULT AddBodyPart( long index = -1 );
//

//  Below, assume #import was used.
hr = pBp->AddBodyPart(-1);
//  You need to specify the default here if you used #import.
//  The definition does not contain the default -1.

Using Interface Pointer Wrapper Classes with Import

The raw_interfaces_only flag to #import directive suppresses the creation of "smart pointer" C++ wrapper classes. In many cases, however, these wrappers are quite useful and make working with CDO in C++ very simple. The following example demonstrates how to use the wrapper classes produced by the #import statement:

#import "c:\program files\common files\system\ado\msado15.dll" no_namespace
#import "c:\program files\common files\Microsoft Shared\CDO\cdoex.dll" no_namespace

#include "cdoexstr.h"
#include "cdoexerr.h"

void main() {
  CoInitialize(NULL);
  {
  try {
   IMessagePtr iMsg(__uuidof(Message));
   FieldsPtr Flds;
   Flds = iMsg->Fields;
   Flds->Item[cdoTo]->Value      = _variant_t("someone@example.com");
   Flds->Item[cdoFrom]->Value    = _variant_t("someone@example.com");
   Flds->Item[cdoSubject]->Value = _variant_t("a test");
   Flds->Item[cdoTextDescription]->Value = _variant_t("this is a test");
   Flds->Update();
   iMsg->Send();
  }
  catch( _com_error err) {
   //  ...
 }

  }
  CoUninitialize();
  return 1;
}

Using the CDO Namespace

You can use namespaces with the #import statement. However, when you import the ADO type information, you must make sure to add it to the CDO  namespace or suppress the namespace by adding it to the global namespace. If you do not do this, all ADO type information will reside in the ADODB namespace by default, and CDO arguments that are ADO types will not resolve when you compile. The following example demonstrates this:

#import "c:\program files\common files\system\ado\msado15.dll" rename("ADODB","CDO")
#import "c:\program files\common files\microsoft shared\cdo\cdoex.dll"
#import "c:\program files\exchsrvr\bin\cdowf.dll"
#import "c:\program files\exchsrvr\bin\cdoexm.dll"

#include "cdoexstr.h"
#include "cdoexerr.h"

void main() {
  CoInitialize(NULL);
  {
  try {
   CDO::IMessagePtr iMsg(__uuidof(CDO::Message));
   CDO::FieldsPtr Flds;
   Flds = iMsg->Fields;
   Flds->Item[cdoTo]->Value      = _variant_t("someone@example.com");
   Flds->Item[cdoFrom]->Value    = _variant_t("someone@example.com");
   Flds->Item[cdoSubject]->Value = _variant_t("a test");
   Flds->Item[cdoTextDescription]->Value = _variant_t("this is a test");
   Flds->Update();
   iMsg->Send();
  }
  catch( _com_error err) {
   //  ...
  }
  try {
    CDOEXM::IMailRecipientPtr pMbxRecip(__uuidof(CDOEXM::Mailbox));
    CDOEXM::IMailboxPtr pMbox;
    pMbox = pMbxRecip;
    // ...
  }
catch( _com_error err) {
  //...
}

  }
  CoUninitialize();
  return 1;
}