Visual C++ ADO 编程

ADO API 参考使用类似于 Microsoft Visual Basic 的语法描述 ADO 应用程序编程接口(API)的功能。 尽管预期受众是所有用户,但 ADO 程序员采用各种语言,如 Visual Basic、Visual C++(带有和不使用 #import 指令),以及 Visual J++ (使用 ADO/WFC 类包)。

注意

Microsoft在 2004 年终止了对 Visual J++ 的支持。

为了适应这种多样性,ADO for Visual C++ 语法索引 在 API 参考中提供了特定于视觉C++语言的语法,并提供有关功能、参数、异常行为等常见说明的链接。

ADO 是使用 COM(组件对象模型)接口实现的。 但是,程序员在某些编程语言中使用 COM 比其他编程语言更容易。 例如,几乎所有关于使用 COM 的详细信息对于 Visual Basic 程序员来说都是隐式处理的,而 Visual C++ 程序员必须自己处理这些细节。

以下部分总结了使用 ADO 和 #import 指令的 C 和 C++ 程序员的详细信息。 它侧重于特定于 COM 的数据类型(VariantBSTRSafeArray),以及错误处理(_com_error)。

使用 #import 编译器指令

#import Visual C++ 编译器指令简化了使用 ADO 方法和属性。 该指令采用包含类型库的文件的名称,例如 ADO .dll(Msado15.dll),并生成包含 typedef 声明、接口智能指针和枚举常量的头文件。 每个接口都被封装或包裹在一个类里。

对于类(即方法或属性调用)中的每个操作,都有一个声明来直接调用操作(即操作的“原始”形式),以及调用原始操作的声明,并在操作未能成功执行时引发 COM 错误。 如果操作是一个属性,则通常有一个编译器指令,该指令为具有 Visual Basic 等语法的操作创建替代语法。

检索属性值的操作的名称形式为 GetProperty。 设置属性值的操作其命名形式为 PutProperty。 设置属性值的操作,使用指向 ADO 对象的指针,其名称格式为 PutRefProperty

可以通过如下形式的调用来获取或设置属性:

variable = objectPtr->GetProperty(); // get property value   
objectPtr->PutProperty(value);       // set property value  
objectPtr->PutRefProperty(&value);   // set property with object pointer  

使用属性指令

__declspec(property...) 编译器指令是Microsoft特定的 C 语言扩展,用于声明用作属性的函数具有替代语法。 因此,可以设置或获取类似于 Visual Basic 的属性的值。 例如,可以通过这种方式设置和获取属性:

objectPtr->property = value;        // set property value  
variable = objectPtr->property;     // get property value  

请注意,无需编写代码:

objectPtr->PutProperty(value);      // set property value  
variable = objectPtr->GetProperty;  // get property value  

编译器将根据声明的替代语法以及属性是读取还是写入,生成适当的 Get-Put- 或 PutRef属性 调用。

__declspec(property...) 编译器指令只能声明 取值设值,或 取值设值 的函数替代语法。 只读操作仅具有 读取 声明;仅写入操作只有 写入 声明;同时读写操作具有 读取写入 声明。

此指令只能使用两个声明;但是,每个属性可能具有三个属性函数:Get属性Put属性PutRef属性。 在这种情况下,只有两种形式的属性具有替代语法。

例如,Command 对象 ActiveConnection 属性的声明使用了一种替代语法,适用于 GetActiveConnectionPutRefActiveConnectionPutRef- 语法是一个不错的选择,因为实际上,你通常需要在此属性中放置一个开放 Connection 对象(即 Connection 对象指针)。 另一方面,Recordset 对象具有 Get-、 Put- 和 PutRefActiveConnection 操作,但没有替代语法。

集合、GetItem 方法和 Item 属性

ADO 定义了多个集合,包括 字段参数属性错误。 在 Visual C++ 中,GetItem(索引 方法返回集合的成员。 索引Variant,其值为集合中成员的数字索引,或者是包含成员名称的字符串。

__declspec(property...) 编译器指令将 Item 属性声明为每个集合的基本 GetItem() 方法的替代语法。 替代语法使用方括号,看起来类似于数组引用。 一般情况下,这两种形式如下所示:

  
      collectionPtr->GetItem(index);  
collectionPtr->Item[index];  

例如,将值分配给 Recordset 对象的字段,该对象名为 rs,派生自 pubs 数据库的 作者 表。 使用 Item() 属性访问 Recordset 对象 字段 集合的第三个 字段(集合从零开始编制索引;假定第三个字段命名为 au_fname)。 然后,对 Field 对象调用 Value() 方法以分配字符串值。

这可以通过以下四种方式在 Visual Basic 中表示(最后两种形式对 Visual Basic 是唯一的;其他语言没有等效项):

rs.Fields.Item(2).Value = "value"  
rs.Fields.Item("au_fname").Value = "value"  
rs(2) = "value"  
rs!au_fname = "value"  

在 Visual C++中等效于上述前两种形式:

rs->Fields->GetItem(long(2))->PutValue("value");   
rs->Fields->GetItem("au_fname")->PutValue("value");  

- 或 - (还显示了 Value 属性的替代语法)

rs->Fields->Item[long(2)]->Value = "value";  
rs->Fields->Item["au_fname"]->Value = "value";  

有关循环访问集合的示例,请参阅“ADO 引用”的“ADO 集合”部分。

COM-Specific 数据类型

通常,在 ADO API 参考中找到的任何 Visual Basic 数据类型都具有等效的 Visual C++。 其中包括 Visual Basic 的 无符号字符 用于 ByteShort 用于 Integer,以及 Long 用于 Long。 在语法索引中查看给定方法或属性的操作数所需的确切内容。

此规则的例外情况是特定于 COM 的数据类型:VariantBSTR,以及 SafeArray

变体

Variant 是包含值成员和数据类型成员的结构化数据类型。 Variant 可能包含多种其他数据类型,包括另一个 Variant、BSTR、布尔值、IDispatch 或 IUnknown 指针、货币、日期等。 COM 还提供使一种数据类型轻松转换为另一种数据类型的方法。

_variant_t 类封装和管理 Variant 数据类型。

当 ADO API 引用表示方法或属性操作数采用值时,它通常意味着该值在 _variant_t中传递。

当 ADO API 参考主题中的 参数 部分表示操作数是 变量时,此规则是显式的。 一个例外是文档明确表示操作数采用标准数据类型,例如 LongByte或枚举。 另一个例外是操作数采用 字符串

BSTR

BSTRBasic STRing)是包含字符串和字符串长度的结构化数据类型。 COM 提供了用于分配、操作和释放 BSTR的方法。

_bstr_t 类封装和管理 BSTR 数据类型。

当 ADO API 引用表示方法或属性采用 字符串 值时,这意味着该值采用 _bstr_t的形式。

强制转换_variant_t和_bstr_t类

通常不需要在操作的参数中显式编码 _variant_t_bstr_t。 如果 _variant_t_bstr_t 类具有与参数数据类型匹配的构造函数,编译器将生成相应的 _variant_t_bstr_t

但是,如果参数不明确,即参数的数据类型与多个构造函数匹配,则必须使用适当的数据类型强制转换参数以调用正确的构造函数。

例如,Recordset::Open 方法的声明为:

    HRESULT Open (  
        const _variant_t & Source,  
        const _variant_t & ActiveConnection,  
        enum CursorTypeEnum CursorType,  
        enum LockTypeEnum LockType,  
        long Options );  

ActiveConnection 参数接受一个 _variant_t的引用,你可以将其设置为连接字符串或指向已打开的 Connection 对象的指针。

如果传递字符串(如“DSN=pubs;uid=MyUserName;pwd=<password>;”)或指针(如“(IDispatch *) pConn”),则会隐式构造正确的 _variant_t

注意

如果要连接到支持 Windows 身份验证的数据源提供程序,则应指定 Trusted_Connection=yesIntegrated Security = SSPI,而不是连接字符串中的用户 ID 和密码信息。

或者,您可以明确编码一个包含指针的 _variant_t,例如“_variant_t((IDispatch *) pConn, true)”。 转换((IDispatch *))使用另一个构造函数解析歧义,该构造函数采用指向 IUnknown 接口的指针。

不常提及但非常重要的一个事实是,ADO 是一个 IDispatch 接口。 每当需要将指向 ADO 对象的指针作为 Variant传递时,必须将该指针转换为指向 IDispatch 接口的指针。

最后一个事例显式编码构造函数的第二个布尔参数,其可选默认值为 true。 此参数导致 Variant 构造函数调用其 AddRef() 方法,该方法补偿 ADO 在 ADO 方法或属性调用完成时自动调用 _variant_t::Release() 方法。

SafeArray

SafeArray 是包含其他数据类型数组的结构化数据类型。 SafeArray 称为 安全,因为它包含有关每个数组维度边界的信息,并限制对这些边界内的数组元素的访问。

当 ADO API 引用显示方法或属性采用或返回数组时,这意味着该方法或属性采用或返回 SafeArray,而不是本机 C/C++ 数组。

例如,Connection 对象的第二个参数 OpenSchema 方法需要 Variant 值的数组。 这些 Variant 值必须作为 SafeArray的元素传递,并且必须将该 SafeArray 设置为另一个 Variant的值。 这是作为 OpenSchema 的第二个参数传递的其他 Variant

例如,Find 方法的第一个参数是 variant 其值为一维 SafeArray;AddNew 的每个可选第一个和第二个参数都是一维 SafeArray;和 GetRows 方法的返回值是 Variant,其值为二维 SafeArray

缺失参数与默认参数

Visual Basic 允许方法中缺少参数。 例如,Recordset 对象 Open 方法有五个参数,但你可以跳过中间参数并离开尾随参数。 将替换默认 BSTRVariant,具体取决于缺少的操作数的数据类型。

在 C/C++ 中,必须指定所有操作数。 如果要指定其数据类型为字符串的缺失参数,请指定包含 null 字符串的 _bstr_t。 如果要指定一个缺失参数,而其数据类型是 Variant,那么应指定一个具有DISP_E_PARAMNOTFOUND值和VT_ERROR类型的 _variant_t。 或者,指定等效的 _variant_t 常量,vtMissing#import 指令提供。

三种方法是 vtMissing的典型用法的例外情况。 这些是 ConnectionCommand 对象的 Execute 方法,以及 Recordset 对象的 NextRecordset 方法。 以下是其签名:

_RecordsetPtr <A HREF="mdmthcnnexecute.htm">Execute</A>( _bstr_t CommandText, VARIANT * RecordsAffected,   
        long Options );  // Connection  
_RecordsetPtr <A HREF="mdmthcmdexecute.htm">Execute</A>( VARIANT * RecordsAffected, VARIANT * Parameters,   
        long Options );  // Command  
_RecordsetPtr <A HREF="mdmthnextrec.htm">NextRecordset</A>( VARIANT * RecordsAffected );  // Recordset  

参数(RecordsAffectedParameters)是指向 Variant的指针。 Parameters 是一个输入参数,它指定包含单个参数或参数数组的 Variant 的地址,该参数将修改所执行的命令。 RecordsAffected 是一个输出参数,它指定 Variant的地址,其中返回受该方法影响的行数。

Command 对象 Execute 方法中,通过将 Parameters 设置为 &vtMissing(推荐)或空指针(即,NULL 或零(0)),来指示没有参数被指定。 如果 参数 设置为 null 指针,则该方法将在内部用 vtMissing的等效值替代,然后完成该操作。

在所有方法中,通过将 RecordsAffected 设置为 null 指针,指示不应返回受影响的记录数。 在这种情况下,null 指针与其说是缺少的参数,不如表示该方法应放弃受影响的记录数。

因此,对于这三种方法,编写如下代码是有效的:

pConnection->Execute("commandText", NULL, adCmdText);   
pCommand->Execute(NULL, NULL, adCmdText);  
pRecordset->NextRecordset(NULL);  

错误处理

在 COM 中,大多数操作返回 HRESULT 返回代码,该代码指示函数是否已成功完成。 #import 指令会围绕每个“raw”方法或属性生成包装器代码,并检查返回的 HRESULT。 如果 HRESULT 指示失败,则包装器代码通过调用 _com_issue_errorex() 并将 HRESULT 返回代码作为参数引发 COM 错误。 COM 错误对象可以在 尝试块-捕获块 中被捕获。 (为了提高效率,捕获对 _com_error 对象的引用。

请记住,这些是 ADO 错误:它们是由 ADO 操作失败导致的。 基础提供程序返回的错误在 Connection 对象的 Errors 集合中以 Error 对象形式出现。

#import 指令仅为 ADO .dll中声明的方法和属性创建错误处理例程。 但是,可以通过编写自己的错误检查宏或内联函数来利用这种相同的错误处理机制。 有关示例,请参阅本主题、Visual C++ Extensions或以下部分中的代码。

Visual Basic 约定的 Visual C++ 等效项

以下是 ADO 文档中几个约定的摘要,这些约定在 Visual Basic 中编码,以及在 Visual C++ 中等效约定。

声明 ADO 对象

在 Visual Basic 中,ADO 对象变量(在本例中为 Recordset 对象)声明如下:

Dim rst As ADODB.Recordset  

在注册表中定义的 Recordset 对象的 ProgID 是“ADODB.Recordset”。 Record 对象的新实例声明如下:

Dim rst As New ADODB.Recordset  

-或-

Dim rst As ADODB.Recordset  
Set rst = New ADODB.Recordset  

在 Visual C++ 中,#import 指令为所有 ADO 对象生成智能指针类型声明。 例如,指向 _Recordset 对象的变量的类型为 _RecordsetPtr,声明如下:

_RecordsetPtr  rs;  

指向 _Recordset 对象的新实例的变量声明如下:

_RecordsetPtr  rs("ADODB.Recordset");  

-或-

_RecordsetPtr  rs;  
rs.CreateInstance("ADODB.Recordset");  

-或-

_RecordsetPtr  rs;  
rs.CreateInstance(__uuidof(_Recordset));  

调用 CreateInstance 方法后,可以使用变量,如下所示:

rs->Open(...);  

请注意,在一种情况下,“.”运算符就像变量是类(rs.CreateInstance)的实例一样,在另一种情况下,“->”运算符就像变量是指向接口的指针(rs->Open)。

可以通过两种方式使用一个变量,因为“->”运算符被重载,以允许类的实例的行为类似于指向接口的指针。 实例变量的私有类成员包含指向 _Recordset 接口的指针;“->”运算符返回该指针;返回的指针访问 _Recordset 对象的成员。

为缺少的参数编写代码 - 字符串

如果需要在 Visual Basic 中为缺少的 字符串 操作数编写代码,只需省略该操作数。 必须在 Visual C++ 中指定操作数。 编写一个具有空字符串作为值的 _bstr_t

_bstr_t strMissing(L"");  

为缺少的参数编码 - 变体

在 Visual Basic 中,需要对缺少的 Variant 操作数进行编码时,只需省略操作数。 必须在 Visual C++ 中指定所有操作数。 将缺少的 Variant 参数编码为,使 _variant_t 的值设置为特殊值 DISP_E_PARAMNOTFOUND,并将类型设置为 VT_ERROR。 或者,指定 vtMissing,这是 #import 指令提供的等效预定义常量。

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);   

