VISUAL C++ 数据库开发-高级篇

在Visual C++中用ADO进行数据库编程

1. 生成应用程序框架并初始化OLE/COM库环境 

  创建一个标准的MFC AppWizard(exe)应用程序,然后在使用ADO数据库的InitInstance函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。
本例为: 
 BOOL CAdotestDlg::OnInitDialog()
 {
        ::CoInitialize(NULL); //初始化OLE/COM库环境 
  } 
  程序最后要调用 ::CoUninitialize();//释放程序占用的COM 资源。

  另外:
m_pRecordset->Close(); 注意!!!不要多次关闭!!!!!!!!!!!!
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL; 
  2. 引入ADO库文件 

  使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:
#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
  ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。 
    程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲突。 

  3.利用智能指针进行数据库操作 

  在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个ListCtrl。
 class CAdotestDlg : public CDialog
{
     _ConnectionPtr m_pConnection;
     _RecordsetPtr m_pRecordset;
   ClistCtrl m_List; 
     ......
}     
  ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。

  _ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。
  _CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。  _RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。 

  在使用ADO程序的事件响应中OnButton1加入以下代码: 
void CAdotestDlg::OnButton1() 
{
 m_List.ResetContent();
 m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指针
 m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指针
 try
 {
  m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据源
  //注意:这是连接不需要用户ID或密码的open 函数
  // 否则形式为 ->Open("DSN=test;uid=sa;pwd=123;","","",0); 
 
  // 执行SQL语句得到一个记录集把其指针赋值给m_pRecordset
  CString strSql="select * from middle";
  BSTR bstrSQL = strSql.AllocSysString(); 
  m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,
adCmdText); 
  //adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句
  while(!m_pRecordset->adoEOF)//遍历所有记录
  { 
   //取纪录字段值方式之一
   _variant_t TheValue; //VARIANT数据类型
   TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值
   if(TheValue.vt!=VT_NULL)
    m_List.AddString((char*)_bstr_t(TheValue));
   //将该值加入到列表控件中
 
   //取纪录字段值方式之二
   // _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value;
   // CString temp=TheValue1.copy();
   // m_List.AddString(temp);
   //数据类型转换
   _variant_t vUsername,vBirthday,vID,vOld;
   TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
   vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);

   m_pRecordset->MoveNext();//转到下一条纪录
  }
  m_pRecordset->Close();
  m_pConnection->Close();
 }
 catch (_com_error e)//异常处理
 {
  AfxMessageBox(e.ErrorMessage());
 }
 m_pRecordset->Close(); //注意!!!不要多次关闭!!!!否则会出错
 m_pConnection->Close();
 m_pRecordset = NULL;
 m_pConnection = NULL; 
} 

  程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用VARIANT数据类型容易得多。 

  好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。该程序将把表middle中的BIG_NAME字段值显示在列表控件中。

4.执行SQL命令并取得结果记录集

      为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
SQL命令的执行可以采用多种形式,下面我们一进行阐述。

  (1)利用Connection对象的Execute方法执行SQL命令

  Execute方法的原型如下所示:
     _RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected,
 long Options ) 
     其中CommandText是命令字串,通常是SQL命令。
 参数RecordsAffected是操作完成后所影响的行数, 
 参数Options表示CommandText中内容的类型,Options可以取如下值之一:
 adCmdText:表明CommandText是文本命令
 adCmdTable:表明CommandText是一个表名
 adCmdProc:表明CommandText是一个存储过程
 adCmdUnknown:未知
    Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。 
    _variant_t RecordsAffected;
    ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,
整形old,日期型birthday
    m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday 
DATETIME)",
   &RecordsAffected,
   adCmdText);
    ///往表格里面添加记录
    m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES 
(1, ''''Washington'''',25,''''1970/1/1'''')",&RecordsAffected,adCmdText);
    ///将所有记录old字段的值加一
    m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
    ///执行SQL统计命令得到包含记录条数的记录集
    m_pRecordset =  m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText);
    _variant_t vIndex = (long)0;
    _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
    上两句可以写成— _variant_t vCount = m_pRecordset->GetCollect((_variant_t)((long)0));
    m_pRecordset->Close();///关闭记录集
    CString message;
    message.Format("共有%d条记录",vCount.lVal);
    AfxMessageBox(message);///显示当前记录条数
(2)利用Command对象来执行SQL命令  
_CommandPtr m_pCommand;
m_pCommand.CreateInstance("ADODB.Command");
_variant_t vNULL;
vNULL.vt = VT_ERROR;
vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它
m_pCommand->CommandText = "SELECT * FROM users";///命令字串
m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集
  在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。 

  (3)直接用Recordset对象进行查询取得记录集

  实例—— 
void CGmsaDlg::OnDBSelect() 
{
    // TODO: Add your control notification handler code here
     _RecordsetPtr Rs1;  //定义Recordset对象
    _bstr_t Connect("DSN=GMS;UID=sa;PWD=;");//定义连接字符串
    _bstr_t Source ("SELECT count(*) FROM buaa.mdb010");  //要执行的SQL语句
    ::CoInitialize(NULL);    //初始化Rs1对象
        HRESUL hr = Rs1.CreateInstance( __uuidof( Recordset ) );
       //省略对返回值hr的判断 
     Rs1->Open( Source,
            Connect,
                adOpenForwardOnly,
                    adLockReadOnly,
            -1 ); 
    _variant_t temp=Rs1->GetCollect(_variant_t((long)0));
    CString strTemp=(char* )(_bstr_t)temp;
    MessageBox("OK!"+strTemp);
}
例如 
  m_pRecordset->Open("SELECT * FROM users",
  _variant_t((IDispatch *)m_pConnection,true),
  adOpenStatic,
  adLockOptimistic,
  adCmdText);
