You are currently offline, waiting for your internet to reconnect

INFO: Using ActiveX Data Objects (ADO) via #import in VC++

This article was previously published under Q169496
The #import directive in Visual C++ offers a powerful new mechanism formanipulating OLE servers. When used with ActiveX Data Objects (ADO),#import can simplify getting at your data. This article discusses what isnecessary to take advantage of #import with ADO.

Before You Instantiate Any Classes Created by #import

It's important to initialize OLE before creating any instances of classescreated by #import. For example, the following code is safe, as it declares a #import smart pointer, initializes OLE, and then instantiates the smartpointer:

   // Declare smart pointer of Recordset    _RecordsetPtr     p;   void main( void )   {      // Initialize OLE.      ::CoInitialize(NULL);      // Instantiate smart pointer.      HRESULT hr = p.CreateInstance( __uuidof( Recordset ) );      ...   }				
The next code sample, however, is not safe and generates an unhandledexception. The global smart pointer p is both declared and instantiated (byvirtue of passing a specific uuid in the constructor):

// Declare & instantiate smart pointer of Recordset    _RecordsetPtr     p( __uuidof( _Recordset ) );   void main( void )   {      // Initialize OLE      ::CoInitialize(NULL);      ...   }				
Because p is a global variable, it is instantiated before CoInitialize isever called in main(). You can correct this with the following code snippet :
struct InitOle {      InitOle()  { ::CoInitialize(NULL); }      ~InitOle() { ::CoUninitialize();   }    } _init_InitOle_;   // Declare & instantiate smart pointer of Recordset    _RecordsetPtr     p( __uuidof( _Recordset ) );   ...				
An instance of the struct InitOle is declared, and instantiated before p,and, therefore, initializes OLE in it's constructor. Without this kind offail-safe, you will see the following error message:
Unhandled exception in [Program] (KERNEL32.DLL): 0xE06D7363
Microsoft C++ Exception.

Correct Implementation of #import

It is important to invoke ADO correctly in your program, or you can havecompiler errors. The following code demonstrates the correct way to use #import with Msado10.dll the MSADO15.dll:

   #import <msado15.dll>            \    no_namespace                     \   rename( "EOF", "adoEOF" )				

Error Handling

With ADO, you may get an error in the HRESULT returned from an ADO method,you may get an exception raised by #import generated classes, and foreither condition the ADO Errors Collection may be populated. In order toget at the Errors Collection you need a valid connection object. For more information please see the following article in the Microsoft Knowledge Base:

169498 INFO: Extracting Error Information from ADO in VC++ with #import

ADO and Dbdaoint.h

