1、第 10 章 开发 ADO 数据库组件10.1 ADO 组件概述10.1.1 COM 组件原理第 2 章我们对 COM 的基本原理进行了简单介绍,这里有必要对 COM 组件的一些问题再进行深入的认识。COM 组件是面向对象的组件模型,对象是非常活跃的元素,我们也把它称为 COM对象。组件模型为 COM 对象提供了活动空间,COM 对象以接口的方式提供服务,我们把这种接口称为 COM 接口,进行 COM 组件的开发不仅开发 COM 对象本身,更重要的是为外部提供 COM 接口。在 Windows 操作系统里,COM 组件是一个 DLL(动态链接库) ,或者是一个EXE(可执行应用程序) 。一个
2、COM 组件往往提供多个 COM 对象,每个对象又有多种COM 接口。例如我们前面使用的 ADO 组件就是一个 DLL,而我们最熟悉的 Microsoft Office 应用家族里的 WORD 则是一个 EXE 形式的 COM 组件。COM 组件可以被普通的应用程序使用,我们称这个应用程序为组件的客户程序。COM 组件也可以被组件所使用,我们在 10.1.2 节里将对 COM 之间的调用进行详细描述。当另外的组件或者组件的客户程序调用 COM 组件的功能时,它需要首先创建一个COM 对象,或者通过其它途径获得 COM 对象,然后通过该对象提供的 COM 接口调用它所提供的服务。当所有的服务结束
3、后,如果客户程序不再需要这个 COM 组件了,那么它应该释放对象所占用的资源,包括对象本身。10.1.2 ADO 组件模型ADO 是采用客户/服务器体系结构的 COM 组件模型,对象和客户之间的相互作用是作为客户/服务器模型里的元素展开的,然而, COM 不仅仅是一个简单的客户/ 服务器模型,客户和对象之间可能有一些功能的重新分配,比如,客户可能帮助 COM 对象进行某些操作。COM 组件已经可以非常灵活地使用这个模型。我们来看一看 COM 组件的客户与对象之间的相互作用。在图 10-1(a)中,客户和对象之间只是一种简单的客户/服务器结构;在图 10-1(b)中,对象 2 既为客户提供服务,
4、也为对象 1 提供服务,这时对象 1 就称为对象 2 的客户,在这样的模型中,对象 1 由客户直接创建,而对象 2 既可以由客户创建,也可以由对象 1 创建;图 10-1(c)和 10-1(d)表示了两种重要的对象重用结构,分别称为包容(containment)和聚合(aggregation) 。对于客户来说,他只知道对象 1 的存在,而不知第 10 章 开发 ADO 数据库组件-305-道对象 2 的存在,但是对象 1 在实现某些功能时,调用了对象 2 的服务。包容和聚合的不同在于,在图 10-1(c)所示的包容结构里里,对象 2 的功能由对象 1 直接调用,对象 1得到对象 2 的服务后再
5、把结果传递给客户,而在图 10-1(d)所示的聚合结构里里,对象1 只是简单地把对象2的句柄传递给客户,由客户执行对象 2 的功能。(a) (b)(c) (d)图 10-1 COM 的组件对象模型我们在开发 ADO 组件的时候通常都是采用图 10-1(c)的模式,这样,客户对对象2 可以没有任何认识,但是仍然可以得到对象 2 的服务。下面对包容和聚合两种重用模型的实现方法进行详细介绍。包容假定我们已经实现了一个 COM 对象,不妨称之为对象 A,它实现了接口ISomeInterface,但是后来有了新的需要,我们要实现另一个对象,我们称之为对象 B,它既要实现一个扩充的 ISomeInterf
6、ace 接口,又要实现其他的接口,例如接口IOtherInterface,因此我们考虑在实现对象 B 实现的过程中重用对象 A 的接口,只要在实现对象 B 的时候增加新的功能就可以了。最简单的想法就是在实现对象 B 的ISomeInterface 接口时调用对象 A 的接口 ISomeInterface,从而实现对象 B 的ISomeInterface 接口功能。这时对象 A 对于客户是不可见的,客户当然也没有必要关心对象 A 了,这就是一种包容方式的重用模型,这种模型的实现过程可以用图 10-2 表示。图 10-2 包容方式的对象重用模型从图 10-2 可以看出,对于对象 B 来说,它本身既
7、是一个 COM 对象,又是对象 A 的一个客户,因为它调用对象 A 的功能服务。对象 B 不再重复实现对象 A 已经实现的功能,而是直接调用对象 A 提供对外的功能服务,而对象 B 的客户根本就不知道这一点,可以说对象 B 的客户是最大的受益者,它得到了全面的功能服务。客户 对象客户 COM 对象 1对象 2客户 COM 对象 1对象 2IOtherInterfaceISomeInterface 对象 BISomeInterface 对象 A客户程序客户 对象 1 对象 2第 10 章 开发 ADO 数据库组件-306-聚合假定我们要实现一个对象 B,它支持两个接口: ISomeInterfa
8、ce 和 IOtherInterface,同时我们发现对象 B 所提供的 ISomeInterface 接口功能在对象 A 里已经有了完整的实现,不需要任何修改即可以满足对象 B 的要求。采用包容方式的重用模型要求对象 B 实现两个接口,即 ISomeInterface 和 IOtherInterface,其中 ISomeInterface 接口只是简单地调用对象A 的 ISomeInterface 功能,除此之外没有任何扩展。我们考虑另外一种实现方法,对象 B本身并不提供 ISomeInterface 接口,但是可以提供 ISomeInterface 接口的功能。在客户请求 ISomeInt
9、erface 接口时,对象 B 把对象 A 的 ISomeInterface 接口暴露给客户,由客户再向对象 A 发送 ISomeInterface 接口请求,从而实现对象 B 的 ISomeInterface 功能,这种重用模型称为聚合,聚合模型的实现过程可以用图 10-3 表示。图 10-3 聚合方式的对象重用模型在聚合模型中,被聚合的对象 A 虽然直接向对象 B 的客户程序提供功能服务,但是它的生存期仍然受对象 B 的控制,而且其它的一些行为也受到对象 B 的控制,包括内部状态和状态初始化以及获取数据等,对象 B 借助对象 A 向客户程序提供 ISomeInterface 接口服务。实现
10、聚合的关键是对象 B 的 QueryInterface 函数,当客户程序向对象 B 请求ISomeInterface 接口时,对象 B 就把对象 A 的 ISomeInterface 接口指针存放到输出的参数里,于是客户就获得了对象 A 的 ISomeInterface 接口指针,可以直接调用接口的方法了。聚合涉及到聚合对象和被聚合对象双方的协作,并不是每个对象都能够支持聚合特性,但是聚合体现了组件软件真正意义上的重用,而包容的重用性则是建立在客户/服务器模型相对性的基础上的,实际上也就是客户程序和组件程序的嵌套关系。这是聚合和包容的本质区别。聚合和包容是实现 COM 对象重用的两种模型,它们
11、之间并无矛盾,可以在一个对象中同时使用两种模型,有的接口通过包容模型实现,而有的模型则通过聚合模型实现,需要根据实际情况确定相应的策略。10.1.3 ADO 组件同客户程序的协作ADO 在实现同客户程序协作时采用两种方式:进程内协作和进程外协作,根据这种协作方式可以将 COM 组件分成进程内组件和进程外组件两类。进程内组件与客户程序运行在同一个进程里,因而可以拥有共同的地址空间,所以客户程序和组件之间的连接是非常简单的,其效率也与统一进程里的任何 C+函数没有区别,并且在编译时还可以进行完整的 C+参数与返回类型的检查。进程内组件的缺点在于它对调用IOtherInterfaceISomeInt
12、erface客户程序对象 B对象 A传递第 10 章 开发 ADO 数据库组件-307-客户程序的稳定性的依赖。进程内组件的健壮性是客户程序健壮性的重要基础,进程内组件的崩溃将导致客户程序的崩溃。进程内组件往往是通过 DLL 代码库提供的。进程外组件则不同,它运行在独立的地址空间里,不与客户程序发生数据共享,进程外组件与客户程序的交互是通过组件的接口实现的,客户程序向组件请求接口,组件将支持的接口指针返回给客户程序,客户程序再通过这个指针实现对组件功能的调用。这种情况下客户程序的健壮性有了保障,进程外组件的不稳定性不会影响到客户程序,组件最多返回给客户程序一个空的接口指针或者空的数据,只要客户
13、程序不忘记检查组件的返回情况,就不会发生组件功能调用引起的软件故障。当然这种组件也存在弱点,由于它运行在独立的地址空间里,所以运行的时候占用相对多的存储空间,运行效率也受到影响。往往提供进程外组件的都是一些有独立运行能力和服务的大型组件,例如 Office 家族的 Word和 Excel,都是进程外组件的典型代表。进程外组件一般是以 EXE 的形式存在的,除了能够为其它客户程序提供组件服务外,进程外组件本身也能够提供直接的客户服务。10.2 ADO 数据库组件开发实例本章我们要开发的 ADO 组件是对 ADO 组件的一个功能具体化,借助 ADO 组件提供的对象及其接口,实现更高层次的组件封装,
14、以实现更加具体的功能,将组件的可操作性提高到新的水平。从组件重用的角度来说,本实例属于包容方式的重用模型。10.2.1 实例概述需求调查与分析从第 8 章的 ADO 编程方法我们可以看到,使用 ADO 的 COM 对象需要进行代码库的引入、ADO 对象的实例创建等步骤,而且 ADO 组件提供的大量的属性以及方法是我们不需要或者说用不到的,因此我们考虑开发一个更高层次上的 ADO-EX 组件,该组件只提供我们最常用的对象属性和方法。数据库系统及其访问技术本实例是不局限于数据源的,ADO 对象支持的数据源,本实例开发的组件都能够支持。本实例所做的工作是缩小 ADO 对象可见的属性以及方法。实例实现
15、效果由于本实例开发的是 ADO 组件,因此需要一个客户程序进行组件开发效果测试,在本实例后面的 10.3 节里,我们编写了一个简单的客户程序测试该 ADO 组件。10.2.2 实例实现过程创建 ADOAccessor 工程ADOAccessor 工程是本章建立 ADO 组件的 VC+工程,该工程是基于 ATL COM 的。ADOAccessor 工程的创建过程如下:第 10 章 开发 ADO 数据库组件-308-(1) 打开 VC+的工程创建向导。从 VC+的菜单中执行“FileNew”命令,将 VC+ 6.0 工程创建向导显示出来。如果当前的选项标签不是“Projects” ,要单击“Pro
16、jects”选项标签将它选中。在左边的列表里选择“ATL COM AppWizard”项,在“Project name”编辑区里输入工程名称“ADOAccessor” ,并在“Location ”编辑区里调整工程路径,如图10-4 所示。图 10-4 工程创建向导(2) 选择 COM 服务器类型。单击工程创建向导“New”窗口的“OK ”按钮,弹出“ATL COM AppWizard Step 1 of 1”窗口,如图 10-5 所示,开始创建 ADOAccessor 工程。创建 ADOAccessor 工程的第一步是选择组件服务器的类型。组件服务器通常有三种类型:动态链接库(Dynamic
17、Link Library,DLL) 、可执行(Excutable)EXE 和服务(Service)EXE 。这里我们选择第一种,即动态链接库(缺省设置) 。在“ATL COM AppWizard Step 1 of 1”窗口里,点击“Finish”按钮,进入下一步。图 10-5 选择 COM 服务器类型第 10 章 开发 ADO 数据库组件-309-(3) 弹出的工程新建信息“New Project Information”对话框显示了工程创建信息,如图 10-6 所示。在窗口里单击 “OK”按钮,ADOAccessor 工程创建完成。图 10-6 工程创建信息添加 ADO 对象库的支持为了能
18、够使用 ADO,我们需要将 ADO 库引入工程。操作系统都提供了 ADO 代码库,它是通过 DLL 的形式存放的,在进行 ADO 编程时,首先要将这个库引入工程。下面的代码将 ADO 库引入 ADOAccessor 工程。#import “E:Program FilesCommon FilesSystemadomsado15.dll“ no_namespace rename(“EOF“, “adoEOF“)可以将这段代码加到工程的 stdafx.h 里,也可以加到工程的 ADODemo.h 里,添加的时候要注意下面的问题: 必须先在系统里查找 msado15.dll 文件的路径。由于在不同的系
19、统安装时,这个路径可能不同,不过通常该文件都在系统的“Program FilesCommon FilesSystemado”路径下。 我们在本实例里使用的是 ADO 的 1.5 版本,也许你的操作系统不支持 1.5,只是1.0 版的,或者是 2.0 版本,这时需要将这个库文件名称改为相应的文件名即可。1.5 版本在对 1.0 版本升级的时候改变了一些函数的接口参数,在实际编程时需要注意灵活处理。添加要实现的 ADO 对象使用 VC+6.0 提供的“New ATL Object”命令,我们为工程添加 ADO 对象。操作步骤:(1) 执行 VC+的“Insert New ATL Object”菜单
20、命令,弹出“ATL Object Wizard”对话框,如图 10-7 所示。第 10 章 开发 ADO 数据库组件-310-图 10-7 “ATL Object Wizard”对话框(2) 在“ATL Object Wizard”对话框的“Category”列表里选择“Objects”项,然后在右边的“Objects ”列表里选择 “Simple Object”项,然后单击“Next”按钮,弹出“ATL Object Wizard 属性”对话框,如图 10-8 所示。图 10-8 “ATL Object Wizard 属性”对话框(3) 在“ATL Object Wizard 属性”对话框的
21、“C+”组的“Short Name”编辑区里输入“ADOTier” ,其它编辑区的内容自动添加进去,如图 10-9 所示。图 10-9 在“ATL Object Wizard 属性”对话框里输入“ADOTier”对象名称(4) 在 “ATL Object Wizard 属性”对话框里单击“确定”按钮,VC+ 就在第 10 章 开发 ADO 数据库组件-311-ADOAccessor 工程里添加了一个 COM 对象 ADOTier。这时 VC+在工程里添加了如下文件: ADOTier.h,该文件用于存放 ADOTier 对象的数据和方法的声明。 ADOTier.cpp,该文件用于存放 ADOTi
22、er 对象的方法的实现代码。在 ADOTier.h 里,CADOTier 类声明如下:/ CADOTierclass ATL_NO_VTABLE CADOTier : public CComObjectRootEx,public CComCoClass,public IDispatchImplpublic:CADOTier()DECLARE_REGISTRY_RESOURCEID(IDR_ADOTIER)DECLARE_PROTECT_FINAL_CONSTRUCT()BEGIN_COM_MAP(CADOTier)COM_INTERFACE_ENTRY(IADOTier)COM_INTERFA
23、CE_ENTRY(IDispatch)END_COM_MAP()/ IADOTierpublic:;可以看出,CADOTier 类是多重继承的结果,该类继承了 ATL 的CComObjectRootEx、CComCoClass 和 IDispatchImpl 模板。而且,通过为工程添加 ADOTier 对象,VC+在 ADOAccessor.idl 文件里添加了以下代码:object,uuid(9A5D23C5-7848-46FF-A1BB-1516122DA76A),dual,helpstring(“IADOTier Interface“),pointer_default(unique)in
24、terface IADOTier1 : IDispatch;第 10 章 开发 ADO 数据库组件-312-这些代码是对 ADOTier 类标识 CLSID 的声明,并建立了一个空的接口模板。添加 ADO 数据库访问对象在 ADOTier.h 的类声明里添加如下的 ADO 对象声明代码:private:_CommandPtr m_command;_RecordsetPtr m_recordset;_ConnectionPtr m_connection;这三个对象我们在完成第 8 章以后已经可以熟练使用了。声明工程的枚举数据类型为了便于代码编写,我们在 ADOAccessor.idl 文件的头部
25、里添加了以下代码:enum DataTypeEnumadEmpty = 0,adTinyInt = 16,adSmallInt = 2,adInteger = 3,adBigInt = 20,adUnsignedTinyInt = 17,adUnsignedSmallInt = 18,adUnsignedInt = 19,adUnsignedBigInt = 21,adSingle = 4,adDouble = 5,adCurrency = 6,adDecimal = 14,adNumeric = 131,adBoolean = 11,adError = 10,adUserDefined =
26、132,adVariant = 12,adIDispatch = 9,adIUnknown = 13,adGUID = 72,adDate = 7,adDBDate = 133,adDBTime = 134,adDBTimeStamp = 135,adBSTR = 8,adChar = 129,第 10 章 开发 ADO 数据库组件-313-adVarChar = 200,adLongVarChar = 201,adWChar = 130,adVarWChar = 202,adLongVarWChar = 203,adBinary = 128,adVarBinary = 204,adLongV
27、arBinary = 205;enum ParameterDirectionEnumadParamUnknown = 0,adParamInput = 1,adParamOutput = 2,adParamInputOutput = 3,adParamReturnValue = 4;声明用于全局的静态变量我们在代码编写的时候经常用到 VARIANT 空值的使用,为此我们在 ADOTier.cpp 的文件头部声明如下的全局的静态变量:static VARIANT* pvtEmpty = static_cast (static _variant_t vtMissing2(DISP_E_PARAMN
28、OTFOUND, VT_ERROR);static VARIANT* pvtEmpty2 = static_cast (为 ADOTier 对象添加方法为了实现组件的接口,必须在 ADOAccessor 工程中为 ADOTier 对象添加方法。VC+提供了方便的方法添加途径,下面介绍将一个方法添加到对象的接口里。操作步骤:(1) 在 VC+开发平台的工作区上,选择“Class View”选项标签。(2) 单击“ADOAccessor classes”节点左边的 图标,将该节点展开。(3) 在展开的节点里选择“IADOTier”并单击鼠标右键,弹出如图 10-10 所示的菜单。第 10 章 开发
29、 ADO 数据库组件-314-图 10-10 “IADOTier”节点的弹出菜单(4) 执行弹出菜单里的 “Add Method”项,开始添加方法操作,VC+弹出“Add Method to Interface”对话框,如图 10-11 所示。图 10-11 “Add Method to Interface”对话框(5) 在“Add Method to Interface”对话框的“Method Name”编辑区里输入要添加的方法名称,在“Parameters ”编辑区里输入方法的参数。例如输入 “Open”方法,并输入“in BSTR source, in BSTR user, in BST
30、R pwd”参数, “in”关键字表示参数为输入类型,对应的“out” 表示参数为输出类型,这时的“ Add Method to Interface”对话框如图 10-12 所示。图 10-12 输入 Open 方法的“Add Method to Interface”对话框(6) 在“Add Method to Interface”对话框里单击“OK”按钮,完成方法的添加。这时 ADOTier.h 文件里增加了方法声明的代码:STDMETHOD(Open)(/*in*/ BSTR source, /*in*/ BSTR user, /*in*/ BSTR pwd);在 ADOTier.cpp
31、文件里增加了方法实现的空函数体,代码如下:STDMETHODIMP CADOTier:Open(BSTR source, BSTR user, BSTR pwd)/ TODO: Add your implementation code here第 10 章 开发 ADO 数据库组件-315-return S_OK;在 ADOAccessor.idl 文件里增加了接口的声明代码:id(1), helpstring(“method Open“) HRESULT Open(in BSTR source, in BSTR user, in BSTR pwd);使用上述步骤为 ADOTier 对象添加表
32、 10-1 所罗列的方法。表 10-1 为 ADOTier 对象添加的方法方法名称 方法参数 方法的功能描述Open in BSTR source, in BSTR user, in BSTR pwd 打开数据库连接OpenRecordset in VARIANT query 打开结果集CloseRecordset (void) 关闭结果集ExecuteConnection in BSTR query, in VARIANT_BOOL bChangeRec 执行连接ExecuteCommand in VARIANT_BOOL bStoredProcedure, in VARIANT_BOOL
33、bChangeRec执行命令AppendParameter in enum DataTypeEnum type, in VARIANT value, in enum ParameterDirectionEnum where, in long size增加参数Update (void) 更新Delete (void) 删除Close (void) 关闭数据库连接First (void) 到结果集首记录Next (void) 到结果集尾记录Last (void) 到结果集下一记录Prev (void) 到结果集前一记录ParamQuery in BSTR query, in long idx1,
34、in BSTR idx2, in BSTR idx3参数查询CallStoredProc in long idx1, in BSTR idx2, in BSTR idx3 调用存储过程StoredProc in BSTR newVal 保存存储过程ChangeParameter in long idx, in enum DataTypeEnum type, in VARIANT value, in enum ParameterDirectionEnum where, in long size改变参数Requery (void) 重新查询ADORelease (void) 释放 ADO 对象编写
35、添加的 ADOTier 对象的方法上一步我们已经添加了 ADOTier 对象的若干方法,下面我们来实现这些方法。第 10 章 开发 ADO 数据库组件-316-1. Open 方法Open 方法用于打开同数据库的连接,函数的实现代码如下:STDMETHODIMP CADOTier:Open(BSTR source, BSTR user, BSTR pwd)HRESULT hr = m_connection.CreateInstance(_uuidof(Connection);if (SUCCEEDED(hr)hr = m_connection-Open(source, user, pwd, a
36、dConnectUnspecified);if (SUCCEEDED(hr)hr = m_command.CreateInstance(_uuidof(Command);if (SUCCEEDED(hr)hr = m_recordset.CreateInstance(_uuidof(Recordset);return hr;Open 方法执行 m_connection 的 CreateInstance 函数建立 ADO 的数据库连接对象,然后使用该对象的 Open 方法打开同数据库的连接,接下来函数还创建了用于数据库命令执行的 m_command 对象,创建用于存放结果集的 m_records
37、et 对象。函数通过检查执行返回的 HRESULT 类型代码 hr,以获取执行的情况。2. OpenRecordset 函数OpenRecordset 函数用于打开根据参数提供的 SQL 查询的结果集,函数的实现代码如下:STDMETHODIMP CADOTier:OpenRecordset(VARIANT query)VARIANT v;V_VT(V_DISPATCH(V_DISPATCH(/ 打开结果集return m_recordset-Open(query, v, adOpenDynamic, adLockOptimistic, adCmdText);OpenRecordset 函数首
38、先获取用于当前连接的连接对象的 Idispatch 指针,将查询语句、连接的 Idispatch 指针、结果集类型、锁定类型以及执行 SQL 类型传递到 m_recordset 对象的 Open 方法里,通过该方法打开一个结果集。3. CloseRecordset 函数CloseRecordset 函数用于关闭当前打开的结果集,函数实现代码如下:STDMETHODIMP CADOTier:CloseRecordset()return m_recordset-Close();第 10 章 开发 ADO 数据库组件-317-CloseRecordset 函数简单执行了 m_recordset 对象
39、的 Close 方法以关闭打开的结果集。4. ExecuteConnection 函数ExecuteConnection 函数执行连接对象的特定命令,函数的实现代码如下:STDMETHODIMP CADOTier:ExecuteConnection(BSTR query, VARIANT_BOOL bChangeRec)_Recordset* prec = 0;HRESULT hr = S_OK;prec = m_connection-Execute(query, pvtEmpty, adCmdText);if (prec)if (bChangeRec) m_recordset = prec;
40、else prec-Release();return hr;在该函数里,m_connection 对象使用 Execute 方法执行输入参数 query 里存放的 SQL语句,如果输入参数 bChangeRec 为真,则将执行产生的结果集保存到 m_recordset 对象里,否则释放该对象。5. ExecuteCommand 函数ExecuteCommand 函数执行一个数据库存储过程,函数的实现代码如下:STDMETHODIMP CADOTier:ExecuteCommand(VARIANT_BOOL bStoredProcedure, VARIANT_BOOL bChangeRec)_R
41、ecordset* prec = 0;HRESULT hr = S_OK;if (bStoredProcedure)prec = m_command-Execute(pvtEmpty, pvtEmpty2, adCmdStoredProc);elseprec = m_command-Execute(pvtEmpty, pvtEmpty2, adCmdText);if (prec) if (bChangeRec) m_recordset = prec;else prec-Release();return hr;在该函数里,m_command 对象使用 Execute 方法执行输入参数 bStor
42、edProcedure 里的数据库存储过程,从而返回一个结果集,如果输入参数 bChangeRec 为真,则将结果集保存到 m_recordset 对象里,否则释放该对象。第 10 章 开发 ADO 数据库组件-318-6. AppendParameter 函数AppendParameter 函数将保存在输入参数里的参数信息追加到数据库里,函数的实现代码如下:STDMETHODIMP CADOTier:AppendParameter(enum DataTypeEnum type, VARIANT value, enum ParameterDirectionEnum where, long si
43、ze)_ParameterPtr param;HRESULT hr = param.CreateInstance(_uuidof(Parameter);if (SUCCEEDED(hr) hr = param-put_Type(type);if (SUCCEEDED(hr) hr = param-put_Value(value);if (SUCCEEDED(hr) hr = param-put_Direction(where);if (SUCCEEDED(hr) hr = param-put_Size(size);Parameters* params = 0;if (SUCCEEDED(hr)
44、 hr = m_command-get_Parameters(if (SUCCEEDED(hr) hr = params-Append(param);if (SUCCEEDED(hr) params-Release();param-Release();return hr;AppendParameter 函数首先创建一个_ParameterPtr 对象,然后使用输入参数信息设置这个对象,然后通过 m_command 对象的 get_Parameters 方法将该参数从数据库里读取,最后使用 ParameterPtr 对象的 Append 方法将该参数追加到 ParameterPtr 对象里。7.
45、 Update 函数Update 函数将结果集的修改更新到数据库的表里,函数的实现代码如下:STDMETHODIMP CADOTier:Update()return m_recordset-Update();Update 函数执行了 m_recordset 对象的 Update 方法,实现结果及更改的保存。8. Delete 函数Delete 函数将结果集当前行删除,函数的实现代码如下:STDMETHODIMP CADOTier:Delete()return m_recordset-Delete(adAffectCurrent);Delete 函数调用了 m_recordset 对象的 Del
46、ete 方法执行行删除操作,参数第 10 章 开发 ADO 数据库组件-319-adAffectCurrent 表示修改影响到当前的结果集,即当前结果集同时改变。9. Close 函数Close 函数将当前的连接同数据库断开,函数的实现代码如下:STDMETHODIMP CADOTier:Close()return m_connection-Close();Close 函数执行了 m_connection 对象的 Close 方法实现数据库连接的断开。10. First 函数First 函数将结果集当前行移动到结果集的第一行,函数的实现代码如下:STDMETHODIMP CADOTier:Fi
47、rst()return m_recordset-MoveFirst();First 函数执行了 m_recordset 对象的 MoveFirst 方法,实现行移动。11. Next 函数Next 函数将结果集当前行移动到下一行,函数的实现代码如下:STDMETHODIMP CADOTier:Next()return m_recordset-MoveNext();Next 函数执行了 m_recordset 对象的 MoveNext 方法,实现行移动。12. Last 函数Last 函数将结果集当前行移动到最后一行,函数的实现代码如下:STDMETHODIMP CADOTier:Last()r
48、eturn m_recordset-MoveLast();Last 函数执行了 m_recordset 对象的 MoveLast 方法,实现行移动。13. Prev 函数Prev 函数将结果集当前行移动到前一行,函数的实现代码如下:STDMETHODIMP CADOTier:Prev()return m_recordset-MovePrevious();Prev 函数执行了 m_recordset 对象的 MovePrevious 方法,实现行移动。第 10 章 开发 ADO 数据库组件-320-14. ParamQuery 函数ParamQuery 函数执行带参数的数据库查询操作,函数的实现
49、代码如下:STDMETHODIMP CADOTier:ParamQuery(BSTR query, long idx1, BSTR idx2, BSTR idx3)HRESULT hr;hr = ChangeParameter(0, adInteger, (_variant_t) idx1, adParamInput, -1);if (SUCCEEDED(hr)hr = ChangeParameter(1, adVarChar, (_variant_t) idx2, adParamInput, 25);if (SUCCEEDED(hr)hr = ChangeParameter(2, adVarChar, (_variant_t) idx3, adParamInput, 80);if (SUCCEEDED(hr)hr = m_com