Open方法的原型是这样的:
HRESULT Recordset15::Open ( const _variant_t & Source, 
   const _variant_t & ActiveConnection, 
   enum CursorTypeEnum CursorType, 
   enum LockTypeEnum LockType, 
   long Options ) 
其中:
①Source是数据查询字符串
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) 
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
enum CursorTypeEnum
{
 adOpenUnspecified = -1,///不作特别指定
 adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方
式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
 adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操
作对你是可见的。
 adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。
 adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你
的记录集来说是不可见的。
};
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
enum LockTypeEnum
{
 adLockUnspecified = -1,///未指定
 adLockReadOnly = 1,///只读记录集
 adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制
 adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做
数据的更新、插入、删除等动作
 adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模
式下完成。
}; 
⑤Options可以取如下值之一:
 adCmdText:表明CommandText是文本命令
 adCmdTable:表明CommandText是一个表名
 adCmdProc:表明CommandText是一个存储过程
 adCmdUnknown:未知

5. 记录集的遍历、更新

      根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,
更改其年龄,保存到数据库。
_variant_t vUsername,vBirthday,vID,vOld;
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance("ADODB.Recordset");
m_pRecordset->Open("SELECT * FROM users",
  _variant_t((IDispatch*)m_pConnection,true),
  adOpenStatic,
  adLockOptimistic,
  adCmdText);
while(!m_pRecordset->adoEOF)
{
    vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,
    ///你也可以直接给出列的名称,如下一行
    vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
    vOld = m_pRecordset->GetCollect("old");
    vBirthday = m_pRecordset->GetCollect("birthday");
    ///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
    if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt
 != VT_NULL)
        TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
  vID.lVal,
  (LPCTSTR)(_bstr_t)vUsername,
  vOld.lVal,
  (LPCTSTR)(_bstr_t)vBirthday);
    m_pRecordset->MoveNext();///移到下一条记录
}
m_pRecordset->MoveFirst();///移到首条记录
m_pRecordset->Delete(adAffectCurrent);///删除当前记录
///添加三条新记录并赋值
for(int i=0;i<3;i++)
{
    m_pRecordset->AddNew();///添加新记录
    m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
    m_pRecordset->PutCollect("username",_variant_t("叶利钦"));
    m_pRecordset->PutCollect("old",_variant_t((long)71));
    m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));
}
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动
到第二条记录处
m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄
m_pRecordset->Update();///保存到库中    
备注:多次查询可把查询过程做成一个函数ExecuteSQL让m_pRecordset获得连接指针m_pConnection查询结果
void ExecuteSQL(_ConnectionPtr  m_pConnection, _RecordsetPtr  m_pRecordset,CString strSql)
{
    //执行Select 语句
    BSTR bstrSQL = strSql.AllocSysString();           
     try
     {
        m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,
adCmdText); 
            //adOpenDynamic:动态  adLockOptimistic乐观封锁法  adCmdText:文本查询语句
     }
     catch(_com_error error)
     {
        CString errorMessage;
        errorMessage.Format("%s",(LPTSTR)error.Description());
        AfxMessageBox(errorMessage);
     }
}        
//出错处理:
3127——没有找到目标表
3092——目标表已经存在
例如:
catch(const _com_error e)
{
     AfxMessageBox(e.Description());
     long errorCode=e.WCode();
     if(3127==errorCode) AfxMessageBox("表不存在");
     if(3092==errorCode) AfxMessageBox("表已经存在");
     return FALSE;
} 

在ODBC中应用DDX和RFX

b>MFC中ODBC类库简介

---- MFC 中 针 对ODBC 数 据 库 编 程 提 供 了 五 种 基 类。 这 些 类 封 装 了 有 关ODBC 的API 调 用, 使 用 户 能 够 利 用ODBC 完 成 不 同 类 型 的 数 据 库 编 程 工 作, 如 访 问Foxpro、dBASE 或Sybase 等 不 同 类 型 数 据 库 文 件, 从 而 避 开 各 种 类 型 数 据 库 文 件 的 复 杂 的 内 部 结 构。 这 五 种 基 类 是:

  • CDatabase 类 对 象 表 示 与 数 据 源 的 连 接。 用 户 正 是 基 于 此 连 接 实 现 对 数 据 源 的 操 作。 
  • CRecordset 类 对 象 表 示 了 从 数 据 源 中 选 出 的 一 组 记 录。 该 对 象 使 用 户 能 完 成 在 记 录 间 的 滚 动、 更 新 记 录、 对 记 录 进 行 过 滤 排 序 等 操 作。 
  • CRecordView 类 对 象 直 接 与 一CRecordset( 记 录 集) 类 对 象 连 接, 为 该 记 录 集 提 供 显 示 的 视 图。 它 是 数 据 库 操 作 的 界 面。 
  • CFieldExchange 类 支 持 数 据 库 的 字 段 交 换 过 程, 即RFX 机 制。 
  • CDBException 类 完 成 数 据 库 类 操 作 的 异 常 处 理。 用 户 可 根 据 其 中 公 用 成 员 变 量 的 取 值 来 分 析 出 现 错 误 的 原 因 或 显 示 出 相 应 的 文 本 信 息。