-或使用 -

...vtMissing...;  

声明变量

在 Visual Basic 中,Variant 使用 Dim 语句声明,如下所示:

Dim VariableName As Variant  

在 Visual C++中,将变量声明为类型 _variant_t。 下面显示了一些示意性的 _variant_t 声明。

注意

这些声明只是为你自己的程序中需要编码的内容提供了一个大致的概念。 有关详细信息,请参阅以下示例和 Visual C++文档。

_variant_t  VariableName(value);  
_variant_t  VariableName((data type cast) value);  
_variant_t  VariableName(value, VT_DATATYPE);  
_variant_t  VariableName(interface * value, bool fAddRef = true);  

使用变体数组

在 Visual Basic 中,可以使用 Dim 语句对 Variants 数组数组进行编码,也可以使用 Array 函数,如以下示例代码所示:

Public Sub ArrayOfVariants  
Dim cn As ADODB.Connection  
Dim rs As ADODB.Recordset  
Dim fld As ADODB.Field  
  
    cn.Open "DSN=pubs"  
    rs = cn.OpenSchema(adSchemaColumns, _  
        Array(Empty, Empty, "authors", Empty))  
    For Each fld in rs.Fields  
        Debug.Print "Name = "; fld.Name  
    Next fld  
    rs.Close  
    cn.Close  
