WTL OLE DB 数据库应用程序简介






4.87/5 (12投票s)
如何使用 ATL 向导生成的 OLE DB 消费者和 WTL 的动态数据交换 (DDX) 版本创建基本的 WTL 数据库应用程序
引言
本文介绍如何使用 WTL 的 CWinDataExchange
(DDX) 创建一个基本的 WTL 数据库应用程序,该应用程序使用 ATL 向导生成的 OLE DB 消费者。示例项目是一个标准的 WTL 基于对话框的应用程序,它使用编辑控件来显示数据。它通过 OLE DB for Jet 4.0
提供程序从 Microsoft Access 97/2000 数据库获取数据。
示例项目演示了行的插入、删除和保存,以及行集的向前和向后导航。它还展示了基本的 OLE DB 错误处理技术。
启用 ATL 对象向导
ATL 对象向导通常无法从 WTL 项目中访问。请按照以下两个步骤来解决此问题。首先,使用“项目”-->“添加到项目”-->“文件”菜单添加一个“idl”文件。例如,本文提供的示例项目包含“Atl.idl”,它基本上是一个包含单个语句的文件:library Atl { };
。添加文件后,转到“项目”-->“设置”,并将该文件从所有构建中排除,如下图所示。
其次,将以下编译器指令添加到项目的 stdafx.h 文件中。这些定义实际上永远不会被使用,但可以欺骗 ATL 对象向导允许将 ATL 对象插入到 WTL 项目中。只有少数 ATL 对象可以与 WTL 项目正确配合使用。例如,可以安全地添加数据访问-->消费者和杂项-->对话框,但不能添加对象和控件类别中的任何项。
// included only to enable ATL object wizard support #if __NEVER #include < atlcom.h > BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP() #endif
创建 OLE DB 消费者
OLE DB 类似于客户端-服务器,是一个两层模型。消费者 与应用程序代码接口,而提供程序 与数据库接口。一旦按照上述说明启用了 ATL 向导,您就可以使用“插入”菜单中的“新建 ATL 对象”选项,从“数据访问”类别(见下图)中向 WTL 项目添加一个消费者 对象。
使用 ATL 对象向导属性表,按照以下步骤(根据您的情况填写适当的值)配置 OLE DB 消费者。
- 使用默认类型:Command
- 单击“选择数据源”按钮
- 选择 OLE DB 提供程序类型
- 在“连接”选项卡上选择数据源
- 填写任何必要的登录信息
- 测试连接
- 根据需要进行高级设置
- 单击“确定”完成数据源配置
- 选择数据库表并单击“确定”
此时,您的对象向导属性应如下图所示。请注意,如果您希望为消费者启用更改、插入和删除功能,则必须选中它们。如果愿意,您还可以更改类或头文件的名称。
接受设置后,ATL 向导会创建一个 OLE DB 消费者头文件并将其添加到您的项目中。它还将 atldbcli.h
添加到 stdafx.h
。此时,消费者已基本准备就绪。本文的下一部分将介绍如何进行一些更改,以帮助消费者在包含自动编号列的表中执行插入操作。
修改 OLE DB 消费者
示例项目使用 MSAccess 数据库,其中包含一个名为 Contacts 的表,该表有一个自动编号主键列,一个文本列和一个备忘录列,两个长整型列和一个浮点数列。示例数据库是在 Access 97 中创建的,然后启用了 Access 2000。由于示例项目使用的是Jet 4.0,您的系统必须具备相应的 DLL。(注意:Jet 3.51、ODBC 和 SQL Server 存在一些显著差异,将在本系列后续文章中讨论。)
提供程序使用“访问器”来确定如何将数据传输到消费者和从消费者传输数据。Contacts 表中的每一列都将在 CContactsAccessor
类中定义一个关联的成员变量。每一列还将在 COLUMN_MAP
中为该成员变量有一个对应的条目,如下面示例应用程序的"Contacts.h"中的示例所示。
BEGIN_COLUMN_MAP(CContactsAccessor) COLUMN_ENTRY(1, m_ID) COLUMN_ENTRY(2, m_First) COLUMN_ENTRY(3, m_Last) COLUMN_ENTRY(4, m_Area) COLUMN_ENTRY(5, m_Phone) COLUMN_ENTRY(6, m_Value) END_COLUMN_MAP()
自动编号列
为了在插入时使用自动编号列,您必须添加一个额外的成员变量,并将 m_ID
变量的列条目从 COLUMN_ENTRY
更改为 COLUMN_ENTRY_STATUS
,如下所示。
// status flag for m_ID ULONG m_status; BEGIN_COLUMN_MAP(CContactsAccessor) // add status flag to column entry COLUMN_ENTRY_STATUS(1, m_ID, m_status) ...
此外,在 ClearRecord()
方法中设置状态标志。ClearRecord()
(如下所示)准备插入行,并且是任何类型行初始化的便捷位置。DBSTATUS_S_DEFAULT
告诉提供程序在未提供显式值时使用默认值。在示例应用程序中,提供程序被告知要递增自动编号主键。
void ClearRecord() { memset(this, 0, sizeof(*this)); // generate autonumber on insert m_status = DBSTATUS_S_DEFAULT; }
错误?
最后,您可能需要注释掉向导生成的某个属性,因为它与 MDAC 2.6 和 2.7 不兼容。
// invalid property for some Jet 4.0 configurations // dbinit.AddProperty(DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO, false);
准备 MainDlg
进行接下来的几个小节中描述的更改,以便将 OLE DB 消费者与应用程序的主对话框一起使用。一般过程是添加适当的头文件,将动态数据交换 (DDX) 添加到多重继承列表中,为消费者添加成员变量,创建 DDX 映射,在 InitDialog()
中添加代码以打开数据库连接,以及添加用于数据库命令(如“下一个”和“插入”)的代码。
头文件和其他
在 maindlg.h
中,添加 WTL DDX 头文件 < atlddx.h >
和消费者头文件(在示例应用程序中为"Contacts.h"
)作为包含。然后,按如下所示启用 DDX 继承。CMainDlg
也可能继承自其他类。
class CMainDlg : public CDialogImpl< CMainDlg >, public CWinDataExchange< CMainDlg >
此外,为 OLE DB 消费者添加一个成员变量。例如:
CContacts m_contacts;
DDX 映射
DDX 的目的是在成员变量和控件(如编辑框和复选框)之间复制数据值。您必须手动创建一个 DDX 映射(抱歉,没有向导),其中包含每个成员变量和控件对的条目,然后使用 DDX_LOAD
将数据从成员变量复制到控件,使用 DDX_SAVE
将数据复制回。
以下示例显示了一个 DDX 映射,它将对话框控件(如 IDC_ID
)与关联的数据库列成员变量(如 m_ID
)链接起来。请注意 DDX 数据类型(如 DDX_UINT
)的使用,并且您还可以使用内置数据有效性检查,例如文本长度和浮点数的数值范围。
BEGIN_DDX_MAP(< CMainDlg >) DDX_UINT(IDC_ID, m_contacts.m_ID) DDX_TEXT_LEN(IDC_FIRST, m_contacts.m_FIRST, 20) DDX_TEXT_LEN(IDC_LAST, m_contacts.m_LAST, 30) DDX_UINT(IDC_AREA, m_contacts.m_AREA) DDX_UINT(IDC_PHONE, m_contacts.m_PHONE) DDX_FLOAT_RANGE(IDC_VALUE, m_contacts.m_VALUE, 0.0, 1.0) END_DDX_MAP()
提示: 如果您从 OLE DB 消费者的列映射开始,然后使用搜索/替换将 COLUMN 更改为 DDX,那么创建 DDX 映射会相对容易。然后,更改类名并添加数据类型、控件 ID 和表前缀。如果控件名称与列名称相似,也会简化此过程。
在 InitDialog 中打开行集
将以下代码添加到对话框的 InitDialog()
方法中,以打开行集进行处理。
// open the rowset and move to the first // record. Display the data if successful HRESULT hr = m_contacts.Open(); if (hr == S_OK) { if (m_contacts.MoveFirst() == S_OK) DoDataExchange(DDX_LOAD); } else AtlTraceErrorRecords(hr);
错误处理
大多数 OLE DB 命令返回 HRESULT
值,例如 S_OK
、E_FAIL
和 E_NOINTERFACE
(当尝试初始化 OLE DB 接口(如 IROWSET
)时出现问题时)。当应用程序在 Visual Studio IDE 的调试模式下运行时,AtlTraceErrorRecords()
对于将错误消息打印到调试窗口非常有用。以下是返回值 E_FAIL
的示例错误列表:
ATL: OLE DB Error Record dump for hr = 0x80004005 ATL: Row #: 0 Source: "Microsoft JET Database Engine" Description: "Could not find file 'Oledb1.mdb'." Help File: "(null)" Help Context: 5003024 GUID: {0C733A8B-2A1C-11CE-ADE5-00AA0044773D} ATL: OLE DB Error Record dump end
命令处理器
以下是用于控制示例应用程序中 OLE DB 操作的两个命令处理程序示例。如果您想查看所有五个(上一个、下一个、插入、保存和删除),请下载示例项目。
atldbcli.h
头文件提供了 OLE DB 行集的多个导航方法,例如移动到第一行、最后一行、下一行或上一行。以下是移动到下一行然后将数据从行集加载到对话框编辑控件的示例。
LRESULT OnNext(WORD, WORD, HWND, BOOL& bHandled) { // move to current row + 1 or beep if already at last row if (m_contacts.MoveNext() == S_OK) DoDataExchange(DDX_LOAD); else ::MessageBeep((UINT)-1); return 0; }
以下是向行集插入新行的示例。由于示例项目使用自动编号主键列,因此必须采取步骤来递增自动编号。如本文前面部分所述,新行使用访问器的 ClearRecord()
方法进行初始化。在行缓冲区初始化后,行将被插入到数据库中,并通过 DDX 将其数据显示在对话框的编辑控件中。
LRESULT OnInsert(WORD, WORD, HWND, BOOL& bHandled) { // initialize the columns for a new row m_contacts.ClearRecord(); // insert the new row HRESULT hr = m_contacts.Insert(); // display the new row in the form if (hr == S_OK) { if (m_contacts.MoveLast() == S_OK) DoDataExchange(DDX_LOAD); } else AtlTraceErrorRecords(hr); return 0; }
预处理器定义
如果数据库表(例如示例应用程序中的 Contacts)包含浮点值,则必须按如下方式修改项目设置 C/C++ 选项卡以启用浮点支持。
- 从 Win32 Release 中删除
_ATL_MIN_CRT
- 将
_ATL_USE_DDX_FLOAT
添加到所有配置
使用条款
本文提供的示例项目是免费的。您可以随意使用此代码。
请注意,此代码能够永久删除实时数据库中的记录。请自行承担风险使用!
本软件按“现状”分发,不提供任何形式的担保。