Attempts to mix ADO (through #import) and either MFC DAO or the DAO SDK in the same implementation file, as follows:

  #include <afxdao.h>  // MFC DAO   // -or-  #include <dbdao.h>   // DAO SDK  #import <msado15.dll> no_namespace ...				

Generates the following six errors:

error C2011: 'EditModeEnum' : 'enum' type redefinition
error C2011: 'LockTypeEnum' : 'enum' type redefinition
error C2011: 'FieldAttributeEnum' : 'enum' type redefinition
error C2011: 'DataTypeEnum' : 'enum' type redefinition
error C2011: 'ParameterDirectionEnum' : 'enum' type redefinition
error C2011: 'RecordStatusEnum' : 'enum' type redefinition
While very nearly identical in content, the actual values in eachenumerated type differ between what is required by ADO and what is requiredby DAO. You have several options to work around this:

  • Separate ADO and DAO code into separate .cpp files. Keep the use of#import or #include <afxdao.h/dbdao.h> in separate implementationfiles as well.
  • Modify the #import statement to create a namespace for anythinggenerated for ADO. This means you will have to reference thenamespace when referencing an ADO object as shown in the two functionsbelow. The first shows how to use ADO exclusively within a function.The second shows how to mix-and-match ADO and DAO objects. This ispossible only by explicitly referencing the ADO namespace for any ADOclass or enumerated type:

       #include <afxdao.h>      #import <msado15.dll>                            \               rename_namespace("AdoNS") rename( "EOF", "adoEOF" )      void ADOOnly( void )      {          using namespace AdoNS;          _RecordsetPtr   prs;          // Generates Compile Errors:          CDaoRecordset   rs;      }      void MixAdoAndDao( void )      {          AdoNS::_RecordsetPtr  prs;          // Compiles just fine          CDaoRecordset   drs;      }						
Dissecting and using Msado105.tlh/Msado15.tli --------------------------------------------

#import generates two files, Msado105.tlh and Msado15.tli off of the typelib contained within Msado15.dll. The structure of the .tlh file can be broken out as follows:
  • Forward References and Typedefs
  • Smart Pointer Typedef and Declarations
  • Type Library Items
Each is described in detail below.

Forward References and Typedefs

Forward References and Typedefs are created through the use of struct__declspec(uuid("...")) on the GUID for any Dual Interface, Interface, andCoClass defined in the typelib.

   ...   struct __declspec(uuid("00000274-0000-0010-8000-00aa006d2ea4"))   /* dual interface */ _Connection;   ...   struct __declspec(uuid("00000275-0000-0010-8000-00aa006d2ea4"))   /* interface */ ICADOConnection;   ...   struct /* coclass */ Connection;   ...				
Not all interfaces, such as Connection, have multiple implementations.This depends on the typelib, but for ADO most interfaces are dual and notimplemented as interface or coclass.

Smart Pointer TypeDef Declarations

For Interfaces and Dual Interfaces, smart pointers are declared, whichgreatly simplifies using the interface:
   ...   _COM_SMARTPTR_TYPEDEF(_Connection, __uuidof(_Connection));   ...   _COM_SMARTPTR_TYPEDEF(ICADOConnection, __uuidof(ICADOConnection));   ...				
Note that no smart pointer was declared for the coclass Connectioninterface.

Type Library Items

This includes any enumerated types defined in the typelib, as wellimplementation of the smart pointers and typelib items:

enum CursorTypeEnum   {      adOpenUnspecified = -1,      adOpenForwardOnly = 0,      adOpenKeyset = 1,      adOpenDynamic = 2,      adOpenStatic = 3   };   ...   struct __declspec(uuid("00000274-0000-0010-8000-00aa006d2ea4"))   _Connection : _ADO   {      //       // Property data.      //       _declspec(property(get=GetConnectionString,                         put=PutConnectionString))      _bstr_t ConnectionString;      ...      //       // Wrapper methods for error-handling.      //       _bstr_t GetConnectionString ( );      void PutConnectionString (          _bstr_t pbstr );      ...      //       // Raw methods provided by interface.      //       virtual HRESULT __stdcall get_ConnectionString (          BSTR * pbstr ) = 0;      virtual HRESULT __stdcall put_ConnectionString (          BSTR pbstr ) = 0;      ...   };				
In the preceding code fragment, the Property Data section uses declspec todeclare get and put methods for ConnectionString. The Wrapper methodssection provides methods created by #import, which wrap these methods, andraise an _com_error exception if they are not successful. The RawMethods section declares the actual method that is invoked by theinterface.

While you could call GetConnectionString or PutConnectionString, it isreally unnecessary. Since ConnectionString is a property you wouldreference it as follows:

   bstrConnect = SysAllocString( L"DSN=AdoDemo;UID=admin;PWD=sa" );   p->ConnectionString = bstrConnect;				

The actual implementation of GetConnectionString/PutConnectionString can befound in the Msado15.tli file.

When it comes time to use the Connection object in your code, you would usean instance of the smart pointer for the dual interface defined inMsado15.tlh as follows:

   _ConnectionPtr p;   bstrConnect   HRESULT           hr = S_OK;   _ConnectionPtr    pConn;   hr = pConn.CreateInstance( __uuidof( Connection ) );      if( !FAILED( hr ) )         hr = pConn->Open( L"pubs", L"sa", L"" );				

Where pubs is an ODBC data source.

#import and Explicitly Calling Release()

The advantage of #import is that it takes care of AddRef, QueryInterface,and Release for you automatically. However, if you decide to start callingRelease() explicitly, you can create problems for yourself.

Within _com_ptr_t is a member variable, m_pInterface. As #import is a verythin wrapper, it makes no distinction with m_pInterface after the object isactually released, versus just decrementing its reference count withoutactually destroying the object. By explicitly calling Release()--withoutvery explicitly calling AddRef() to balance it--#import will gladly try torelease an object that doesn't exist, creating interesting side effects andcrashing behavior.

Best advice, you did not AddRef() it (or at least no need to), do not release it either.
  • Inside Com by Dale Rogerson ISBN 1-57231-349-8
  • The OLE-COM Object Viewer (Oleview.exe) that ships with Visual C++ for examining the contents of a typelib.
  • Visual C++ online documentation: search on #import
For additional information, click the article numbers below to view the articles in the Microsoft Knowledge Base:
182389 FILE: Adovcbm.exe ADO 1.5 with #import and Getrows/Bookmarks
184968 FILE: Adovcsp.exe Demonstrates Using Stored Procedures with ADO
186387 SAMPLE: Ado2atl.exe Returns ADO Interfaces from COM
181733 FILE: Adovcbtd.exe #import Using UpdateBatch and CancelBatch
166112 PRB: Conflict with EOF when using #import with ADO
168354 INFO: Underlying OLE and OLEDB Provider Errors Are Exposed Through ADO

Article ID: 169496 - Last Review: 03/02/2005 18:05:27 - Revision: 3.2

  • Microsoft ActiveX Data Objects 1.0
  • Microsoft ActiveX Data Objects 1.5
  • Microsoft ActiveX Data Objects 2.0
  • Microsoft ActiveX Data Objects 2.1
  • Microsoft ActiveX Data Objects 2.5
  • Microsoft ActiveX Data Objects 2.6
  • Microsoft Data Access Components 2.7
  • kbcode kbdatabase kbinfo kbusage KB169496