TN054:使用 MFC DAO 类时直接调用 DAO
注意
DAO 用于 Access 数据库,并通过 Office 2013 提供支持。 DAO 3.6 是最终版本,被视为已过时。 Visual C++ 环境和向导不支持 DAO(尽管包含 DAO 类且你仍可以使用它们)。 Microsoft 建议对新项目使用 OLE DB 模板或 ODBC 和 MFC。 应只在维护现有应用程序时使用 DAO。
使用 MFC DAO 数据库类时,可能需要直接使用 DAO。 一般不存在这种情况,但 MFC 提供了一些帮助器机制,以便于在将 MFC 类与直接 DAO 调用结合使用时发出直接 DAO 调用。 只需编写几行代码应该就能对 MFC 管理的 DAO 对象的方法发出直接 DAO 调用。 如果需要创建和使用不由 MFC 管理的 DAO 对象,则必须通过对该对象实际调用 Release
来处理略多的工作。 本技术说明解释何时可能需要直接调用 DAO、MFC 帮助器可为你提供哪些帮助,以及如何使用 DAO OLE 接口。 最后,本说明提供了一些示例函数,用于演示如何直接调用 DAO 以获得 DAO 安全性功能。
何时发出直接 DAO 调用
发出直接 DAO 调用的最常见场合是需要刷新集合或实现未由 MFC 包装的功能时。 MFC 未公开的最重要功能是安全性。 如果你想要实现安全性功能,需要直接使用 DAO 用户和组对象。 除了安全性之外,只有其他少数几项 DAO 功能不受 MFC 支持。 其中包括记录集克隆和数据库复制功能,以及后来对 DAO 补充的几项功能。
DAO 和 MFC 实现的简要概述
MFC 的 DAO 包装会处理许多的细节,使你可以更方便地使用 DAO,而不用担心一些细枝末节的事情。 这包括 OLE 的初始化、DAO 对象(尤其是集合对象)的创建和管理、错误检查,以及提供强类型化的更简单接口(无 VARIANT 或 BSTR
参数)。 可以发出直接 DAO 调用,同时仍利用这些功能。 代码只需针对直接 DAO 调用创建的任何对象调用 Release
,而无需修改 MFC 可能在内部依赖的任何接口指针。 例如,除非你了解所有内部衍生物,否则请不要修改打开的 CDaoRecordset
对象的 m_pDAORecordset 成员。 但是,可以使用 m_pDAORecordset 接口直接调用 DAO 以获取 Fields 集合。 在这种情况下,不会修改 m_pDAORecordset 成员。 处理完对象后,只需对 Fields 集合对象调用 Release
。
用于简化 DAO 调用的帮助器的说明
为简化 DAO 调用而提供的帮助器与在 MFC DAO 数据库类内部使用的帮助器相同。 这些帮助器用于在发出直接 DAO 调用、记录调试输出、检查预期错误以及按需引发相应异常时检查返回代码。 有两个基础帮助器函数和四个宏映射到这两个帮助器之一。 阅读代码就能获得最好的解释。 请参阅 AFXDAO.H 中的 DAO_CHECK、DAO_CHECK_ERROR、DAO_CHECK_MEM 和 DAO_TRACE 以了解宏,并参阅 DAOCORE.CPP 中的 AfxDaoCheck 和 AfxDaoTrace。
使用 DAO OLE 接口
DAO 对象层次结构中每个对象的 OLE 接口在头文件 DBDAOINT.H 中定义,该文件位于 \Program Files\Microsoft Visual Studio .NET 2003\VC7\include 目录中。 这些接口提供用于操作整个 DAO 层次结构的方法。
对于 DAO 接口中的许多方法,需要操作 BSTR
对象(OLE 自动化中使用的以长度为前缀的字符串)。 BSTR
对象通常封装在 VARIANT 数据类型中。 MFC 类 COleVariant
本身继承自 VARIANT 数据类型。 DAO 接口将返回 ANSI 或 Unicode BSTR
,具体取决于你要为 ANSI 还是 Unicode 生成项目。 V_BSTR 和 V_BSTRT 这两个宏可用于确保 DAO 接口获取预期类型的 BSTR
。
V_BSTR 将提取 COleVariant
的 bstrVal 成员。 需要将 COleVariant
的内容传递给 DAO 接口的方法时,通常会使用此宏。 以下代码片段演示了利用 V_BSTR 宏的 DAO DAOUser 接口的两个方法的声明和实际用法:
COleVariant varOldName;
COleVariant varNewName(_T("NewUser"), VT_BSTRT);
// Code to assign pUser to a valid value omitted DAOUser *pUser = NULL;
// These method declarations were taken from DBDAOINT.H
// STDMETHOD(get_Name) (THIS_ BSTR FAR* pbstr) PURE;
// STDMETHOD(put_Name) (THIS_ BSTR bstr) PURE;
DAO_CHECK(pUser->get_Name(&V_BSTR (&varOldName)));
DAO_CHECK(pUser->put_Name(V_BSTR (&varNewName)));
请注意,上述 COleVariant
构造函数中指定的 VT_BSTRT
参数确保 COleVariant
中存在 ANSI BSTR
(如果生成应用程序的 ANSI 版本)或 Unicode BSTR
(如果生成应用程序的 Unicode 版本)。 这符合 DAO 的预期。
另一个宏 V_BSTRT 将根据生成类型(ANSI 或 Unicode)提取 COleVariant
的 ANSI 或 Unicode bstrVal 成员。 以下代码演示了如何将 BSTR
值从 COleVariant
提取到 CString
中:
COleVariant varName(_T("MyName"), VT_BSTRT);
CString str = V_BSTRT(&varName);
DAOVIEW 示例中演示了 V_BSTRT 宏,以及用于打开存储在 COleVariant
中的其他类型的其他方法。 具体而言,此转换是在 CCrack::strVARIANT
方法中执行的。 在可能的情况下,此方法会将 COleVariant
的值转换为 CString
的实例。
直接调用 DAO 的简单示例
有时可能需要刷新基础 DAO 集合对象。 通常不必要这样做,但如果必要,刷新过程也很简单。 可能需要刷新集合的一个示例是多个用户在多用户环境中操作时创建了新的 tabledef。 在这种情况下,tabledefs 集合可能会过时。 若要刷新集合,只需调用特定集合对象的 Refresh
方法并检查错误:
DAO_CHECK(pMyDaoDatabase->m_pDAOTableDefs->Refresh());
请注意,当前所有 DAO 集合对象接口都是 MFC DAO 数据库类的未记录实现细节。
直接为 DAO 安全性功能使用 DAO
MFC DAO 数据库类不会包装 DAO 安全性功能。 必须调用 DAO 接口的方法才能使用某些 DAO 安全性功能。 以下函数设置系统数据库,然后更改用户的密码。 此函数调用随后定义的其他三个函数。
void ChangeUserPassword()
{
// Specify path to the Microsoft Access *// system database
CString strSystemDB =
_T("c:\\Program Files\\MSOffice\\access\\System.mdw");
// Set system database before MFC initilizes DAO
// NOTE: An MFC module uses only one instance
// of a DAO database engine object. If you have
// called a DAO object in your application prior
// to calling the function below, you must call
// AfxDaoTerm to destroy the existing database
// engine object. Otherwise, the database engine
// object already in use will be reused, and setting
// a system datbase will have no effect.
//
// If you have used a DAO object prior to calling
// this function it is important that DAO be
// terminated with AfxDaoTerm since an MFC
// module only gets one copy of the database engine
// and that engine will be reused if it hasn't been
// terminated. In other words, if you do not call
// AfxDaoTerm and there is currently a database
// initialized, setting the system database will
// have no effect.
SetSystemDB(strSystemDB);
// User name and password manually added
// by using Microsoft Access
CString strUserName = _T("NewUser");
CString strOldPassword = _T("Password");
CString strNewPassword = _T("NewPassword");
// Set default user so that MFC will be able
// to log in by default using the user name and
// password from the system database
SetDefaultUser(strUserName, strOldPassword);
// Change the password. You should be able to
// call this function from anywhere in your
// MFC application
ChangePassword(strUserName, strOldPassword, strNewPassword);
// ...
}
接下来的四个示例演示如何:
设置系统 DAO 数据库(.MDW 文件)。
设置默认用户和密码。
更改用户的密码。
更改 .MDB 文件的密码。
设置系统数据库
以下示例函数设置应用程序使用的系统数据库。 必须在发出任何其他 DAO 调用之前调用此函数。
// Set the system database that the
// DAO database engine will use
void SetSystemDB(CString& strSystemMDB)
{
COleVariant varSystemDB(strSystemMDB, VT_BSTRT);
// Initialize DAO for MFC
AfxDaoInit();
DAODBEngine* pDBEngine = AfxDaoGetEngine();
ASSERT(pDBEngine != NULL);
// Call put_SystemDB method to set the *// system database for DAO engine
DAO_CHECK(pDBEngine->put_SystemDB(varSystemDB.bstrVal));
}
设置默认用户和密码
若要为系统数据库设置默认用户和密码,请使用以下函数:
void SetDefaultUser(CString& strUserName,
CString& strPassword)
{
COleVariant varUserName(strUserName, VT_BSTRT);
COleVariant varPassword(strPassword, VT_BSTRT);
DAODBEngine* pDBEngine = AfxDaoGetEngine();
ASSERT(pDBEngine != NULL);
// Set default user:
DAO_CHECK(pDBEngine->put_DefaultUser(varUserName.bstrVal));
// Set default password:
DAO_CHECK(pDBEngine->put_DefaultPassword(varPassword.bstrVal));
}
更改用户的密码
若要更改用户的密码,请使用以下函数:
void ChangePassword(CString &strUserName,
CString &strOldPassword,
CString &strNewPassword)
{
// Create (open) a workspace
CDaoWorkspace wsp;
CString strWspName = _T("Temp Workspace");
wsp.Create(strWspName, strUserName, strOldPassword);
wsp.Append();
// Determine how many objects there are *// in the Users collection
short nUserCount;
short nCurrentUser;
DAOUser *pUser = NULL;
DAOUsers *pUsers = NULL;
// Side-effect is implicit OLE AddRef()
// on DAOUser object:
DAO_CHECK(wsp.m_pDAOWorkspace->get_Users(&pUsers));
// Side-effect is implicit OLE AddRef()
// on DAOUsers object
DAO_CHECK(pUsers->getcount(&nUserCount));
// Traverse through the list of users
// and change password for the userid
// used to create/open the workspace
for(nCurrentUser = 0; nCurrentUser <nUserCount; nCurrentUser++)
{
COleVariant varIndex(nCurrentUser, VT_I2);
COleVariant varName;
// Retrieve information for user nCurrentUser
DAO_CHECK(pUsers->get_Item(varIndex, &pUser));
// Retrieve name for user nCurrentUser
DAO_CHECK(pUser->get_Name(&V_BSTR(&varName)));
CString strTemp = V_BSTRT(&varName);
// If there is a match, change the password
if (strTemp == strUserName)
{
COleVariant varOldPwd(strOldPassword, VT_BSTRT);
COleVariant varNewPwd(strNewPassword, VT_BSTRT);
DAO_CHECK(pUser->NewPassword(V_BSTR(&varOldPwd),
V_BSTR(&varNewPwd)));
TRACE("\t Password is changed\n");
}
}
// Clean up: decrement the usage count
// on the OLE objects
pUser->Release();
pUsers->Release();
wsp.Close();
}
更改 .MDB 文件的密码
若要更改 .MDB 文件的密码,请使用以下函数:
void SetDBPassword(LPCTSTR pDB,
LPCTSTR pszOldPassword,
LPCTSTR pszNewPassword)
{
CDaoDatabase db;
CString strConnect(_T(";pwd="));
// the database must be opened as exclusive
// to set a password
db.Open(pDB, TRUE, FALSE, strConnect + pszOldPassword);
COleVariant NewPassword(pszNewPassword, VT_BSTRT),
OldPassword(pszOldPassword, VT_BSTRT);
DAO_CHECK(db.m_pDAODatabase->NewPassword(V_BSTR(&OldPassword),
V_BSTR(&NewPassword)));
db.Close();
}