End Sub  

以下 Visual C++ 示例演示如何使用与 _variant_t一起使用的 SafeArray

笔记

以下说明对应于代码示例中的注释部分。

  1. 再次定义 TESTHR() 内联函数,以利用现有的错误处理机制。

  2. 只需要一维数组,因此可以使用 SafeArrayCreateVector,而无须 SAFEARRAYBOUND 通用声明和 SafeArrayCreate 函数。 以下是使用 SafeArrayCreate的代码示例:

       SAFEARRAYBOUND   sabound[1];  
       sabound[0].lLbound = 0;  
       sabound[0].cElements = 4;  
       pSa = SafeArrayCreate(VT_VARIANT, 1, sabound);  
    
  3. 枚举常量(adSchemaColumns)标识的架构与四个约束列相关联:TABLE_CATALOG、TABLE_SCHEMA、TABLE_NAME和COLUMN_NAME。 因此,将创建包含四个元素的 Variant 值的数组。 然后指定与第三列(TABLE_NAME)对应的约束值。

    返回的 Recordset 由多个列组成,其中子集是约束列。 每个返回行的约束列的值必须与相应的约束值相同。

  4. 熟悉 SafeArrays 的人可能会感到惊讶,因为在退出前并没有调用 SafeArrayDestroy()。 事实上,在这种情况下调用 SafeArrayDestroy() 将导致运行时异常。 原因是当 _variant_t 超出范围时,vtCriteria 的析构函数将调用 VariantClear(),这将释放 SafeArray。 在没有手动清除 _variant_t的情况下调用 SafeArrayDestroy,将导致析构函数尝试清除一个无效的 SafeArray 指针。

    如果调用了 SafeArrayDestroy,则代码将如下所示:

          TESTHR(SafeArrayDestroy(pSa));  
       vtCriteria.vt = VT_EMPTY;  
          vtCriteria.parray = NULL;  
    

    但是,让 _variant_t 管理 SafeArray要简单得多。