---- 以 下 我 们 利 用VC 中 的AppWizard 生 成 一 个 简 单 的 示 例 程 序 以 供 使 用:

---- 1. 选 择[File] 中 的[New], 在 弹 出 的 对 话 框 中 选 择 文 件 类 型 为[MFC AppWizard(EXE)]。 在[Project name] 中 键 入 文 件 名, 如:testodbc, 点 按[OK] 按 钮。

---- 2. 在 程 序 类 型 中 选 择[Single document], 点 按[Next] 按 钮。

---- 3. 在 数 据 库 支 持 中 选 择[Database view with file support], 点 按[Data source] 按 钮 进 入 数 据 库 选 择 对 话 框。

---- 4. 在[Datasource] 中 选 择[ODBC], 然 后 在 下 拉 列 表 中 选 择 [Visual Foxpro Tables]。[Recordset type] 设 定 为[Snapshot]。 点 按[OK] 按 钮 选 择 数 据 库 源 文 件:test.dbf。

---- 5. 择 了 数 据 库 源 文 件 后, 接 连 点 按[Next], 生 成 程 序 所 需 的 源 文 件。

---- 此 时 生 成 以 下 各 类 及 其 对 象:

  • CAboutDlg 
  • CMainFrame 
  • CTestodbcApp 
  • CTestodbcDoc 
  • CTestodbcSet 
  • CTestodbcView

RFX 简 介

---- RFX 是Record Field Exchange 的 缩 写, 意 即 记 录 字 段 数 据 交 换。 它 在 用 户 选 择 的 记 录 集(Data set) 和 隐 藏 于 后 台 的 数 据 源(Data source) 之 间 建 立 对 应 关 系, 使 用 户 能 通 过 操 作 此 记 录 集 来 实 现 对 数 据 源 的 操 作。MFC 中 提 供 了 一 系 列RFX 调 用 函 数, 通 过 这 些 函 数, 可 以 随 时 在 记 录 集 和 数 据 源 之 间 进 行 数 据 交 换, 这 种 交 换 是 双 向 的。 这 些 函 数 有:

 函 数 数 据 类 型
RFX_Bool BOOL
RFX_Byte BYTE
RFX_Binary CByteArray
RFX_Double double
RFX_Single float
RFX_Int int
RFX_Long lonig
RFX_LongBinary CLongBinary
RFX_Text CString
RFX_Date Ctime

---- 它 们 大 多 有 三 个 参 数( 个 别 会 有 四 或 五 个):

  • 一 个 指 向CFieldExchange 类 对 象 的 指 针; 
  • 数 据 源 中 的 一 个 字 段 名; 
  • 与 该 字 段 对 应 的 变 量 名。

---- 当 用AppWizard 生 成 代 码 时, 程 序 会 自 动 选 择 适 当 的 函 数 与 数 据 类 型 相 匹 配。

---- 记 录 字 段 数 据 交 换 的 核 心 是CRecordset 类 中 的 虚 函 数DoFieldExchange,RFX 函 数 都 在DoFieldExchange 中 调 用, 它 为RFX 函 数 提 供 了 一 个 指 向CFieldExchange 类 对 象 的 指 针, 其 原 型 为:

---- virtual void DoFieldExchange(CFieldExchang ? pFX);

---- 上 文 示 例 中 生 成 的CTestodbcSet 类 是 从CRecordset 派 生 而 来 的, 为 了 利 用RFX 机 制, 它 声 明 了 两 个 成 员 变 量:Cstring m_name 和Cstring m_age, 分 别 对 应 数 据 库 表 文 件 中 的[name] 和[age] 字 段。 在CTestodbcSet 类 重 载 的 方 法DoFieldExchange(CFieldExchange * pFX) 中, 可 以 看 到 调 用 了 两 个RFX 函 数:

---- RFX_Text(pFX, _T(“[name]”), m_name);

---- RFX_Text(pFX, _T(“[age]”), m_age);

---- pFX 参 数 即 为DoFieldExchange 传 递 来 的CFieldExchange 类 对 象 指 针。

DDX 简 介

---- DDX 是Dialog Data Exchange 的 缩 写, 意 即 对 话 框 数 据 交 换。 它 在 对 话 框 的 可 视 控 件(Controls) 和 成 员 变 量(Member variables) 之 间 建 立 双 向 的 对 应 关 系, 使 用 户 能 通 过 对 话 框 上 的 控 件 浏 览 和 修 改 变 量 的 取 值。

---- 类 似 记 录 字 段 数 据 交 换, 对 话 框 数 据 交 换 的 核 心 是CRecordView 类 中 的 虚 函 数DoDataExchange,DDX 函 数 都 在DoDataExchange 中 调 用, 它 为DDX 函 数 提 供 了 一 个 指 向CDataExchange 类 对 象 的 指 针, 其 原 型 为:

---- virtual void DoDataExchange(CDataExchan ? pDX);

