Visual C++ ADO 编程

适用于:Access 2013、Office 2013

《ADO API 参考》采用类似 Microsoft Visual Basic 的语法来描述 ADO 应用程序编程接口 (API) 的功能。 虽然其目标读者为所有用户,但 ADO 程序员使用的是多种不同的语言,如 Visual Basic、Visual C++(带或不带 #import 指令)和 Visual J++(带 ADO/WFC 类包)。

为照顾这种多样性,ADO for Visual C++ 语法索引中提供了 Visual C++ 语言的专用语法,以及指向《API 参考》中功能、参数、异常行为等内容的一般性说明的链接。

ADO 采用 COM(组件对象模型)接口来实现。 不过,对于程序员而言,在某些编程语言中使用 COM 要比在其他一些语言中更为容易。 例如,在 Visual Basic 中,几乎所有使用 COM 的细节均已隐式处理,而在 Visual C++ 中,程序员必须留意这些细节。

以下部分概述了 C 和 C++ 程序员使用 ADO 和 #import 指令的详细信息。 其中重点介绍了 COM 特有的数据类型(Variant、BSTR 和 SafeArray)和错误处理 (_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- 或 PutRefProperty 调用。

__declspec(property...) 编译器指令只能为函数声明 get、put 或 get 和 put 替代语法。 只读操作只能具有 get 声明;只写操作只能具有 put 声明;读写操作可以具有 get 和 put 声明。

此指令只能有两个声明;不过,每个属性可以有三个属性函数:GetProperty、PutProperty 和 PutRefProperty。 因此,只有两个属性具有替代语法。

例如,Command 对象的 ActiveConnection 属性可以用 GetActiveConnection 和 PutRefActiveConnection 的替代语法声明。 因为在实践中通常希望将打开的 Connection 对象(即 Connection 对象指针)放在此属性中,因此 PutRef- 语法是较好的选择。 另一方面,Recordset 对象具有 Get-、Put- 和 PutRefActiveConnection 操作,但没有替代语法。

集合、GetItem 方法和 Item 属性

ADO 定义了多个集合,包括 Fields、Parameters、Properties 和 Errors。 在 Visual C++ 中, GetItem (index) 方法返回集合的一个成员。 index 为 Variant,其值可以是集合中成员的数字索引,也可以是包含成员名称的字符串。

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

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

例如,向派生自 pubs 数据库的作者表的 Recordset 对象的字段赋值,名为 rs。 使用 Item () 属性可访问 Recordset 对象 Fields 集合的第三个 Field, (集合从零编制索引;假设第三个字段名为 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"; 

特定于 COM 的数据类型

一般而言,在《ADO API 参考》中找到的任何 Visual Basic 数据类型在 Visual C++ 中都有对应的类型。 其中包括标准的数据类型,如无符号字符型对应于 Visual Basic 的 Byte,短型对应于整型以及长整型对应于长整型。 请查阅“语法索引”,了解特定方法或属性具体需要什么类型的操作数。

此规则的例外之处是 COM 特有的数据类型:Variant、BSTR 和 SafeArray。

Variant

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

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

当《ADO API 参考》中提到方法或属性操作数采用值时,通常表示值在 _variant_t 中传递。

This rule is explicitly true when the Parameters section in the topics of the ADO API Reference says an operand is a Variant. 一个例外是:当文档明确指出操作数采用标准数据类型,如 Long、Byte 或枚举型时。 另一个例外是:当操作数采用 String 时。

BSTR

BSTR(基本字符串)是一种结构化数据类型,其中包含字符串和字符串的长度。 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”等字符串,则将隐式构造正确的 _variant_t uid=sa;pwd=;“或指针,例如” (IDispatch *) pConn”。

或者,可以显式编码包含指针的 _variant_t ,例如“_variant_t ( (IDispatch *) pConn,true) ”。 其中的转换 (IDispatch *) 用另一个构造函数来解析多义性,该构造函数采用指向 IUnknown 接口的指针。

ADO 是 IDispatch 接口这一事实虽然很少提及,但这一点极为重要。 指向 ADO 对象的指针都必须作为 Variant 传递,且该指针必须转换为指向 IDispatch 接口的指针。

最后一种情况是,显式地编写构造函数的第二个布尔型参数,其可选的默认值为 true。 此参数使 Variant 构造函数调用其 AddRef() 方法,当 ADO 方法或属性调用完成后,该方法自动补偿调用 _variant_t::Release() 方法的 ADO。

SafeArray

SafeArray 是一种结构化数据类型,其中包含其他数据类型的数组。 SafeArray 之所以被称为是“安全的”,是由于它包含每个数组维度的界限信息,并限制对这些界限内数组元素的访问。

当《ADO API 参考》中提到方法或属性获取或返回数组时,表示方法或属性获取或返回的是 SafeArray,而不是本机 C/C++ 数组。

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

更进一步,例如, Find 方法的第一个参数是值为一维 SafeArray 的 变量型 ; AddNew 方法的可选的第一个和第二个参数为一维 SafeArray ; GetRows 方法的返回值是值为二维 SafeArray 的 变量型 。

缺少和默认参数

Visual Basic 允许方法中省略参数。 例如,Recordset 对象 Open 方法具有五个参数,但您可以跳过中间的参数,只留下后面的参数。 根据省略的操作数数据类型的不同,将替换为默认的 BSTR 或 变量型 。

在 C/C++ 中,必须指定所有操作数。 如果要指定的省略参数的数据类型为字符串,请指定包含空字符串的 _bstr_t。 如果要指定的省略参数的数据类型为变量型,请用 DISP_E_PARAMNOTFOUND 值和 VT_ERROR 类型指定 _variant_t。 或者,也可以指定等效的 _variant_t 常量 vtMissing ,该常量由 #import 指令提供。

vtMissing 的典型用法有三种方法例外。 这些是 Connection 和 Command 对象的 Execute 方法,以及 Recordset 对象的 NextRecordset 方法。 下面是它们的签名:

 
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthcnnexecute_HV10294345.xml( _bstr_t CommandText, VARIANT * RecordsAffected, 
 long Options ); // Connection 
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthcmdexecute_HV10294344.xml( VARIANT * RecordsAffected, VARIANT * Parameters, 
 long Options ); // Command 
_RecordsetPtr Invalid DDUE based on source, error:link not allowed in code, link filename:mdmthnextrec_HV10294541.xml( VARIANT * RecordsAffected ); // Recordset 

其中参数 RecordsAffected 和 Parameters 是指向变量型的指针。 Parameters 是输入参数,用于指定包含单个参数或参数数组的变量型的地址,它将修改要执行的命令。 RecordsAffected 是输出参数,用于指定变量型的地址,其中返回了受方法影响的行的数量。

在 Command 对象 Execute 方法中,指示未通过将 Parameters 设置为 &vtMissing ((建议) )或 null 指针 ((即 NULL 或零 (0) ) )来指定参数。 如果将 Parameters 设置为空指针,则方法将在内部替换等效的 vtMissing,然后完成操作。

在所有方法中,可以将 RecordsAffected 设置为空指针,以指示不应当返回受影响的记录数。 此时,空指针不仅仅是省略的参数,还作为一个指示,表明方法应当放弃受影响的记录数。

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

 
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 错误对象可以在 try-catch 块中捕获。 (为了提高效率,请捕获对 _com_error object 的引用。)

请记住,这些是 ADO 错误:它们是由于 ADO 操作失败而导致的。 基础提供程序返回的错误显示为 Connection 对象的 Errors 集合中的 Error 对象。

#import 指令只为 ADO .dll 中声明的方法和属性创建错误处理例程。 不过,您可以编写自己的错误检测宏或内嵌函数,以利用同样的错误处理机制。 有关示例,请参阅主题 Visual C++ 扩展或下文中的代码。

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

以下摘要介绍用 Visual Basic 编写的 ADO 文档中的若干约定,以及等效的 Visual C++ 代码。

声明 ADO 对象

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

 
Dim rst As ADODB.Recordset 

子句“ADODB”。Recordset“, 是注册表中定义的 Recordset 对象的 ProgID。 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 对象的成员。

对缺少的参数进行编码

String

在 Visual Basic 中,如果需要对省略的字符串型操作数进行编码,只需省略该操作数即可。 在 Visual C++ 中,则必须指定操作数。 方法是编写值为空字符串的 _bstr_t。

 
_bstr_t strMissing(L""); 

Variant

在 Visual Basic 中,如果需要对省略的变量型操作数进行编码,只需省略该操作数即可。 在 Visual C++ 中,则必须指定所有操作数。 方法如下:编写省略的变量型参数,将 _variant_t 设置为特殊值 DISP_E_PARAMNOTFOUND 和类型 VT_ERROR。 或者,指定等效的预定义常量 vtMissing,该常量由 #import 指令提供。

 
_variant_t vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR); 

-或者使用-

 
...vtMissing...; 

声明变体

在 Visual Basic 中,用 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 语句编写变量型数组,或者也可以使用 Array 函数,如以下代码所示:

 
Public Sub ArrayOfVariants 
Dim cn As ADODB.Connection 
Dim rs As ADODB.Recordset 
Dim fld As ADODB.Field 
 
cn.Open "DSN=pubs", "sa", "" 
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。 因此,将创建包含四个元素的变量型数组。 随后,指定与第三列 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 要简单得多。

 
    #import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") 
    #include <stdio.h> 
    
    // Note 1 
    inline void TESTHR( HRESULT _hr ) 
    { if FAILED(_hr) _com_issue_error(_hr); } 
    
    void main(void) 
    { 
    CoInitialize(NULL); 
    try 
    { 
    _RecordsetPtr pRs("ADODB.Recordset"); 
    _ConnectionPtr pCn("ADODB.Connection"); 
    _variant_t vtTableName("authors"), 
    vtCriteria; 
    long ix[1]; 
    SAFEARRAY *pSa = NULL; 
    
    pCn->Open("DSN=pubs;User ID=MyUserId;pwd=MyPassword;Provider=MSDASQL;", "", "", 
    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 *) 。

    
     #import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") 
     #include <stdio.h> 
    
     void main(void) 
     { 
      CoInitialize(NULL); 
      try 
      { 
      _ConnectionPtr cn("ADODB.Connection"); 
      _RecordsetPtr rs("ADODB.Recordset"); 
      _bstr_t strMissing(L""); 
      long oldPgSz = 0, 
      newPgSz = 5; 
    
     // Note 1 
      cn->Open("Provider=sqloledb;Data Source=MyServer;" 
      "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 示例展示了 Item() 的标准语法和替代语法。

 
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。

注意

[!注释] 以下注释对应于代码示例中的注释部分。

  1. 当用 Item 访问集合时,索引 2 必须转换为长整型,以调用适当的构造函数。

    
     #import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") 
     #include <stdio.h> 
    
     void main(void) 
     { 
      CoInitialize(NULL); 
      try { 
      _RecordsetPtr rs("ADODB.Recordset"); 
      _variant_t vtFirstName; 
    
      rs->Open("authors", 
      "Provider=sqloledb;Data Source=MyServer;" 
      "Initial Catalog=pubs;Integrated Security=SSPI;", 
      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. 在显式编码的 变量型 中指定打开的 Connection 对象。 用 (IDispatch *) 转换该对象,以调用正确的构造函数。 此外,将第二个 _variant_t 参数显式设置为默认值 true,以使对象引用计数在 Recordset::Open 操作结束时保持正确。

  2. 表达式 (_bstr_t) 不是强制转换,而是从 Value 返回的 Variant 中提取_bstr_t字符串的 _variant_t 运算符。 表达式( (char*) )不是强制转换,而是 一个_bstr_t 运算符,用于提取指向 _bstr_t 对象中封装字符串的指针。 以下代码展示了 _variant_t 和 _bstr_t 运算符的一些有用的行为。

    
     #import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile") 
    
     #include <stdio.h> 
    
     void main(void) 
     { 
      CoInitialize(NULL); 
      try 
      { 
      _ConnectionPtr pConn("ADODB.Connection"); 
      _RecordsetPtr pRst("ADODB.Recordset"); 
    
      pConn->Open("Provider=sqloledb;Data Source=MyServer;" 
      "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(); 
     }