// Visual_CPP_ADO_Prog_1.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
// Note 1  
inline void TESTHR( HRESULT _hr ) {   
   if FAILED(_hr)   
      _com_issue_error(_hr);   
}  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _RecordsetPtr pRs("ADODB.Recordset");  
      _ConnectionPtr pCn("ADODB.Connection");  
      _variant_t vtTableName("authors"), vtCriteria;  
      long ix[1];  
      SAFEARRAY *pSa = NULL;  
  
      pCn->Provider = "sqloledb";  
      pCn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", adConnectUnspecified);  
      // Note 2, Note 3  
      pSa = SafeArrayCreateVector(VT_VARIANT, 1, 4);  
      if (!pSa)   
         _com_issue_error(E_OUTOFMEMORY);  
  
      // Specify TABLE_NAME in the third array element (index of 2).   
      ix[0] = 2;        
      TESTHR(SafeArrayPutElement(pSa, ix, &vtTableName));  
  
      // There is no Variant constructor for a SafeArray, so manually set the   
      // type (SafeArray of Variant) and value (pointer to a SafeArray).  
  
      vtCriteria.vt = VT_ARRAY | VT_VARIANT;  
      vtCriteria.parray = pSa;  
  
      pRs = pCn->OpenSchema(adSchemaColumns, vtCriteria, vtMissing);  
  
      long limit = pRs->GetFields()->Count;  
      for ( long x = 0 ; x < limit ; x++ )  
         printf( "%d: %s\n", x + 1, ((char*) pRs->GetFields()->Item[x]->Name) );  
      // Note 4  
      pRs->Close();  
      pCn->Close();  
   }  
   catch (_com_error &e) {  
      printf("Error:\n");  
      printf("Code = %08lx\n", e.Error());  
      printf("Code meaning = %s\n", (char*) e.ErrorMessage());  
      printf("Source = %s\n", (char*) e.Source());  
      printf("Description = %s\n", (char*) e.Description());  
   }  
   CoUninitialize();  
}  