---- 进 入 函 数Ctestodbc::DoDataExchange, 此 时 函 数 体 中 没 有 什 么 代 码, 因 为 我 们 并 没 有 在 对 话 框 中 创 建 控 件, 也 没 有 建 立DDX 机 制。

---- 打 开 示 例 程 序 的 对 话 框 资 源, 编 辑 自 动 生 成 的 对 话 框IDD_TESTODBC_FORM, 此 时, 该 对 话 框 中 仅 有 一 个 静 态 文 本 对 象“TODO: Place form controls on this dialog.”。 它 是 自 动 生 成 的 提 示 性 文 本, 表 示 可 在 此 对 话 框 上 添 加 控 件。 删 掉 此 文 本, 添 加 两 个 编 辑 框 对 象(Edit box)IDC_EDIT1 和IDC_EDIT2, 在IDC_EDIT1 上 按 鼠 标 右 键, 选 择 弹 出 菜 单 中 的[ClasWizard] 进 入 类 属 性 编 辑 对 话 框。 选 择[Member Variables] 属 性, 在[Control IDs] 中 选 择IDC_EDIT1, 然 后 按[Add Variable] 按 钮 以 选 择 与 它 对 应 的 变 量, 在 下 拉 列 表 框[Member Variable name] 中 选 择m_pSet -$#@62;m_name, 按[OK] 返 回。 用 同 样 方 法 为IDC_EDIT2 选 择m_pSet -$#@62;m_age。

---- 退 出 对 话 框 编 辑, 再 进 入 类CTestodbcView 源 文 件 中, 发 现 函 数DoDataExchange 中 增 加 了 两 个DDX 调 用 函 数:

---- DDX_FieldText(pDX, IDC_EDIT1, m_pSet -$#@62;m_name, m_pSet);

---- DDX_FieldText(pDX, IDC_EDIT2, m_pSet -$#@62;m_age, m_pSet);

---- DDX_FieldText 函 数 在 对 话 框 的 编 辑 控 件 和 记 录 的 字 段 之 间 建 立 联 系。 它 可 以 自 动 管 理 以 下 类 型 的 数 据:int、short、long、DWORD、Cstring、float、double、BOOL 和BYTE。 它 的 四 个 参 数 分 别 为:

  • 一 个 指 向CDataExchange 类 对 象 的 指 针; 
  • 与 数 据 交 换 相 关 的 对 话 框 控 件 的ID 号; 
  • 记 录 中 的 一 个 字 段; 
  • 与 数 据 交 换 相 关 的 记 录 集 对 象 指 针。

---- 在DDX 双 向 数 据 交 换 中, 用 户 不 直 接 调 用DoFieldExchange 函 数, 而 是 调 用 函 数UpdateData(BOOL direct), 此 函 数 的direct 参 数 决 定 了 数 据 交 换 的 方 向:

---- direct=FALSE 用 记 录 的 字 段 值 更 新 控 件 值

---- direct=TRUE 用 控 件 值 更 新 记 录 的 字 段 值

---- 例 如 我 们 在 对 话 框 中 添 加 一 个[Save] 按 钮, 在 它 的 事 件 处 理 过 程 中 将 控 件 的 值 保 存:

void CTestodbcView::OnButtonSave() 
{
.. .. ..
UpdateData(TRUE);
.. .. ..
}

DDX 和RFX 的 关 系 及 比 较

---- 从 上 面 可 以 看 出,RFX 是 数 据 库 编 程 中 数 据 交 换 的 内 部 基 础, 它 更 紧 密 地 与 记 录 集 对 象(CRecordset) 相 联 系, 是 隐 于 后 台 的;DDX 是 数 据 库 编 程 中 数 据 交 换 在 对 话 框 界 面 上 实 现 的 基 础, 它 更 紧 密 地 与 视 图 对 象(CRecordView) 相 联 系, 是 显 现 于 前 台 的。 最 后 我 们 用 一 幅 图 更 直 观 地 展 示 它 们 的 关 系: 

Visual C++多线程DAO处理

在DAO多线程处理中,有许多局限性,所以我设计了这么一个类,通过GUI线程来使用DAO的强制调用。在类中使用了GUI的消息队列,所有进入到CMultiDAORecordset的调用都被迫使用AfxGetThread()来检查当前的线程。GUI线程指针是放在InitInstance的首端,如果在GUI线程中,引入的调用请求不在运行,那么CMultiDAORecordSet就会发送一个WM_MULTIDAOMESSAGE消息给AfxGetMainWnd()(在Mainfrm.cpp中)。Mainfrm接受到这个消息,线程也就要再运行一次,这个时候,消息已经接受了,基类CDaoRecordset也就得到了调用。所以你的类是从CMultiDAORecordset继承的,而不是CDaoRecordset,如下: 

class CMySet : public CMultiDaoRecordSet

在相应的CPP文件中也应该改一下:

IMPLEMENT_DYNAMIC(CMySet, CMultiDaoRecordSet)

CMySet::CMySet (CDaoDatabase* pdb) : CMultiDaoRecordSet(pdb) 
为了处理接受到的WM_MULTIDAOMESSAGE消息,下面的代码还应该加在MainFrm中:

在MainFrm.h文件中加:
#ifdef MAINFRAME_CPP 
UINT WM_MULTIDAOMESSAGE = RegisterWindowMessage("WM_MULTIDAOMESSAGE"); 
#else 
extern UINT WM_MULTIDAOMESSAGE; 
#endif

afx_msg LONG OnMultiDaoMessage( UINT uParam, LONG lParam); 
在MainFrm.cpp文件中加:
#define MAINFRAME_CPP

#include "MutliDaoRecordset.h"

//added to the message map

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) 
//{{AFX_MSG_MAP(CMainFrame) 
ON_REGISTERED_MESSAGE(WM_MULTIDAOMESSAGE, OnMultiDaoMessage) 
END_MESSAGE_MAP()

//this function added 
LONG CMainFrame::OnMultiDaoMessage( UINT uParam, LONG lParam) 

//jtm 
//based on switch, perform operation... 
CMultiDaoRecordSet *pSet = (CMultiDaoRecordSet *)lParam; 
LONG lRet = 0;

CString cRet = ""; 
COleVariant cVar;

try 

//jtm-------debug-------------------------------------- 
CString cTraceMessage = cDAOMessageArray[uParam]; 
cTraceMessage += "\n"; 
TRACE(cTraceMessage); 
//jtm-------debug-------------------------------------- 

switch(uParam) 

case MultiDaoOpen: 
pSet->Open(); 
break; 
case MultiDaoClose: 
pSet->Close(); 
break; 
case MultiDaoIsOpen: 
lRet = (LONG)pSet->IsOpen(); 
break; 
case MultiDaoIsBOF: 
lRet = (LONG)pSet->IsBOF(); 
break; 
case MultiDaoIsEOF: 
lRet = (LONG)pSet->IsEOF(); 
break; 
case MultiDaoIsDeleted: 
lRet = (LONG)pSet->IsDeleted(); 
break; 
case MultiDaoIsFieldDirty: 
lRet = (LONG)pSet->IsFieldDirty(pSet->pParam1); 
break; 
case MultiDaoIsFieldNull: 
lRet = (LONG)pSet->IsFieldNull(pSet->pParam1); 
break; 
case MultiDaoIsFieldNullable: 
lRet = (LONG)pSet->IsFieldNullable(pSet->pParam1); 
break; 
case MultiDaoGetName: 
cRet = pSet->GetName(); 
lRet = (LONG)&cRet; 
break; 
case MultiDaoGetType: 
lRet = (LONG)pSet->GetType(); 
break; 
case MultiDaoGetEditMode: 
lRet = (LONG)pSet->GetEditMode(); 
break; 
case MultiDaoGetLastModifiedBookmark: 
cVar = pSet->GetLastModifiedBookmark(); 
lRet = (LONG)&cVar; 
break; 
case MultiDaoGetRecordCount: 
lRet = (LONG)pSet->GetRecordCount(); 
break; 
case MultiDaoMoveNext: 
pSet->MoveNext(); 
break; 
case MultiDaoMovePrev: 
pSet->MovePrev(); 
break; 
case MultiDaoMoveFirst: 
pSet->MoveFirst(); 
break; 
case MultiDaoMoveLast: 
pSet->MoveLast(); 
break; 
case MultiDaoMove: 
pSet->Move(*(LONG *)pSet->pParam1); 
break; 
case MultiDaoFindNext: 
lRet = (LONG)pSet->FindNext(*(LPCTSTR *)pSet->pParam1); 
break; 
case MultiDaoFindPrev: 
lRet = (LONG)pSet->FindPrev(*(LPCTSTR*)pSet->pParam1); 
break; 
case MultiDaoFindFirst: 
lRet = (LONG)pSet->FindFirst(*(LPCTSTR *)pSet->pParam1); 
break; 
case MultiDaoFindLast: 
lRet = (LONG)pSet->FindLast(*(LPCTSTR *)pSet->pParam1); 
break; 
case MultiDaoFind: 
lRet = (LONG)pSet->Find(*(LONG *)pSet->pParam1, *(LPCTSTR*)pSet->pParam2); 
break; 
case MultiDaoGetBookmark: 
cVar = pSet->GetBookmark(); 
lRet = (LONG)&cVar; 
break; 
case MultiDaoSetBookmark: 
pSet->SetBookmark(*(COleVariant*)pSet->pParam1); 
break; 
case MultiDaoAddNew: 
pSet->AddNew(); 
break; 
case MultiDaoEdit: 
pSet->Edit(); 
break; 
case MultiDaoUpdate: 
pSet->Update(); 
break; 
case MultiDaoDelete: 
pSet->Delete(); 
break; 
case MultiDaoCancelUpdate: 
pSet->CancelUpdate(); 
break; 
case MultiDaoRequery: 
pSet->Requery(); 
break; 


catch (CDaoException *e) 

TRACE("Database Multithread Operation Failed%s\n", 
e->m_pErrorInfo->m_strDescription); 

return lRet; 
}

下面的代码是加在对应的应用程序对象的头文件中的:
public:
CWinThread *pGUIThread;

And this to the constructor in the app .cpp file:


CMyApp::CMyApp()
{
pGUIThread = AfxGetThread();
}

  如果没有上面的定义的话,你的应用程序就会出问题。整个DAO公共函数并没有全部实现,但不管怎么样,上面的函数是保护CDaoRecordset的使用的。如果你要加个函数的话,加进去就行了,你也可以用一个对话框来显示错误信息,它们都应该在MultiDaorecordset.h中被定义。为了支持DAO多线程的类,下面的代码也应该加进去:

加在Multidaorecordset.cpp中:
// MultiDaoRecordSet.cpp : implementation file 
//

#define MULTIDAORECORDSET_CPP

#include "stdafx.h"

#include "MyApp.h"

#include "MultiDaoRecordSet.h"

#include "mainfrm.h"

#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif


// CMultiDaoRecordSet

IMPLEMENT_DYNAMIC(CMultiDaoRecordSet, CDaoRecordset)

CMultiDaoRecordSet::CMultiDaoRecordSet(CDaoDatabase* pdb) 
: CDaoRecordset(pdb) 

//{{AFX_FIELD_INIT(CMultiDaoRecordSet) 
//}}AFX_FIELD_INIT 
m_nDefaultType = dbOpenDynaset; 
}

//jtm 
//thread safe destructor.... 
CMultiDaoRecordSet::~CMultiDaoRecordSet() 

if (IsOpen()) 
Close();

// Clean up database if necessary 
if (m_pDatabase != NULL && (m_nStatus & AFX_DAO_IMPLICIT_DB)) 

m_pDatabase->Close(); 
delete m_pDatabase; 
m_pDatabase = NULL; 

}

CString CMultiDaoRecordSet::GetDefaultDBName() 

CMyApp *pApp = ((CMYApp *)AfxGetApp()); 
return pApp->GetDatabaseFullPath(); 
}


// CMultiDaoRecordSet diagnostics

//jtm 
//multi thread safe functoins 
void CMultiDaoRecordSet::Open(int nOpenType, LPCTSTR lpszSQL, int nOptions) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Open(nOpenType, lpszSQL, nOptions); 

else 

pParam1 = (void *)&nOpenType; 
pParam2 = (void *)lpszSQL; 
pParam3 = (void *)&nOptions; 
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoOpen, (LPARAM)this); 

}

void CMultiDaoRecordSet::Close() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Close(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoClose, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsOpen() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsOpen(); 

else 

return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsOpen, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsBOF() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsBOF(); 

else 

return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsBOF, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsEOF() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsEOF(); 

else 

return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsEOF, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsDeleted() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsDeleted(); 

else 

return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsDeleted, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsFieldDirty(void* pv) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsFieldDirty(pv); 

else 

pParam1 = pv; 
return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldDirty, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsFieldNull(void* pv) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsFieldNull(pv); 

else 

pParam1 = pv; 
return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldNull, (LPARAM)this); 

}

BOOL CMultiDaoRecordSet::IsFieldNullable(void* pv) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::IsFieldNullable(pv); 

else 

pParam1 = pv; 
return (BOOL)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoIsFieldNullable, (LPARAM)this); 

}

CString CMultiDaoRecordSet::GetName() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetName(); 

else 

return (CString)*(CString *)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetName, (LPARAM)this); 

}

short CMultiDaoRecordSet::GetType() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetType(); 

else 

return (short)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetType, (LPARAM)this); 

}

short CMultiDaoRecordSet::GetEditMode() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetEditMode(); 

else 

return (short)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetEditMode, (LPARAM)this); 

}

CString CMultiDaoRecordSet::GetSQL() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetSQL(); 

else 

return (CString)*(CString *)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetSQL, (LPARAM)this); 

}

COleVariant CMultiDaoRecordSet::GetLastModifiedBookmark() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetLastModifiedBookmark(); 

else 

return (COleVariant)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetLastModifiedBookmark, (LPARAM)this); 

}

long CMultiDaoRecordSet::GetRecordCount() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetRecordCount(); 

else 

return (long)AfxGetMainWnd()-> 
SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetRecordCount, (LPARAM)this); 

}

void CMultiDaoRecordSet::MoveNext() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::MoveNext(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveNext, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::MovePrev() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::MovePrev(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMovePrev, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::MoveFirst() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::MoveFirst(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveFirst, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::MoveLast() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::MoveLast(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMoveLast, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::Move(long lRows) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Move(lRows); 

else 

pParam1 = (void *)&lRows; 
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoMove, 
(LPARAM)this); 

}

BOOL CMultiDaoRecordSet::FindNext(LPCTSTR lpszFilter) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::FindNext(lpszFilter); 

else 

pParam1 = (void *)lpszFilter; 
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindNext, 
(LPARAM)this); 

}

BOOL CMultiDaoRecordSet::FindPrev(LPCTSTR lpszFilter) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::FindPrev(lpszFilter); 

else 

pParam1 = (void *)lpszFilter; 
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindPrev, 
(LPARAM)this); 

}

BOOL CMultiDaoRecordSet::FindFirst(LPCTSTR lpszFilter) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::FindFirst(lpszFilter); 

else 

pParam1 = (void *)lpszFilter; 
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindFirst, 
(LPARAM)this); 

}

BOOL CMultiDaoRecordSet::FindLast(LPCTSTR lpszFilter) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::FindLast(lpszFilter); 

else 

pParam1 = (void *)lpszFilter; 
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFindLast, 
(LPARAM)this); 

}

BOOL CMultiDaoRecordSet::Find(long lFindType, LPCTSTR lpszFilter) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::Find(lFindType, lpszFilter); 

else 

