programming with ado in visual c++
////////////////////////////////////////////////////////////////////////
vc++程序员应当如何阅读ado文档
《ado api参考》用vb的语法描述了ado api的内容。但ado程序员却使用着不同的编程语言,比如vb,vc++,vj++。对此《ado for vc++的语法索引》提供了符合vc++语法规范的详细描述,包括功能、参数、异常处理等等。
ado基于若干的com借口实现,因此它的使用对于一个正进行com编程的程序员而言更简单。比如,几乎所有使用com的细节对于vb程序员而言都是隐藏了的,但对于vc++程序员而言却要特别注意。以下是对于c和c++程序员使用ado和#import指示符方面的概述,主要描述了com使用的数据类型(variant, bstr, and safearray)和异常的处理(_com_error)。
com特定的数据类型
一般的,你在《ado api reference》中看到的vb的数据类型在vc++中也能找到对应的类型。其中包括标准的数据类型,比如unsigned char对应vb的byte,short对应integer,long对应long。参见《syntax indexes》将可以获得关于所需操作数的更详细内容。
而作为例外的专属于com使用的数据类型则有:variant, bstr, and safearray.
variant
variant是一个结构化的数据类型,包含了一个成员值及其数据类型的表示。variant可以表示相当多的数据类型,甚至另一个variant, bstr, boolean, idispatch或iunknown指针,货币,日期等等。同时com也提供了许多方法使数据类型间的转换更简单化。
_variant_t类封装并管理variant这一数据类型。
当《ado api reference》中说到一个方法或属性要使用一个参数时,通常意味着需要一个_variant_t类型的参数。这条准则在《ado api reference》的parameters一章中得到了明白无误的表述。作为例外的是,有时则会要求操作数是一个标准的数据类型,比如long或byte, 或者一个枚举值。另一个例外是要求操作数是一个字符串string。
bstr
bstr (basic string)也是一个结构化的数据类型,包括了串及串的长度。com提供了方法进行串的空间分配、操作、释放。
_bstr_t类封装并管理bstr这一数据类型。
当《ado api reference》中说到一个方法或属性要使用一个字符串参数时,通常意味着需要一个类_bstr_t型的参数。
声明一个ado对象
在vb中,一个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
而在vc++中,#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(...);
注意,如果变量是一个类的实例则用"."操作符,若是一个指向实例的指针则应使用"->"操作符。
一个变量能通过两种方式被使用。因为"->"操作符被重载,允许一个对象实例类似一个接口指针那样被使用;"->"操作符返回该指针;而由这个返回的指针访问_recordset对象的成员。
声明一个variant
在vb中,一个variant如下被声明:
dim variablename as variant
在vc++中,定义一个_variant_t型的变量即可。主要有以下几种形式。注意:这些声明只是你在变成时刻采用的一个粗略的思路。
_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数组
在vb中,利用dim语句可以进行variant数组的编程,并可以使用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
以下的代码演示了如何通过一个_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()会导致一个运行时的异常发生。这是因为vtcriteria的析构函数会在_variant_t超出使用范围时调用variantclear(),从而释放safearray。只调用safearraydestroy,而没有手动清除_variant_t,将会导致析构函数试图去清除一个无效的safearray指针。如果要调用safearraydestroy(),那么代码应该象这样:
testhr(safearraydestroy(psa));
vtcriteria.vt = vt_empty;
vtcriteria.parray = null;
实际更像是让_variant_t管理safearray。
使用属性的get/put/putref
在vb中,属性的名称并未被检验,无论它是被读取、被赋值,或者赋予一个引用。
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;user id=sa;password=;"
rs.pagesize = 10
sz = rs.pagesize
rs.activeconnection = cn
rs.open "authors",,adopenstatic
' ...
rs.close
cn.close
end sub
使用getitem(x)和item[x]
下面是vb中关于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