使用属性 Get/Put/PutRef

在 Visual Basic 中,属性的名称不因它是被检索、赋值,还是分配引用而有所区别。

Public Sub GetPutPutRef  
Dim rs As New ADODB.Recordset  
Dim cn As New ADODB.Connection  
Dim sz as Integer  
cn.Open "Provider=sqloledb;Data Source=yourserver;" & _  
         "Initial Catalog=pubs;Integrated Security=SSPI;"  
rs.PageSize = 10  
sz = rs.PageSize  
rs.ActiveConnection = cn  
rs.Open "authors",,adOpenStatic  
' ...  
rs.Close  
cn.Close  
End Sub  

此 Visual C++ 示例演示了 Get/Put/PutRef属性

笔记

以下说明对应于代码示例中的注释部分。

  1. 此示例使用缺少字符串参数的两种形式:显式常量 strMissing,以及在 Open 方法的作用域内由编译器用于创建的临时 _bstr_t

  2. 不需要将 rs->PutRefActiveConnection(cn) 的操作数强制转换为 (IDispatch *),因为操作数的类型已经 (IDispatch *)

// Visual_CPP_ado_prog_2.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _bstr_t strMissing(L"");  
      long oldPgSz = 0, newPgSz = 5;  
  
      // Note 1  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", strMissing, "", adConnectUnspecified);  
  
      oldPgSz = rs->GetPageSize();  
      // -or-  
      // oldPgSz = rs->PageSize;  
  
      rs->PutPageSize(newPgSz);  
      // -or-  
      // rs->PageSize = newPgSz;  
  
      // Note 2  
      rs->PutRefActiveConnection( cn );  
      rs->Open("authors", vtMissing, adOpenStatic, adLockReadOnly, adCmdTable);  
      printf("Original pagesize = %d, new pagesize = %d\n", oldPgSz, rs->GetPageSize());  
      rs->Close();  
      cn->Close();  
  
   }  
   catch (_com_error &e) {  
      printf("Description = %s\n", (char*) e.Description());  
   }  
   ::CoUninitialize();  
}  

