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 参考中提供了特定于 Visual C++ 语言的语法以及指向功能、参数、异常行为等的常见说明的链接。

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 的替代语法声明的。 PutRef- 语法是一个不错的选择,因为在实践中,你通常希望在此属性中放置一个打开的 Connection 对象(即 Connection 对象指针)。 另一方面,Recordset 对象具有 Get-、Put- 和 PutRefActiveConnection 操作,但没有替代语法。

集合、GetItem 方法和 Item 属性

ADO 定义了几个集合,包括字段、参数、属性和错误。 在 Visual C++ 中,GetItem(index) 方法返回集合的成员。 索引是一个变量,其值要么是集合中成员的数字索引,要么是包含成员名称的字符串。

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

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

例如,将值分配给从 pubs 数据库的 authors 表派生的 Recordset 对象的字段,名为 rs。 使用 Item() 属性访问 Recordset 对象 Fields 集合的第三个字段(集合从零开始索引;假设第三个字段名为 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");  

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

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

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

COM 特定的数据类型

通常,在 ADO API 参考中找到的任何 Visual Basic 数据类型都具有 Visual C++ 等效项。 其中包括标准数据类型,例如用于 Visual Basic Byte 的 unsigned char、用于 Integer 的 short 和用于 Long 的 long。 查看语法索引以准确了解给定方法或属性的操作数所需的内容。

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

变量

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

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

如果 ADO API 参考指示方法或属性操作数采用一个值,通常意味着该值在 _variant_t 中传递。

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

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;uid=MyUserName;pwd=MyPassword;”之类的字符串或诸如“(IDispatch *) pConn”之类的指针,则将隐式构造正确的_variant_t。

注意

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

或者你可以显式地编写一个包含诸如“_variant_t((IDispatch *) pConn, true)”之类的指针的 _variant_t。 强制转换 (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 方法有五个参数,但你可以跳过中间参数并省略尾随参数。 根据缺少的操作数的数据类型,将替换默认的 BSTR 或 Variant。

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

有三种方法是 vtMissing 典型用法的例外。 包括 Connection 和 Command 对象的 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  

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

在 Command 对象的 Execute 方法中,通过将 Parameters 设置为 &vtMissing(推荐)或空指针(即 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 对象的引用。)

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

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

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

以下是 ADO 文档中几个约定的摘要,这些约定用 Visual Basic 编码,以及它们在 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 中编码缺少的 String 操作数,只需省略该操作数。 必须在 Visual C++ 中指定操作数。 编码一个 _bstr_t,它具有一个空字符串作为值。

_bstr_t strMissing(L"");  

编码缺少的参数 - Variant

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

_variant_t  vtMissingYours(DISP_E_PARAMNOTFOUND, VT_ERROR);   

-或者使用 -

...vtMissing...;  

声明 Variant

在 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);  

使用 Variants 数组

在 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++ 示例演示如何将 SafeArray 与 _variant_t 配合使用。

备注

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

  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/PutRefProperty。

备注

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

  1. 此示例使用两种形式的缺失字符串参数:显式常量 strMissing 和编译器将用于创建临时 _bstr_t 的字符串,它将存在于 Open 方法的范围内。

  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 示例演示了 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。

注意

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

// 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) 不是强制转换,而是从 Value 返回的 Variant 中提取 _bstr_t 字符串的 _variant_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();  
}