pParam1 = (void *)&lFindType; 
pParam2 = (void *)lpszFilter; 
return (BOOL)AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoFind, 
(LPARAM)this); 

}

COleVariant CMultiDaoRecordSet::GetBookmark() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

return CDaoRecordset::GetBookmark(); 

else 

return AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoGetBookmark, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::SetBookmark(COleVariant varBookmark) 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::SetBookmark(varBookmark); 

else 

pParam1 = (void *)&varBookmark; 
AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoSetBookmark, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::AddNew() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::AddNew(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoAddNew, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::Edit() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Edit(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoEdit, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::Update() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Update(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoUpdate, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::Delete() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Delete(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoDelete, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::CancelUpdate() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::CancelUpdate(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoCancelUpdate, 
(LPARAM)this); 

}

void CMultiDaoRecordSet::Requery() 

CMYApp *pApp = ((CMYApp *)AfxGetApp()); 
if (pApp->pGUIThread == AfxGetThread()) 

CDaoRecordset::Requery(); 

else 

AfxGetMainWnd()->SendMessage(WM_MULTIDAOMESSAGE, MultiDaoRequery, 
(LPARAM)this); 

}

and, Multidaorecordset.h


#if !defined(AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_)
#define AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// MultiDaoRecordSet.h : header file
//

/
// CMultiDaoRecordSet DAO recordset

enum {
MultiDaoOpen, 
MultiDaoClose, 
MultiDaoIsOpen,
MultiDaoIsBOF,
MultiDaoIsEOF,
MultiDaoIsDeleted,
MultiDaoIsFieldDirty,
MultiDaoIsFieldNull,
MultiDaoIsFieldNullable,
MultiDaoGetName,
MultiDaoGetType,
MultiDaoGetSQL,
MultiDaoGetEditMode,
MultiDaoGetLastModifiedBookmark,
MultiDaoGetRecordCount,
MultiDaoMoveNext,
MultiDaoMovePrev,
MultiDaoMoveFirst,
MultiDaoMoveLast,
MultiDaoMove,
MultiDaoFindNext,
MultiDaoFindPrev,
MultiDaoFindFirst,
MultiDaoFindLast,
MultiDaoFind,
MultiDaoGetBookmark,
MultiDaoSetBookmark,
MultiDaoAddNew,
MultiDaoEdit,
MultiDaoUpdate,
MultiDaoDelete,
MultiDaoCancelUpdate,
MultiDaoRequery,
};

#ifdef MULTIDAORECORDSET_CPP
CString cDAOMessageArray[] = 
{
"MultiDaoOpen", 
"MultiDaoClose", 
"MultiDaoIsOpen",
"MultiDaoIsBOF",
"MultiDaoIsEOF",
"MultiDaoIsDeleted",
"MultiDaoIsFieldDirty",
"MultiDaoIsFieldNull",
"MultiDaoIsFieldNullable",
"MultiDaoGetName",
"MultiDaoGetType",
"MultiDaoGetSQL",
"MultiDaoGetEditMode",
"MultiDaoGetLastModifiedBookmark",
"MultiDaoGetRecordCount",
"MultiDaoMoveNext",
"MultiDaoMovePrev",
"MultiDaoMoveFirst",
"MultiDaoMoveLast",
"MultiDaoMove",
"MultiDaoFindNext",
"MultiDaoFindPrev",
"MultiDaoFindFirst",
"MultiDaoFindLast",
"MultiDaoFind",
"MultiDaoGetBookmark",
"MultiDaoSetBookmark",
"MultiDaoAddNew",
"MultiDaoEdit",
"MultiDaoUpdate",
"MultiDaoDelete",
"MultiDaoCancelUpdate",
"MultiDaoRequery",
};
#else
extern CString cDAOMessageArray[]; 
#endif

class CMultiDaoRecordSet : public CDaoRecordset
{
public:

//jtm
//FORCE user to pass database...
CMultiDaoRecordSet(CDaoDatabase* pDatabase);

~CMultiDaoRecordSet();

DECLARE_DYNAMIC(CMultiDaoRecordSet)

// Field/Param Data
//{{AFX_FIELD(CMultiDaoRecordSet, CDaoRecordset)
//}}AFX_FIELD

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMultiDaoRecordSet)
public:
virtual CString GetDefaultDBName(); // Default database name
//}}AFX_VIRTUAL

// Implementation
//jtm
//redefined public functions
//Note: I am only redefining SOME
//of the public functions this is NOT
//a FULL implementation !!!!!!
public:
//jtm -- only supporting this open.....
virtual void Open(int nOpenType = AFX_DAO_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL = NULL, int nOptions = 0);
virtual void Close();
BOOL IsOpen();
BOOL IsBOF();
BOOL IsEOF();
BOOL IsDeleted();
BOOL IsFieldDirty(void* pv);
BOOL IsFieldNull(void* pv);
BOOL IsFieldNullable(void* pv);
CString GetName();
short GetType();
short GetEditMode();
CString GetSQL();
COleVariant GetLastModifiedBookmark();
long GetRecordCount(); 
void MoveNext();
void MovePrev();
void MoveFirst();
void MoveLast();
virtual void Move(long lRows); 
BOOL FindNext(LPCTSTR lpszFilter);
BOOL FindPrev(LPCTSTR lpszFilter);
BOOL FindFirst(LPCTSTR lpszFilter);
BOOL FindLast(LPCTSTR lpszFilter);
virtual BOOL Find(long lFindType, LPCTSTR lpszFilter);
COleVariant GetBookmark();
void SetBookmark(COleVariant varBookmark); 
virtual void AddNew();
virtual void Edit();
virtual void Update();
virtual void Delete();
virtual void CancelUpdate(); 
virtual void Requery();

void *pParam1;
void *pParam2;
void *pParam3;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the //previous line.

#endif // !defined(AFX_MULTIDAORECORDSET_H__BECC8DC3_A967_11D2_BA4C_006097808646__INCLUDED_)

  调试的东西等你搞好你的程序后就可以删了,不过要注意,不要直接调用CDaoRecordset,否则,你的应用程序就会问题。我试了一下,使用这个类的时候,你的计算机的速度会稍微的暂停几秒钟(正在运行你的程序),不过这不会影响到你的计算机的使用的,因为这个类DAO的调用是通过GUI的,如果你使用不当的话(比如临界区问题)就会导致计算机的死锁。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编 者 的 话 5 第1 基础 6 第1章 数据库原理与访问 7 1.1 数据库基本原理 7 1.1.1 概述 7 1.1.2 桌面数据库 7 1.1.3 对象数据库 8 1.1.4 关系数据库服务器 9 1.1.5 选择适用的数据库 9 1.2 数据库访问技术 10 1.2.1 概述 10 1.2.2 ODBC API 10 1.2.3 ODBC的MFC类 11 1.2.4 DAO与RDO 11 1.2.5 OLE DB与ADO 12 1.3 数据库操纵语言SQL 13 1.3.1 SQL命令 13 1.3.2 SQL从句 13 1.3.3 SQL运算符 14 1.3.4 SQL合计函数 14 1.4 小 结 14 第2章 COM与数据库访问 15 2.1 COM的基本原理 15 2.1.1 COM历史 16 2.1.2 COM结构 16 2.1.3 COM优势 17 2.1.4 COM接口 18 2.1.5 COM与数据库访问 19 2.1.6 COM与Internet 19 2.2 ACTIVEX的数据库访问 19 2.2.1 ActiveX简介 19 2.2.2 ActiveX对数据库访问的支持 20 2.3 ATL的数据库访问 20 2.3.1 ATL目标 20 2.3.2 ATL内容简介 22 2.3.3 ATL对数据库访问的支持 22 2.4 小 结 23 第3章 数据库开发过程 23 3.1 阶段1:调查与分析 24 3.2 阶段2:数据建模 24 3.3 阶段3:功能设计 24 3.4 阶段4:选择数据库系统 25 3.5 阶段5:选择数据库访问技术 25 3.6 阶段6:代码设计 25 3.7 阶段7:测试与调试 26 3.8 阶段8:发行产品 26 第4章 VC++数据库开发基础 26 4.1 VC++ 6.0工程创建向导 26 4.2 VC++ 6.0数据库新建工具 27 4.3 VC++ 6.0的数据库工程 29 4.4 小 结 31 第2 实例 32 第5章 ODBC API编程 33 5.1 了解ODBC API 34 5.2 ODBC API编程步骤 34 5.2.1 步骤1:连接数据源 34 5.2.2 步骤2:分配语句句柄 36 5.2.3 步骤3:准备并执行SQL语句 36 5.2.4 步骤4:获取结果集 37 5.2.5 步骤5:提交事务 38 5.2.6 步骤6:断开数据源连接并释放环境句柄 39 5.3 ODBC API编程实例 39 5.3.1 实例概述 39 5.3.2 实例实现过程 40 5.3.3 编译并运行ODBCDemo1工程 97 5.3.4 ODBCDemo1实例小结 98 5.4 本 章 小 结 99 第6章 MFC ODBC编程 100 6.1 了解MFC ODBC 100 6.1.1 CDatabase类 100 6.1.2 CRecordSet类 100 6.2 MFC ODBC数据库访问技术 101 6.2.1 记录查询 101 6.2.2 记录添加 102 6.2.3 记录删除 102 6.2.4 记录修改 102 6.2.5 撤销数据库更新操作 103 6.2.6 直接执行SQL语句 103 6.2.7 MFC ODBC的数据库操作过程 103 6.3 MFC ODBC编程实例 104 6.3.1 实例概述 104 6.3.2 实例实现过程 105 6.3.3 编译并运行ODBCDemo2工程 132 6.3.4 ODBCDemo2实例小结 137 6.4 本 章 小 结 137 第7章 DAO数据库编程 138 7.1 DAO的数据访问 138 7.1.1 DAO对象 138 7.1.2 MFC对DAO的支持 139 7.1.3 DAO与ODBC的比较 139 7.1.4 MFC的DAO类简介 139 7.2 DAO编程实例 142 7.2.1 实例概述 142 7.2.2 实例实现过程 143 7.2.3 运行DAODemo工程 167 7.2.4 DAODemo实例小结 171 7.3 小 结 172 第8章 OLE DB客户数据库编程 172 8.1 OLE DB原理 172 8.1.1 OLE DB与ODBC 172 8.1.2 OLE DB的结构 173 8.1.3 OLE DB的优越性 173 8.1.4 OLE DB对象 174 8.1.5 OLE DB客户模板结构 177 8.1.6 OLE DB客户模

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值