使用 GetItem(x) 和 Item[x]

此 Visual Basic 示例演示 () 的标准和替代语法。

Public Sub GetItemItem  
Dim rs As New ADODB.Recordset  
Dim name as String  
rs = rs.Open "authors", "DSN=pubs;", adOpenDynamic, _  
         adLockBatchOptimistic, adTable  
name = rs(0)  
' -or-  
name = rs.Fields.Item(0)  
rs(0) = "Test"  
rs.UpdateBatch  
' Restore name  
rs(0) = name  
rs.UpdateBatch  
rs.Close  
End Sub  

此 Visual C++ 示例演示了

注意

以下注释对应于代码示例中的注释节:当使用 Item访问集合时,索引(2)必须强制转换为 ,以便调用适当的构造函数。

// Visual_CPP_ado_prog_3.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
void main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr cn("ADODB.Connection");  
      _RecordsetPtr rs("ADODB.Recordset");  
      _variant_t vtFirstName;  
  
      cn->Provider = "sqloledb";  
      cn->Open("Data Source='(local)';Initial Catalog=pubs;Integrated Security=SSPI;", "", "", adConnectUnspecified);  
  
      rs->PutRefActiveConnection( cn );  
      rs->Open("authors", vtMissing, adOpenStatic, adLockOptimistic, adCmdTable);  
      rs->MoveFirst();  
  
      // Note 1. Get a field.  
      vtFirstName = rs->Fields->GetItem((long)2)->GetValue();  
      // -or-  
      vtFirstName = rs->Fields->Item[(long)2]->Value;  
  
      printf( "First name = '%s'\n", (char*)( (_bstr_t)vtFirstName) );  
  
      rs->Fields->GetItem((long)2)->Value = L"TEST";  
      rs->Update(vtMissing, vtMissing);  
  
      // Restore name  
      rs->Fields->GetItem((long)2)->PutValue(vtFirstName);  
      // -or-  
      rs->Fields->GetItem((long)2)->Value = vtFirstName;  
      rs->Update(vtMissing, vtMissing);  
      rs->Close();  
   }  
   catch (_com_error &e) {  
      printf("Description = '%s'\n", (char*) e.Description());  
   }  
   ::CoUninitialize();  
}  

