ATL COM 和 ADO
原著:Amit Dey
翻译:王先生(MrWang2000)
原文出处:ATL COM and ADO
最近我在NIIT.Bangalore做一个在线银行工程。这个工程几乎是用VB编写,仅有一小部分涉及ATL组件,仅仅能教会我们编写分布式应用程序。 我编写的一个中间层组件用到了ATL和ADO来查询后端(SQL Server),部分代码展示在这里。
我假设读者懂(至少了解)ATL 的 COM 编程和 VB 的 ADO 编程
什么是ADO?
ADO是 ActiveX数据对象(ActiveX Data Object)的缩写。ADO使用OLEDB数据支持提供了一个面向对象的访问数据源的接口,它是DAO and RDO 对象模型的集大成者,集合了DAO 和 RDO 的优点。
C++编写OLEDB程序很容易。但是,那些没有提供指针和其他C++特性的语言(如Visual Basic)实现OLEDB就很难了。
这就是ADO真正露脸的原因。ADO是基于COM接口技术的OLEDB的高级接口,所以,任何支持COM的应用程序都可以实现ADO。
ADO的特性
- 允许访问所有的数据类型
- 提供自由线程
- 提供异步查询
- 提供客户端和服务器端指针
- 提供分离的记录集
ADO的体系结构
在ADO模型中,我们将使用三种主要的对象类型:
- Connection
- Command
- Recordset
Connection 对象用来建立与数据源的连接,首先,数据源名称,所在位置,用户ID,密码等都存储在ConnectionString对象中,用来传递给Connection对象来连接数据源。
Command 对象用来执行SQL命令,查询和存储过程
当一个查询被执行了,它返回一组由Recordset对象存储的结果集,Recordset中的数据可以被修改并更新到数据库中。
使用ADO
首先,我们将创建一个ATL DLL组件。这个组件由一个方法,该方法有一个输入参数(该工程的用户ID)并返回相应的Recordset结果引用给VB 的客户机,然后客户机在一个窗体中显示数据。
为了创建DLL ,我们使用ATL COM AppWizard产生一个应用程序框架,给工程命名为FindCust,选择服务器类型为 Dynamic Link Library ,并选择support MFC library.选项。
向工程插入一个新的ATL对象(New ATL Object ),选择Simple Object 在 ATL Object Wizard Properties窗口的Short Name文本框中输入 Search 并点击OK,添加了对象。
在类视图(CLASSVIEW)中,右击接口名称并添加一个方法,给该方法起名为SearchCust,并把下面的文字输入到Parameters文本框中:
[in] BSTR bstrcustid,[out,retval] _Recordset **ptr
点击OK按钮添加这个方法。由于SearchCust 方法返回一个Recordset 对象的引用,所以我们需要导入(import)ADO库,打开StdAfx.h 文件并添加下列代码:
#import "C:/Program Files/Common Files/System/ADO/MSADO15.DLL" rename_namespace("ADOCust") rename("EOF","EndOfFile") using namespace ADOCust;
这一步是为了让VC++编译器明白在对象类型库 MSADO15.DLL 中定义的ADO对象。rename_namespace 函数改变了一个名字空间的名称,DLL文件就是被导入到该名字空间中的。rename选项把EOF关键字改成 EndOfFile,这是因为EOF已经被定义到标准头文件中了。
.idl文件中也包含了SearchCust 方法,改方法返回Recordset 对象的引用,为了让MIDL编译器也明白ADO对象,用importlib语句在.idl文件 的library 段中导入类型库( 在importlib "stdole2.tlb"之后)如:
importlib("C:/Program Files/Common Files/System/ADO/MSADO15.DLL");
把接口的定义移动到.idl 文件新添加的imporlib语句的后边,也是为了让MIDL编译器明白ADO对象做完后,我的接口块定义如下:
[ object, uuid(EB78D558-E071-4D25-80DD-41FD3519934E), dual, helpstring("ISearch Interface"), pointer_default(unique) ] interface ISearch : IDispatch { [id(1), helpstring("method SearchCust")] HRESULT SearchCust([in] BSTR rcustid, [out,retval] _Recordset **ptr); };
构建ATL组件
现在我们已经准备好编写SearchCust方法的代码来获得相应的讯息,我们需要做如下这些事情:
- 初始化COM库
- 连接到数据源
- 执行SQL命令
- 返回Recordset对象
- 反初始化COM库
初始化COM库:
CoInitialize(NULL);
为了连接数据源,首先要声明一个Connection对象指针传递组件类的ID:
_ConnectionPtr conptr(__uuidof(Connection));
现在调用Open函数来家里与数据源的连接:
conptr->Open(_T("Provider=SQLOLEDB.1; Data Source=SQLServer; Initial Catalog=Customer"), _T("user1"), _T(""), adOpenUnspecified);
Open函数由四个参数。第一个参数是连接字符串,包含提供者的名字和要连接的SQL Server的名字。 第二个参数和第三个参数是用来建立连接的用户名和密码。 第四个参数是使用的指针的类型 _T宏确保了字符串的UNICODE的兼容性。
为了执行SQL命令,通过传递一个Command对象的CLSID来建立一个Command对象指针:
_CommandPtr cmd(__uuidof(Command));
设置Command对象的ActiveConnection 属性为打开的Connection对象的指针:
cmd->ActiveConnection=conptr;
把要执行的SQL语句存储到Command对象的CommandText属性中:
cmd->CommandText="<Your SQL statement goes here>"
创建Recordset 对象并指明作为记录源的Command对象:
_RecordsetPtr rst(__uuidof(Recordset)); rst->PutRefSource(cmd);
现在打开Recordset,使用Open方法,如下:
_variant_v vNull; rst->Open(vNull, vNull, adOpenDynamic, adLockOptimistic, adCmdText);
Open方法有个参数
第一个和第二个参数是数据源的名称和各自的活动连接由于数据源在Connection对象中已经被ActiveConnection 属性指定了,而且在Command 对象中也被指定了,所以第一个和第二个参数使用了NULL的变体值。第三个参数指明了指针类型,第四个参数指明了锁定参数,第五个参数指明了在命令被发送时数据库应该怎样评估命令。现在创建的Recordset对象指针将会引用由SQL语句返回的记录集,我们应该把这个Recordset返回给客户机,使用如下代码:
rst->QueryInterface(__uuidof(_Recordset), (void **) ptr);
QueryInterface函数使用了Recordset对象的IID 并返回由SQL语句返回的记录集的引用,当客户机调用SearchCust 方法时,这个指针会返回给客户机。
反初始化 COM库:
::CoUninitialize();
现在构建(build)组件,这将会把DLL注册到注册表中。
创建客户机
打开 VB 并创建一个新的标准EXE工程(Standard EXE project)。很简单,这里就不详细描述了。
译者:王先生(MrWang2000)
Email : yuanzhaowang@sohu.com
或yuanzhaowang@163.com