使用 (IDispatch *) 强制转换 ADO 对象指针

以下 Visual C++ 示例演示如何使用 (IDispatch *) 来强制转换 ADO 对象指针。

笔记

以下说明对应于代码示例中的注释部分。

  1. 在显式编码的 Variant中指定打开的 Connection 对象。 使用 (IDispatch *) 对其进行强制转换,以便调用正确的构造函数。 此外,显式将第二个 _variant_t 参数设置为 true的默认值,因此当 Recordset::Open 操作结束时,对象引用计数将正确。

  2. 表达式 (_bstr_t)不是强制转换,而是一个 _variant_t 运算符,该运算符从 Value返回的 Variant 中提取 _bstr_t 字符串。

表达式 (char*)不是强制转换,而是 _bstr_t 运算符,用于从 _bstr_t 对象中提取封装字符串的指针。

此代码部分演示 _variant_t_bstr_t 运算符的一些有用行为。

// Visual_CPP_ado_prog_4.cpp  
// compile with: /EHsc  
#import "msado15.dll" no_namespace rename("EOF", "EndOfFile")  
  
int main() {  
   CoInitialize(NULL);  
   try {  
      _ConnectionPtr pConn("ADODB.Connection");  
      _RecordsetPtr pRst("ADODB.Recordset");  
  
      pConn->Provider = "sqloledb";  
      pConn->Open("Data Source='(local)';Initial Catalog='pubs';Integrated Security=SSPI", "", "", adConnectUnspecified);  
  
      // Note 1.  
      pRst->Open("authors", _variant_t((IDispatch *) pConn, true), adOpenStatic, adLockReadOnly, adCmdTable);  
      pRst->MoveLast();  
  
      // Note 2.  
      printf("Last name is '%s %s'\n",   
         (char*) ((_bstr_t) pRst->GetFields()->GetItem("au_fname")->GetValue()),  
         (char*) ((_bstr_t) pRst->Fields->Item["au_lname"]->Value));  
  
      pRst->Close();  
      pConn->Close();  
   }  
   catch (_com_error &e) {  
      printf("Description = '%s'\n", (char*) e.Description());  
   }     
   ::CoUninitialize();  
}