松达婴儿护肤山茶油多少钱:关于VC ado数据库的整理

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 01:45:49

//***************************************************************终于解决所需要的功能了,修成正果半只网上找了好多ado操作access的东西, 很多都是一小丢丢的, 极其的零碎, 贴一些出来, 希望对初学的人有些帮助, 不保证没有问题, 仅供参考, 俺可不承担任何事故责任-_-;;;
头文件#导入什么什么东西...
#include
#import "C:\Program Files\Common Files\System\ado\msadox.dll" rename_namespace("ADOX") \
rename("_Collection", "_CollectionX")\
rename("_DynaCollection", "_DynaCollectionX")\
rename("DataTypeEnum", "DataTypeEnumX")\
rename("Properties", "PropertiesX")\
rename("Property", "PropertyX")
#import "C:\Program Files\Common Files\System\ado\msado15.dll"\
no_namespace\
rename( "EOF", "adoEOF" )\
_ConnectionPtr m_pConnection;=================================================cpp
CoInitialize( NULL );//好像是初始化com
CoUninitialize();卸载
void dump_com_error(_com_error &e )//异常处理
{
TCHAR errtext[4096];
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
_tsprintf( errtext, _T("CADORecordset Error\n\tCode = %08lx\n\tCode meaning = %s\n\tSource = %s\n\tDescription = %s\n"),
   e.Error(), e.ErrorMessage(), (LPTSTR)bstrSource, (LPTSTR)bstrDescription );
DWORD dwErr;
dwErr = e.Error();
TCHAR errcaption[255];
_tsprintf( errcaption, _T("Err : %08X | %u"), dwErr, dwErr );MessageBox( NULL, errtext, errcaption, MB_OK | MB_ICONERROR );
}//连接access
m_pConnection.CreateInstance( __uuidof( Connection ) );// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。
try
{
   // 打开本地Access库Demo.mdb
   _tstring strFilePath = _T("Provider=Microsoft.ACE.OLEDB.12.0;Data Source="); // 2000 Provider=Microsoft.Jet.OLEDB.4.0 2007 Provider=Microsoft.ACE.OLEDB.12.0
   strFilePath += m_szFilepath;
   m_pConnection->Open(
    strFilePath.c_str(),
    _T(""),
    _T(""),
    adModeUnknown
    );   return true;
}
catch(_com_error e)
{
   dump_com_error( e );
   return false;
}//执行sql,_RecordsetPtr DoExecute( TCHAR * _CmdText )
{
if ( m_pConnection == NULL )
   return NULL;
_RecordsetPtr pRecordset;
pRecordset.CreateInstance(__uuidof(Recordset));
_variant_t RecordsAffected;
try
{
   pRecordset = m_pConnection->Execute( _CmdText, &RecordsAffected, adCmdText ); // 注意pRecordset 只是个记录集 不可以用来修改里面的内容, 如果进行操作会出异常提示, 具体提示忘记了...
   return pRecordset;
}
catch ( _com_error &e )
{
   dump_com_error( e );
   return NULL;
}
}//ado执行sql指令
_RecordsetPtr pRecordset;
pRecordset.CreateInstance(__uuidof(Recordset));==========================================try
{
   pRecordset->Open(dest,              
    m_pConnection.GetInterfacePtr(),
    adOpenDynamic,
    adLockOptimistic,
    adCmdText);
   return pRecordset;//这个pRecordset可以修改增删里面的内容
}
catch ( _com_error &e )
{
   dump_com_error( e );
   return NULL;
}//遍历执行指令后得到的记录集上面得到的 pRecordset 都管这东西叫 只能指针, 调试的时候里面的东西啥都看不到, 太高级了.常用的pRecordset里面的东西pRecordset->adoEOF 当前所指向的记录是否指向最后, 用的时候有点像 map里 iterator != map.end() 那样对于当前所指向的记录操作有pRecordset->MoveFirst();//指向第一条记录pRecordset->MoveNext();//指向下一条, 循环的时候用 就像 iterator++pRecordset->Delete( adAffectCurrente ); //删除当前指向的记录 参数adAffectCurrente 可以直接查看头文件pRecordset->AddNew();//在当前位置插入一条新纪录, 此时记录为空pRecordset->Update();//将当前操作更新到access, 有重载 用来更新当前指向记录的某个字段 参数是两个_variant_t, 第一个字段名 第二个是值 _variant_t看起来复杂,用起来很简单pRecordset->Fields;//好像是当前指向的记录, 目前还没有对它做过什么东西pRecordset->Fields->Item[Index];//一条记录中列,用long做索引, 一定要用long, 用其他类型做索引会报异常pFields->Item[j]->PutValue( _variant_t ); //写入数据使用过pRecordset以后记得要pRecordset->Close(); 还有个pRecordset->Release() 看起来也是释放, 但是以调用就出错, 看了别人也提出这个问题, 好多人说close就行, 姑且先这样吧, 具体原因有空再看//开始遍历
   while ( !pRecordset->adoEOF )
   {
     //do somthing
    pRecordset->MoveNext();
   }==============================获取所有表
    _RecordsetPtr table = m_pConnection->OpenSchema(adSchemaTables);//枚举表的名称处理
    if ( table == NULL )
     return NULL;
    while(!(table->adoEOF /*EndOfFile*/))
    {
     _variant_t table_name = table->Fields->GetItem(_T("TABLE_NAME"))->Value;//获取表的名称
     _variant_t table_type = table->Fields->GetItem(_T("TABLE_TYPE"))->Value;//获取表的类型
     if( _tstrcmp( table_type.bstrVal, _T("TABLE") ) == 0 )
     {
      values[table_name.bstrVal]   = column;
      datatype[table_name.bstrVal] = type;
     }
     table->MoveNext();
    }
    table->Close();//获取所有字段名
_RecordsetPtr m_pRecordset = DoOpen( _T("SELECT * FROM [%s]"), tablename );//DoOpen就是pRecordset ->Open( ... );try
{
   FieldsPtr fields = m_pRecordset->GetFields();
   if ( fields == NULL )
    return 0;   long count = fields->GetCount();   _bstr_t collect_name;
   DataTypeEnum vt;
   long attribute;
   for ( long i = 0; i < count; i++ )
   {
    collect_name = fields->Item[i]->GetName();
    column.push_back( (TCHAR*)collect_name );
    vt = fields->Item[i]->GetType();
    attribute = fields->Item[i]->Attributes;
    datatype.push_back( vt );
   }
   fields->Release();
   m_pRecordset->Close();
}
catch ( _com_error &e )
{
   dump_com_error( e );
}
添加删除字段, 找了一整天用ado的方法未果, 还是用sql了ALTER TABLE [tablename] ADD COLUMN [字段名] 类型 NULL //添加 常用类型Integer date textALTER TABLE [tablename] DROP COLUMN [字段名] // 删除字段用[]是因为懒得去找sql的保留字
顺便看了一下select语句的返回记录数量和排序的问题, 发现有好多人使用类似这条语句SELECT TOP 10 * FROM [users] WHERE regist <= #2009-7-10 11:26:08# ORDER BY regist desc结果却返回超过10条记录,如果修改一下SELECT TOP 10 * FROM [users] WHERE regist <= #2009-7-17 11:26:08# ORDER BY regist desc, id asc结果就是10条, 理解是 有多条记录的 regist字段内数据都符合查询条件且regist字段的数据完全一样,所以出现这种现象 在后面增加一个排序条件后 会对regist字段完全相同的记录再次排序, 莫非是冒泡套着用了-_-#, 所以想用像regist这种可相同数据的字段排序 后面最好再加一个条件, 效果会好很多还有sql语句里面的时间/日期要用#包括, 而添加或者更新时间日期的时候不要#好像就这么多了, 需要的功能都差不多了,该下一步了
//================================================================
VC通过ADO操作Access数据库[转]2008-12-11 16:58  在示例程序中我们仍采用原库结构,数据库名Demo.mdb,库内表名DemoTable,表内字段名为Name(姓名)和Age(年龄)的两个字段,来构造示例程序操作所需的Access数据库,这也和上两篇文章的示例源码中的库结构相兼容。
下面让我们看看ADO数据库访问技术使用的基本步骤及方法:
      首先,要用#import语句来引用支持ADO的组件类型库(*.tlb),其中类型库可以作为可执行程序(DLL、EXE等)的一部分被定位在其自身程序中的附属资源里,如:被定位在msado15.dll的附属资源中,只需要直接用#import引用它既可。可以直接在Stdafx.h文件中加入下面语句来实现:
#import "c:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename ("EOF", "adoEOF")
其中路径名可以根据自己系统安装的ADO支持文件的路径来自行设定。当编译器遇到#import语句时,它会为引用组件类型库中的接口生成包装类,#import语句实际上相当于执行了API涵数LoadTypeLib()。#import语句会在工程可执行程序输出目录中产生两个文件,分别为*.tlh(类型库头文件)及*.tli(类型库实现文件),它们分别为每一个接口产生智能指针,并为各种接口方法、枚举类型,CLSID等进行声明,创建一系列包装方法。语句no_namespace说明ADO对象不使用命名空间,rename ("EOF", "adoEOF")说明将ADO中结束标志EOF改为adoEOF,以避免和其它库中命名相冲突。
    其次,在程序初始过程中需要初始化组件,一般可以用CoInitialize(NULL);来实现,这种方法在结束时要关闭初始化的COM,可以用下面语句CoUnInitialize();来实现。在MFC中还可以采用另一种方法来实现初始化COM,这种方法只需要一条语句便可以自动为我们实现初始化COM和结束时关闭COM的操作,语句如下所示: AfxOleInit();
    接着,就可以直接使用ADO的操作了。我们经常使用的只是前面用#import语句引用类型库时,生成的包装类.tlh中声明的智能指针中的三个,它们分别是_ConnectionPtr、_RecordsetPtr和_CommandPtr。下面分别对它们的使用方法进行介绍:
1、_ConnectionPtr智能指针,通常用于打开、关闭一个库连接或用它的Execute方法来执行一个不返回结果的命令语句(用法和_CommandPtr中的Execute方法类似)。
——打开一个库连接。先创建一个实例指针,再用Open打开一个库连接,它将返回一个IUnknown的自动化接口指针。代码如下所示:
_ConnectionPtr m_pConnection;
// 初始化COM,创建ADO连接等操作
AfxOleInit();
m_pConnection.CreateInstance(__uuidof(Connection));
// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。jingzhou xu
try
{
// 打开本地Access库Demo.mdb
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Demo.mdb","","",adModeUnknown);
}
catch(_com_error e)
{
AfxMessageBox("数据库连接失败,确认数据库Demo.mdb是否在当前路径下!");
return FALSE;
}
——关闭一个库连接。如果连接状态有效,则用Close方法关闭它并赋于它空值。代码如下所示:
if(m_pConnection->State)
m_pConnection->Close();
m_pConnection= NULL;
2、_RecordsetPtr智能指针,可以用来打开库内数据表,并可以对表内的记录、字段等进行各种操作。
——打开数据表。打开库内表名为DemoTable的数据表,代码如下:
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));
// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。jingzhou xu
try
{
m_pRecordset->Open("SELECT * FROM DemoTable",                // 查询DemoTable表中所有字段
theApp.m_pConnection.GetInterfacePtr(),  // 获取库接库的IDispatch指针
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——读取表内数据。将表内数据全部读出并显示在列表框内,m_AccessList为列表框的成员变量名。如果没有遇到表结束标志adoEOF,则用GetCollect(字段名)或m_pRecordset->Fields->GetItem(字段名)->Value方法,来获取当前记录指针所指的字段值,然后再用MoveNext()方法移动到下一条记录位置。代码如下所示:
_variant_t var;
CString strName,strAge;
try
{
if(!m_pRecordset->BOF)
m_pRecordset->MoveFirst();
else
{
AfxMessageBox("表内数据为空");
return;
}
// 读入库中各字段并加入列表框中
while(!m_pRecordset->adoEOF)
{
var = m_pRecordset->GetCollect("Name");
if(var.vt != VT_NULL)
strName = (LPCSTR)_bstr_t(var);
var = m_pRecordset->GetCollect("Age");
if(var.vt != VT_NULL)
strAge = (LPCSTR)_bstr_t(var);
m_AccessList.AddString( strName + " --> "+strAge );
m_pRecordset->MoveNext();
}
// 默认列表指向第一项,同时移动记录指针并显示
m_AccessList.SetCurSel(0);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——插入记录。可以先用AddNew()方法新增一个空记录,再用PutCollect(字段名,值)输入每个字段的值,最后再Update()更新到库中数据既可。其中变量m_Name和m_Age分别为姓名及年龄编辑框的成员变量名。代码所下所示:
try
{
// 写入各字段值
m_pRecordset->AddNew();
m_pRecordset->PutCollect("Name", _variant_t(m_Name));
m_pRecordset->PutCollect("Age", atol(m_Age));
m_pRecordset->Update();
AfxMessageBox("插入成功!");
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——移动记录指针。移动记录指针可以通过MoveFirst()方法移动到第一条记录、MoveLast()方法移动到最后一条记录、MovePrevious()方法移动到当前记录的前一条记录、MoveNext()方法移动到当前记录的下一条记录。但我们有时经常需要随意移动记录指针到任意记录位置时,可以使用Move(记录号)方法来实现,注意: Move()方法是相对于当前记录来移动指针位置的,正值向后移动、负值向前移动,如:Move(3),当前记录是3时,它将从记录3开始往后再移动3条记录位置。代码如下所示:
try
{
int curSel = m_AccessList.GetCurSel();
// 先将指针移向第一条记录,然后就可以相对第一条记录来随意移动记录指针
m_pRecordset->MoveFirst();
m_pRecordset->Move(long(curSel));
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——修改记录中字段值。可以将记录指针移动到要修改记录的位置处,直接用PutCollect(字段名,值)将新值写入并Update()更新数据库既可。可以用上面方法移动记录指针,修改字段值代码如下所示:
try
{
// 假设对第二条记录进行修改
m_pRecordset->MoveFirst();
m_pRecordset->Move(1);        // 从0开始
m_pRecordset->PutCollect("Name", _variant_t(m_Name));
m_pRecordset->PutCollect("Age", atol(m_Age));
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——删除记录。删除记录和上面修改记录的操作类似,先将记录指针移动到要修改记录的位置,直接用Delete()方法删除它并用Update()来更新数据库既可。代码如下所示:
try
{
// 假设删除第二条记录
m_pRecordset->MoveFirst();
m_pRecordset->Move(1);        // 从0开始
m_pRecordset->Delete(adAffectCurrent);  // 参数adAffectCurrent为删除当前记录
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
——关闭记录集。直接用Close方法关闭记录集并赋于其空值。代码如下所示:
m_pRecordset->Close();
m_pRecordset = NULL;
3、CommandPtr智能指针,可以使用_ConnectionPtr或_RecordsetPtr来执行任务,定义输出参数,执行存储过程或SQL语句。
——执行SQL语句。先创建一个_CommandPtr实例指针,再将库连接和SQL语句做为参数,执行Execute()方法既可。代码如下所示:
_CommandPtr  m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection;  // 将库连接赋于它
m_pCommand->CommandText = "SELECT * FROM DemoTable";  // SQL语句
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText); // 执行SQL语句,返回记录集
——执行存储过程。执行存储过程的操作和上面执行SQL语句类似,不同点仅是CommandText参数中不再是SQL语句,而是存储过程的名字,如Demo。另一个不同点就是在Execute()中参数由adCmdText(执行SQL语句),改为adCmdStoredProc来执行存储过程。如果存储过程中存在输入、输出参数的话,需要使用到另一个智能指针_ParameterPtr来逐次设置要输入、输出的参数信息,并将其赋于_CommandPtr中Parameters参数来传递信息,有兴趣的读者可以自行查找相关书籍或MSDN。执行存储过程的代码如下所示:
_CommandPtr  m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection;  // 将库连接赋于它
m_pCommand->CommandText = "Demo";
m_pCommand->Execute(NULL,NULL, adCmdStoredProc);
、、、
文章导读:VC用ADO访问数据库全攻略,介绍了VC用ADO来访问数据库的各个对象及各方法,很经典,也很实用,很值得一看。一、ADO概述
  ADO是Microsoft为最新和最强大的数据访问范例 OLE DB 而设计的,是一个便于使用的应用程序层接口。ADO 使您能够编写应用程序以通过 OLE. DB 提供者访问和操作数据库服务器中的数据。ADO 最主要的优点是易于使用、速度快、内存支出少和磁盘遗迹小。ADO 在关键的应用方案中使用最少的网络流量,并且在前端和数据源之间使用最少的层数,所有这些都是为了提供轻量、高性能的接口。之所以称为 ADO,是用了一个比较熟悉的暗喻,OLE 自动化接口。  OLE DB是一组”组件对象模型”(COM) 接口,是新的数据库低层接口,它封装了ODBC的功能,并以统一的方式访问存储在不同信息源中的数据。OLE DB是Microsoft UDA(Universal Data Access)策略的技术基础。OLE DB 为任何数据源提供了高性能的访问,这些数据源包括关系和非关系数据库、电子邮件和文件系统、文本和图形、自定义业务对象等等。也就是说,OLE DB 并不局限于 ISAM、Jet 甚至关系数据源,它能够处理任何类型的数据,而不考虑它们的格式和存储方法。在实际应用中,这种多样性意味着可以访问驻留在 Excel 电子数据表、文本文件、电子邮件/目录服务甚至邮件服务器,诸如 Microsoft Exchange 中的数据。但是,OLE DB 应用程序编程接口的目的是为各种应用程序提供最佳的功能,它并不符合简单化的要求。您需要的API 应该是一座连接应用程序和OLE DB 的桥梁,这就是 ActiveX Data Objects (ADO)。 二、在VC中使用ADO(开发步骤如下:) 1、引入ADO库文件   使用ADO前必须在工程的stdafx.h头文件里用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下所示: 用#import引入ADO库文件 #import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF")
  这行语句声明在工程中使用ADO,但不使用ADO的名字空间,并且为了避免常数冲突,将常数EOF改名为adoEOF。现在不需添加另外的头文件,就可以使用ADO接口了。 2、初始化OLE/COM库环境
  必须注意的是,ADO库是一组COM动态库,这意味应用程序在调用ADO前,必须初始化OLE/COM库环境。在MFC应用程序里,一个比较好的方法是在应用程序主类的InitInstance成员函数里初始化OLE/COM库环境。 BOOL CMyAdoTestApp::InitInstance()
{
if(!AfxOleInit())//这就是初始化COM库
{
AfxMessageBox(“OLE初始化出错!”);
return FALSE;
} …… } 3、ADO接口简介   ADO库包含三个基本接口:_ConnectionPtr接口、_CommandPtr接口和_RecordsetPtr接口。
  _ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用_ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr来实现。而用_ConnectionPtr操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr时不需要。   _CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记录集,那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用_CommandPtr接口执行存储过程和SQL语句。   _RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同_CommandPtr接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给_RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口
,然后使用_RecordsetPtr执行存储过程和SQL语句。  4、使用_ConnectionPtr接口
  _ConnectionPtr主要是一个连接接口,取得与数据库的连接。它的连接字符串可以是自己直接写,也可以指向一个ODBC DSN。 _ConnectionPtr pConn;
if (FAILED(pConn.CreateInstance("ADODB.Connection")))
{
AfxMessageBox("Create Instance failed!");
return;
}
CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="suppersoft";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=SA;PWD=";CString strSQL = "Insert into student(no,name,sex,address) values(3,'aaa','male','beijing')";_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);
_bstr_t bstrSRC(strSRC);if (FAILED(pConn->Open(bstrSRC,"","",-1)))
{
AfxMessageBox("Can not open Database!");
pConn.Release();
return;
}COleVariant vtOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);pConn->Execute(_bstr_t(strSQL),&vtOptional,-1);pConn.Release();AfxMessageBox("ok!"); 5、使用_RecordsetPtr接口(以连接SQL Server为例)_RecordsetPtr pPtr;
if (FAILED(pPtr.CreateInstance("ADODB.Recordset")))
{
AfxMessageBox("Create Instance failed!");
return FALSE;
}CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="210.46.141.145";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=sa;PWD=";
strSRC+="sa";CString strSQL = "select id,name,gender,address from personal";_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);if(FAILED(pPtr->Open(varSQL,varSRC,adOpenStatic,adLockOptimistic,adCmdText)))
{
AfxMessageBox("Open table failed!");
pPtr.Release();
return FALSE;
}while(!pPtr->GetadoEOF())
{
_variant_t varNo;
_variant_t varName;
_variant_t varSex;
_variant_t varAddress;varNo = pPtr->GetCollect ("id");
varName = pPtr->GetCollect ("name");
varSex = pPtr->GetCollect ("gender");
varAddress = pPtr->GetCollect ("address");CString strNo =(char *)_bstr_t(varNo);
CString strName =(char *)_bstr_t(varName);
CString strSex =(char *)_bstr_t(varSex);
CString strAddress =(char *)_bstr_t(varAddress);strNo.TrimRight();
strName.TrimRight();
strSex.TrimRight();
strAddress.TrimRight();int nCount = m_list.GetItemCount();
int nItem = m_list.InsertItem (nCount,_T(""));
m_list.SetItemText (nItem,0,strNo);
m_list.SetItemText (nItem,1,strName);
m_list.SetItemText (nItem,2,strSex);
m_list.SetItemText (nItem,3,strAddress);pPtr->MoveNext();
}pPtr->Close();
pPtr.Release(); 6、使用_CommandPtr接口
  _CommandPtr接口返回一个Recordset对象,并且提供了更多的记录集控制功能,以下代码示例了使用_CommandPtr接口的方法:   代码:使用_CommandPtr接口获取数据_CommandPtr pCommand;
_RecordsetPtr pRs;
pCommand.CreateInstance(__uuidof(Command));
pCommand->ActiveConnection=pConn;
pCommand->CommandText="select * from student";
pCommand->CommandType=adCmdText;
pCommand->Parameters->Refresh();
pRs=pCommand->Execute(NULL,NULL,adCmdUnknown);
_variant_t varValue = pRs->GetCollect("name");
Cstring strValue=(char*)_bstr_t(varValue); 7、关于数据类型转换由于COM对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据,因此Cstring 类和COM对象是不兼容的,我们需要一组API来转换COM对象和C++类型的数据。_vatiant_t和_bstr_t就是这样两种对象。它们提供了通用的方法转换COM对象和C++类型的数据。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Channels_net/archive/2009/05/27/4221122.aspx
 
。。。。。。。。。。。。。。。。。。。。。。。。。。
......略 前面的有些观点是错误的,_ConnectionPtr的可以随便定义的. 首先在StdAfx.h加入这样一句引入ADO库后,就可以随便使用了. #import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename ("EOF", "adoEOF") _ConnectionPtr m_pConnection = NULL; // 定义变量, 当然也可以是全局变量的 然后在APP的InitIntance函数里: // 初始化COM,创建ADO连接等操作
AfxOleInit();
m_pConnection.CreateInstance(__uuidof(Connection)); // 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。jingzhou xu
try               
{
// 打开本地Access库Demo.mdb
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Home.mdb","","",adModeUnknown);
}
catch(_com_error e)
{
AfxMessageBox("数据库连接失败,确认数据库Demo.mdb是否在当前路径下!");
return FALSE;
} //从数据库中读入经手人名单和类型名单
_RecordsetPtr pHandlerRecordset;
pHandlerRecordset.CreateInstance(__uuidof(Recordset));
try
{
pHandlerRecordset->Open("SELECT * FROM Handler",                // 查询DemoTable表中所有字段
m_pConnection.GetInterfacePtr(), // 获取库接库的IDispatch指针
adOpenDynamic,
adLockOptimistic,
adCmdText);
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}
。。。。。。。。。。。。。。。。。。。
1如何切分窗口并产生响应:在消息中有讲到.
2.对话框:不要用对象,用指针。
在关闭后立即释放指针。
用对象出现的问题就是,当你关闭了对话框,但主程序还是在运行,你的对话框的对象还是存在的,再次运行就会出错。
非模态对话框之间进行切换:
添加一个CDialog*   m_pDlg的成员变量,初始为NULL。
不管你有几个对话框,打开前,如果m_pDlg不为NULL,delete之。
不管是创建什么对话框,都先new一个,再Create,然后将地址赋给m_pDlg。
3、简单对话框全屏显示方法
在OnInitDialog()中任意加入:
<1>、ModifyStyle(WS_CAPTION,0,0);   //如果不想去掉标题栏,去掉该句。
      SendMessage(WM_SYSCOMMAND,SC_MAXIMIZE,0);
<2>、ShowWindow(SW_SHOWMAXIMIZED);  
文章出处:http://www.diybl.com/course/3_program/c++/cppsl/200831/104723.html
4.如何让一个对话框按键后自动关闭,就像IDOK键一样.只要用EndDialog(对话框ID号);就行了(可以用debug调试IDOK看到在最后::~~~(忘了,必竟也只有两行代码而已)的时候进入就会看到)
5.改变对话框中控件大小和位置
在父窗口中响应VM_SIZE消息,在上篇中有详细说到
6.编辑框中内容显示问题.
当设定完一个Editbox的框后,再设定一个CString变量m_param, 在程序中如何让它显示出来呢,
先m_param = "124fsdg", 后要加上UpdateData(FALSE)(记住)7.改变单文档标题和单文档图标
<1>改变标题
比如你的工程叫hello
在hello.cpp中  
BOOL   CQuestionApp::InitInstance()
{
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->SetWindowText("hello");//加入标题文字hello
    m_pMainWnd->UpdateWindow()
}
<2>改变图标
在资源中删除ICON中的IDR_MAINFRAME图标,添加一个.ico的图标并把ID改为IDR_MAINFRAME即可
8.更换文档程序图标
一、如果是指定主框架窗口的图标,其步骤如下:
  
创建或打开工程Icon(以下都以工程名为Icon为例)。
  
单击Workspace窗口的ResourceView标签,选中资源ID为IDR_MAINFRAME图标资源,然后
按Delete键把它删除掉。注意:一定要把它删除才行。
  
从Developer   Studio的Insert菜单中选择Resource,然后选择Icon,新建(New)一个
新的图标或导入(Import)一个已有的图标。
  
把新图标的资源ID改为AFX_IDI_STD_MDIFRAME(如果是MDI应用程序)或改为
AFX_IDI_STD_FRAME(如果是SDI应用程序)。AFX_IDI_STD_MDIFRAME和
AFX_IDI_STD_FRAME这两个资源ID是MFC中预定义了的。
  
编译并运行程序,可以发现主框架窗口的图标就是你指定的图标。  
方法二、在程序运行时动态地改变窗口的图标
  
----   在程序运行时动态地改变框架窗口图标的原理是使用函数
  
----   CWnd::SendMessage()向窗口发送WM_SETICON消息。其方法是:
  
HICON   hIcon=AfxGetApp()-   >LoadIcon(IDI_ICON1);
ASSERT(hIcon);
AfxGetMainWnd()-   >SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);
  
----   以上叙述中的AfxGetMainWnd()是获得主框架窗口的窗口句柄,所以改变的是主框
架窗口(包括MDI和SDI)的图标9.让某一项在某种条件下显示为被选择状态,颜色不同
要调用SetFocus()获得焦点,然后使用SetItemState(iItem,LVIS_SELECTED,
LVIS_SELECTED),或者修改ListCtrl的风格为LVS_SHOWSELALWAYS。
{
    m_babyinfolist.SetExtendedStyle(LVS_EX_FULLROWSELECT);
    UINT flag = LVIS_SELECTED|LVIS_FOCUSED;
    m_babyinfolist.SetFocus();
    m_babyinfolist.SetItemState( i, LVIS_SELECTED, flag );
}
10.按键改变颜色
注意要把按钮控件设为owner draw<在styles中>
http://www.codeguru.com/Cpp/controls/buttonctrl/article.php/c2087
<1>.在.h中声明CColorButton变量
<2>.在.cpp中定义RGB()颜色
<3>.用VERIFY(m_btnjianhu.Attach(IDC_totaltel, this, LOUDBLUE,WHITE,WHITE));设置颜色
11.改变对话框背景颜色及static控件颜色<这种方法是不能改变按钮控件的颜色的>
每个控件和对话框在显示之前回发送WM_CTLCOLOR 消息,如果派生对话框类对WM_CTLCOLOR消息进行了映射,就可以设定文本的前景色和背景色。同时还可以为控件或对话框的非文本区域选择一个刷子。
WM_CTLCOLOR消息对应的函数为OnCtlColor,OnCtlColor函数的原型为:afx_msg   HBRUSH   OnCtlColor(   CDC*   pDC,   CWnd*   pWnd,   UINT   nCtlColor   );其中参数pDC是:一个指向设备环境的指针。pWnd:指定特定的控件。nCtlColor:指明控件类型。   可以有以下类型:
CTLCOLOR_BTN                   button   control
CTLCOLOR_DLG                  dialog   box
CTLCOLOR_EDIT                 edit   control
CTLCOLOR_LISTBOX           list   box
CTLCOLOR_MSGBOX          message   box
CTLCOLOR_SCROLLBAR     scroll   bar
CTLCOLOR_STATIC             static   text,   frame,   or   rectangle
现在为了突出显示文本框的显示号码,我们将文本框的文字背景设为黄色。
步骤1、添加一个成员变量   HBRUSH   m_hBrush;
步骤2、用Class   Wizard为CGuessDlg加入WM_CTLCOLOR消息,并编辑OnCtlColor函数:   
HBRUSH   CGuessDlg::OnCtlColor(CDC*   pDC,   CWnd*   pWnd,   UINT   nCtlColor)   
{
          if(nCtlColor==CTLCOLOR_EDIT){
                  pDC->SetBkColor   (RGB(255,255,0));//EDIT控件文字背景为黄色
                  return   m_hBrush;
          }
          return   CDialog::OnCtlColor   (pDC,pWnd,nCtlColor);
}  
12.ADO连接数据库时
m_prs->Open("SELECT * FROM babyinfo ORDER BY Room ASC, Bed ASC",_variant_t((IDispatch*)m_pConn,true),adOpenStatic,adLockOptimistic,adCmdText);    这样打开以后要记得关闭    m_prs->Close();   
如果不关闭,连续执行两次就会出错
13.当数据库内字段跟系统产生歧异。
今后字段取名要注意下,不要取datetime,count之类的可能是数据库关键字的敏字.
因为datetime是数据库的关键字,表示日期型数据类型。所以为了让数据库管理系统不把它当数据类型看的话,必须加上[]..
在数据库内执行SQL语句可能可以,但却还是有错误TRACE("%u,x%s",e.Error(),e.ErrorMessage(),e.Description()); ,可能是数据库内字段跟系统产生歧异。
14.得到系统时间.
当前时间:now()
当前日期:datatime()
VC得到当前时间:
CTime   NowTime=CTime::GetCurrentTime();
CString   str;
str.Format(%d%d%d",NowTime.GetYear(),NowTime.GetMonth()NowTime.GetDay());  
15.
error   C2501:   'CCriticalSection '   :   missing   storage-class   or   type   specifiers
原因:1.没包含头文件#include
      2.包含了,但#include 顺序不对,改了头文件包含的顺序,或者其间#define了一些宏,刚好与下面#include的代码冲突了
。。。。。。。。。。。。。。。。。。。。。。
忽略错误仍然可以运行?
连接数据库的代码我建议你放在对话框的OnInitDialog()函数里,而且最好放在主窗口的OnInitDialog()里。如果一定要放在子窗口,那也要在OnInitDialog()里,如果没有这个函数就自己写一个。
*********************
补充:我的经验都来自VC++6.0,所以没有见过出错之后还能运行的程序。
ADO的大致步骤是:
1.初始化库,就是调用AfxOleInit();
2.创建Connection对象,如果创建成功,就接着用这个Connection对象调用数据库驱动程序连接数据库;
3.创建结果集对象,用Connection对象执行SQL语句,将返回的查询结果放入结果集对象中。
在这之前,还要导入Windows操作系统的msado15.dll动态链接库。
开发环境虽不同,但ADO方法应该是一样的。
如果你觉得有必要看我很久前做的例子,就说一声。
//=============================================================。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
VC下利用ADO连接Access数据库 (2-1)(2007-04-23 09:54:05)
转载   
 
ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文详细地介绍在Visual C++开发环境下如何使用ADO来进行数据库应用程序开发,并给出示例代码。为了使读者朋友都能测试本例提供的代码,我们采用Access数据库,您可以直接在我们提供的示例代码中找到这个test.mdb。
  一、实现方法  万事开头难,任何一种新技术对于初学者来说最重要的还是"入门",掌握其要点。让我们来看看ADO数据库开发的基本流程吧!它的基本步骤如下:  (1)初始化COM库,引入ADO库定义文件  (2)用Connection对象连接数据库  (3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。  (4)使用完毕后关闭连接释放对象。  下面我们将详细介绍上述步骤并给出相关代码。  1、COM库的初始化  我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:BOOL CADOTest1App::InitInstance()
{
 AfxOleInit();
 ......
}
  2、用#import指令引入ADO类型库  为了引入ADO类型库,需要在项目的stdafx.h文件中加入如下语句:#import "c:\program files\common files\system\ado\msado15.dll"
no_namespace rename("EOF","adoEOF")
  这一语句有何作用呢?其最终作用同我们已经十分熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。  需要读者朋友注意的是:您的开发环境中msado15.dll不一定在这个目录下,请按实际情况修改;在编译的时候可能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告:msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned。  3、创建Connection对象并连接数据库  为了首先我们需要添加一个指向Connection对象的指针_ConnectionPtr m_pConnection,下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉:BOOL CADOTest1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 HRESULT hr;
 try
 {
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
  if(SUCCEEDED(hr))
  {
   hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
   Data Source=test.mdb","","",adModeUnknown);///连接数据库
   //上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,
   //需要改为:Provider=Microsoft.Jet.OLEDB.3.51;
  }
 }
 catch(_com_error e)///捕捉异常
 {
  CString errormessage;
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  AfxMessageBox(errormessage);///显示错误信息
 }
  在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型:HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options );
  上述函数中参数ConnectionString为连接字串;参数UserID是用户名;参数Password是登陆密码;参数Options是连接选项,用于指定Connection对象对数据的更新许可权,一般情况下Options可以是如下几个常量:  adModeUnknown:缺省。当前的许可权未设置  adModeRead:只读  adModeWrite:只写  adModeReadWrite:可以读写  adModeShareDenyRead:阻止其它Connection对象以读权限打开连接  adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接  adModeShareExclusive:阻止其它Connection对象以读写权限打开连接  adModeShareDenyNone:阻止其它Connection对象以任何权限打开连接  我们给出一些常用的连接方式供大家参考:  (1)通过JET数据库引擎对ACCESS2000数据库的连接:m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\\test.mdb","","",adModeUnknown);
  (2)通过DSN数据源对任何支持ODBC的数据库进行连接:m_pConnection->Open("Data Source=adotest;UID=sa;PWD=;","","",adModeUnknown);
  (3)不通过DSN对SQL SERVER数据库进行连接:m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;
UID=sa;PWD=139","","",adModeUnknown);
  其中Server是SQL服务器的名称,DATABASE是库的名称。  Connection对象除Open()方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State。ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如:m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);
  State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:if(m_pConnection->State)
m_pConnection->Close(); ///如果已经打开了连接则关闭它
  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表明CommandText内容未知。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变量
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对象进行查询取得记录集,例如: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的含义请参考本文中对Connection对象的Execute()方法的介绍。  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)
///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","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();///保存到库中
  6、关闭记录集与连接  记录集或连接都可以用Close()方法来关闭:m_pRecordset->Close();///关闭记录集
m_pConnection->Close();///关闭连接
  至此,我想读者朋友已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子实现的功能是读出所有记录并放到列表控件中,同时可以添加、删除、修改记录。、、、、、、、、、、、、、、
VC++学习--VC下利用ADO连接Access数据库 (2-2)(2007-04-23 10:08:06)
转载   
 
二、编程步骤  1、 启动Visual C++6.0,生成一个基于对话框的应用程序,将该程序命名为ADOTest1;  2、在对话框界面上放置显示记录列表控件和添加、删除记录用的的编辑、按钮控件,具体设置参加代码中的对话框资源部分;  3、 使用Class Wizard为添加、修改数据库记录的按钮添加消息响应函数;  4、 添加成程序代码,编译运行程序。三、程序代码/////////////////////////////////////////////////// ADOTest1Dlg.h : header file
#if !defined(AFX_ADOTEST1DLG_H__29B385C0_02C0_4588_A8B4_D0EFBB4F578D__INCLUDED_)
#define AFX_ADOTEST1DLG_H__29B385C0_02C0_4588_A8B4_D0EFBB4F578D__INCLUDED_
#if _MSC_VER >1000
#pragma once
#endif // _MSC_VER >1000
class CADOTest1Dlg : public CDialog
{
 // Construction
 public:
  BOOL m_bAutoSave;
  void SaveData();
  void LoadData();
  _variant_t vUserID,vUsername,vOld,vBirthday;
  BOOL m_bSuccess;
  int m_nCurrentSel;
  _RecordsetPtr m_pRecordset;
  CADOTest1Dlg(CWnd* pParent = NULL); // standard constructor
  // Dialog Data
  //{{AFX_DATA(CADOTest1Dlg)
   enum { IDD = IDD_ADOTEST1_DIALOG };
   CButton m_cDelItem;
   CButton m_cAddItem;
   CListCtrl m_userlist;
   UINT m_nUserID;
   UINT m_nOld;
   CString m_sUsername;
   COleDateTime m_tBirthday;
  //}}AFX_DATA
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CADOTest1Dlg)
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  //}}AFX_VIRTUAL
  // Implementation
 protected:
  HICON m_hIcon;
  // Generated message map functions
  //{{AFX_MSG(CADOTest1Dlg)
   virtual BOOL OnInitDialog();
   afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
   afx_msg void OnPaint();
   afx_msg HCURSOR OnQueryDragIcon();
   virtual void OnOK();
   afx_msg void OnAdditem();
   afx_msg void OnDelitem();
   afx_msg void OnItemchangedUserlist(NMHDR* pNMHDR, LRESULT* pResult);
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};
#endif
////////////////////////////////////////////////////// ADOTest1Dlg.cpp : implementation file
#include "stdafx.h"
#include "ADOTest1.h"
#include "ADOTest1Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern CADOTest1App theApp;
class CAboutDlg : public CDialog
{
 public:
  CAboutDlg();
  // Dialog Data
  //{{AFX_DATA(CAboutDlg)
   enum { IDD = IDD_ABOUTBOX };
  //}}AFX_DATA
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CAboutDlg)
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  //}}AFX_VIRTUAL
  // Implementation
 protected:
  //{{AFX_MSG(CAboutDlg)
  //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
 //{{AFX_DATA_INIT(CAboutDlg)
 //}}AFX_DATA_INIT
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CAboutDlg)
 //}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 //{{AFX_MSG_MAP(CAboutDlg)
  // No message handlers
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()CADOTest1Dlg::CADOTest1Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CADOTest1Dlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CADOTest1Dlg)
  m_nUserID = 0;
  m_nOld = 0;
  m_sUsername = _T("");
  m_tBirthday = COleDateTime::GetCurrentTime();
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 m_nCurrentSel = -1;
 m_bSuccess = FALSE;
 m_bAutoSave = TRUE;
}void CADOTest1Dlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CADOTest1Dlg)
  DDX_Control(pDX, IDC_DELITEM, m_cDelItem);
  DDX_Control(pDX, IDC_ADDITEM, m_cAddItem);
  DDX_Control(pDX, IDC_USERLIST, m_userlist);
  DDX_Text(pDX, IDC_USERID, m_nUserID);
  DDX_Text(pDX, IDC_OLD, m_nOld);
  DDX_Text(pDX, IDC_USERNAME, m_sUsername);
  DDX_DateTimeCtrl(pDX, IDC_DATETIMEPICKER1, m_tBirthday);
 //}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CADOTest1Dlg, CDialog)
//{{AFX_MSG_MAP(CADOTest1Dlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_BN_CLICKED(IDC_ADDITEM, OnAdditem)
 ON_BN_CLICKED(IDC_DELITEM, OnDelitem)
 ON_NOTIFY(LVN_ITEMCHANGED, IDC_USERLIST, OnItemchangedUserlist)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()BOOL CADOTest1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 m_cDelItem.EnableWindow(FALSE);
 ::SendMessage(m_userlist.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,                       ******************************************************************
 LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
 //////////为列表控件添加列//////////
 m_userlist.InsertColumn(0,"用户ID",LVCFMT_LEFT,60);
 m_userlist.InsertColumn(1,"用户名",LVCFMT_LEFT,100);
 m_userlist.InsertColumn(2,"年龄",LVCFMT_LEFT,60);
 m_userlist.InsertColumn(3,"生日",LVCFMT_LEFT,100);
 //////////读取数据库中的信息添加到列表控件///////////
 int nItem;
 _variant_t vUsername,vBirthday,vID,vOld;
 try
 {
  m_pRecordset.CreateInstance("ADODB.Recordset");
  m_pRecordset->Open("SELECT*FROM users",
  _variant_t((IDispatch*)theApp.m_pConnection,true),
  adOpenStatic,adLockOptimistic,adCmdText);
  m_bSuccess = TRUE;
  while(!m_pRecordset->adoEOF)
  {
   vID = m_pRecordset->GetCollect("ID");
   vUsername = m_pRecordset->GetCollect("username");
   vOld = m_pRecordset->GetCollect("old");
   vBirthday = m_pRecordset->GetCollect("birthday");
   nItem=m_userlist.InsertItem(0xffff,(_bstr_t)vID);
   m_userlist.SetItem(nItem,1,1,(_bstr_t)vUsername,NULL,0,0,0);
   m_userlist.SetItem(nItem,2,1,(_bstr_t)vOld,NULL,0,0,0);
   m_userlist.SetItem(nItem,3,1,(_bstr_t)vBirthday,NULL,0,0,0);
   m_pRecordset->MoveNext();
  }
 }
 catch(_com_error e)///捕捉异常
 {
  AfxMessageBox("读取数据库失败!");///显示错误信息
 }
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);
 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon
 return TRUE; // return TRUE unless you set the focus to a control
}void CADOTest1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}void CADOTest1Dlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting
  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;
  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
 }
}HCURSOR CADOTest1Dlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}void CADOTest1Dlg::OnOK()
{
 if(m_bSuccess)
 {
  m_pRecordset->Update();
  m_pRecordset->Close();
 }
 CDialog::OnOK();
}void CADOTest1Dlg::OnAdditem()
{
 if(UpdateData())
  if(m_sUsername.GetLength()>0)
  {
   m_pRecordset->AddNew();
   m_nCurrentSel = m_userlist.InsertItem(0xffff,"");
   SaveData();///保存数据
   m_userlist.SetItemState(m_nCurrentSel,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
   m_userlist.SetHotItem(m_nCurrentSel);
   m_userlist.SetFocus();
  }
  else
   AfxMessageBox("请输入用户名");
}void CADOTest1Dlg::OnDelitem()
{
 m_bAutoSave = FALSE;
 if(m_nCurrentSel >= 0)
 {
  m_userlist.DeleteItem(m_nCurrentSel);
  int count = m_userlist.GetItemCount();
  if(count <= m_nCurrentSel)
   m_nCurrentSel = count-1;
   m_pRecordset->Delete(adAffectCurrent);
   m_pRecordset->MoveNext();
   LoadData();
   m_userlist.SetItemState(m_nCurrentSel,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
   m_userlist.SetFocus();
 }
 m_bAutoSave = TRUE;
}//////////在选择列表框的时候调用///////////////////
void CADOTest1Dlg::OnItemchangedUserlist(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
 if(pNMListView->uNewState&LVIS_SELECTED)
 {
  UpdateData();
  SaveData();///保存旧数据
  m_nCurrentSel = pNMListView->iItem;
  LoadData();///加载新数据
  m_cDelItem.EnableWindow();
 }
 *pResult = 0;
}//////将记录集中的数据加载到编辑框/////
void CADOTest1Dlg::LoadData()
{
 m_pRecordset->Move(m_nCurrentSel,_variant_t((long)adBookmarkFirst));
 vUserID = m_pRecordset->GetCollect("ID");
 vUsername = m_pRecordset->GetCollect("username");
 vOld = m_pRecordset->GetCollect("old");
 vBirthday = m_pRecordset->GetCollect("birthday");
 m_nUserID = vUserID.lVal;
 m_sUsername = (LPCTSTR)(_bstr_t)vUsername;
 m_nOld = vOld.lVal;
 m_tBirthday = vBirthday;
 UpdateData(FALSE);
}/////将编辑框的数据保存到记录集与列表框
void CADOTest1Dlg::SaveData()
{
 if(!m_pRecordset->adoEOF && m_nCurrentSel >= 0 && m_bAutoSave)
 {
  vUserID = (long)m_nUserID;
  vUsername = m_sUsername;
  vOld = (long)m_nOld;
  vBirthday = m_tBirthday;
  m_pRecordset->PutCollect("ID",vUserID);
  m_pRecordset->PutCollect("username",vUsername);
  m_pRecordset->PutCollect("old",vOld);
  m_pRecordset->PutCollect("birthday",vBirthday);
  m_userlist.SetItem(m_nCurrentSel,0,LVIF_TEXT,(_bstr_t)vUserID,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,1,LVIF_TEXT,(_bstr_t)vUsername,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,2,LVIF_TEXT,(_bstr_t)vOld,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,3,LVIF_TEXT,(_bstr_t)vBirthday,NULL,0,0,0);
 }
}
////////////////////////////////////////////////////////////////////////
BOOL CADOTest1App::InitInstance()
{
 AfxEnableControlContainer();
 AfxOleInit();///初始化COM库
 HRESULT hr; ////////////连接数据库//////////////
 try
 {
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
  if(SUCCEEDED(hr))
  {
   hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
   Data Source=test.mdb","","",adModeUnknown);///连接数据库
   ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,
   /////对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51; }
  }
 }
 catch(_com_error e)///捕捉异常
 {
  CString errormessage;
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  AfxMessageBox(errormessage);///显示错误信息
  return FALSE;
 }
 #ifdef _AFXDLL
  Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
  Enable3dControlsStatic(); // Call this when linking to MFC statically
 #endif
 CADOTest1Dlg dlg;
 m_pMainWnd = &dlg;
 int nResponse = dlg.DoModal();
 if (nResponse == IDOK)
 {}
 else if (nResponse == IDCANCEL)
 {}
 return FALSE;
}/////////////////////////////////////////////
int CADOTest1App::ExitInstance()
{
 if(m_pConnection->State)
 m_pConnection->Close(); ///如果已经打开了连接则关闭它
 return CWinApp::ExitInstance();。。。。。。。。。。。。。。。。。
、、、、、、、、、、、、、、
VC ADO连接数据库VC技术 2007-06-11 15:12:08 阅读353 评论0 字号:大中小 .VC下利用ADO连接Access数据库 :
#include
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename ("eof", "adoeof")  int main(){   //使用ado连接数据库...
   //---------------------------------------------------------------------------------
   _connectionptr m_pconnection;    coinitialize(null);
   m_pconnection.createinstance(__uuidof(connection));   // 在ado操作中建议语句中要常用try...catch()来捕获错误信息,
   // 因为它有时会经常出现一些想不到的错误。
   try   
   {  
     // 打开本地access库db1.mdb
     m_pconnection->open("provider=microsoft.jet.oledb.4.0;data source=db1.mdb","","",admodeunknown);
    
   }
   catch(_com_error e)
   {
     cout<<"数据库连接失败,确认数据库db1.mdb是否在当前路径下!"<     return false;
   }
   //-------------------------------------------------------------------------------------
   //建立数据集
   //-------------------------------------------------------------------------------------
   _recordsetptr m_precordset;
   m_precordset.createinstance(__uuidof(recordset));   // 在ado操作中建议语句中要常用try...catch()来捕获错误信息,
   // 因为它有时会经常出现一些意想不到的错误。
   try
   {  
     m_precordset->open("select * from home2",  
     m_pconnection.getinterfaceptr(), // 获取库接库的idispatch指针
     adopendynamic,
     adlockoptimistic,
     adcmdtext);
   }
   catch(_com_error *e)
   {
     //afxmessagebox(e->errormessage());
     cout<errormessage()<   }      //--------------------------------------------------------------------------------------
   //读取数据
   //--------------------------------------------------------------------------------------
   _variant_t var;
   char *strid,*strx,*stry;
   float x,y;
   try
   {
     if(!m_precordset->bof)
       m_precordset->movefirst();
     else
     {
       cout<<"表内数据为空"<       return 1;
     }
    
     // 读入库中各字段并加入列表框中
     while(!m_precordset->adoeof)
     {
       var = m_precordset->getcollect("id");
       if(var.vt != vt_null)
         strid= _com_util::convertbstrtostring((_bstr_t)var);   //_variant_t转字符串
       var = m_precordset->getcollect("x");
       if(var.vt != vt_null)
           strx=_com_util::convertbstrtostring((_bstr_t)var);
       var = m_precordset->getcollect("y");
       if(var.vt != vt_null)
         stry=_com_util::convertbstrtostring((_bstr_t)var);
       cout<    
       m_precordset->movenext();
     }
   }
   catch(_com_error *e)
   {
     cout<errormessage()<   }     //--------------------------------------------------------------------------------------
   //关闭数据集
   m_precordset->close();
   m_precordset = null;  
   //--------------------------------------------------------------------------------------
   //关闭数据库连接
   //--------------------------------------------------------------------------------------
   if(m_pconnection->state)
  m_pconnection->close();
   m_pconnection= null;
   return 0;
}以上都是源代码部分,有个问题很奇怪,我数据库原来起的名字是position,可就是连不上去,我随便换了个叫home2的就行了,不知道是怎么回事?//==========================================================================
.........................................................................。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
VC++中使用ADO方式操作ACCESS数据库
2008-01-08 11:32:46 来源:编程论坛 作者: 点击:5447
--------------------------------------------------------------------------------ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。 
  ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文详细地介绍在Visual C++开发环境下如何使用ADO来进行数据库应用程序开发。   一、实现方法  万事开头难,任何一种新技术对于初学者来说最重要的还是"入门",掌握其要点。让我们来看看ADO数据库开发的基本流程吧!它的基本步骤如下:  (1)初始化COM库,引入ADO库定义文件  (2)用Connection对象连接数据库  (3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。  (4)使用完毕后关闭连接释放对象。  下面我们将详细介绍上述步骤并给出相关代码。  1、COM库的初始化  我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:BOOL CADOTest1App::InitInstance()
{
 AfxOleInit();
 ......
}   2、用#import指令引入ADO类型库  为了引入ADO类型库,需要在项目的stdafx.h文件中加入如下语句:#import "c:\program files\common files\system\ado\msado15.dll"
no_namespace rename("EOF","adoEOF")   这一语句有何作用呢?其最终作用同我们已经十分熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。  需要读者朋友注意的是:您的开发环境中msado15.dll不一定在这个目录下,请按实际情况修改;在编译的时候可能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告:msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned。  3、创建Connection对象并连接数据库  为了首先我们需要添加一个指向Connection对象的指针_ConnectionPtr m_pConnection,下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉:BOOL CADOTest1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 HRESULT hr;
 try
 {
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
  if(SUCCEEDED(hr))
  {
   hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
   Data Source=test.mdb","","",adModeUnknown);///连接数据库
   //上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,
   //需要改为:Provider=Microsoft.Jet.OLEDB.3.51;
  }
 }
 catch(_com_error e)///捕捉异常
 {
  CString errormessage;
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  AfxMessageBox(errormessage);///显示错误信息
 }    在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型:HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options );   上述函数中参数ConnectionString为连接字串;参数UserID是用户名;参数Password是登陆密码;参数Options是连接选项,用于指定Connection对象对数据的更新许可权,一般情况下Options可以是如下几个常量:  adModeUnknown:缺省。当前的许可权未设置  adModeRead:只读  adModeWrite:只写  adModeReadWrite:可以读写  adModeShareDenyRead:阻止其它Connection对象以读权限打开连接  adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接  adModeShareExclusive:阻止其它Connection对象以读写权限打开连接  adModeShareDenyNone:阻止其它Connection对象以任何权限打开连接  我们给出一些常用的连接方式供大家参考:  (1)通过JET数据库引擎对ACCESS2000数据库的连接:m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\\test.mdb","","",adModeUnknown);   (2)通过DSN数据源对任何支持ODBC的数据库进行连接:m_pConnection->Open("Data Source=adotest;UID=sa;PWD=;","","",adModeUnknown);   (3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;
UID=sa;PWD=139","","",adModeUnknown);   其中Server是SQL服务器的名称,DATABASE是库的名称。  Connection对象除Open()方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State。ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如:m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);   State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:if(m_pConnection->State)
m_pConnection->Close(); ///如果已经打开了连接则关闭它   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表明CommandText内容未知。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变量
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对象进行查询取得记录集,例如: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的含义请参考本文中对Connection对象的Execute()方法的介绍。  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)
///这里为什么是adoEOF而不是EOF呢?还记得rename("EOF","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();///保存到库中   6、关闭记录集与连接   记录集或连接都可以用Close()方法来关闭: m_pRecordset->Close();///关闭记录集
m_pConnection->Close();///关闭连接   至此,我想读者朋友已经熟悉了ADO操作数据库的大致流程,也许您已经胸有成竹,也许您还有点胡涂,不要紧!建议你尝试写几个例子,这样会更好地熟悉ADO,最后我给大家写了一个小例子,例子实现的功能是读出所有记录并放到列表控件中,同时可以添加、删除、修改记录。  二、编程步骤  1、 启动Visual C++6.0,生成一个基于对话框的应用程序,将该程序命名为ADOTest1;  2、 在对话框界面上放置显示记录列表控件和添加、删除记录用的的编辑、按钮控件,具体设置参加代码中的对话框资源部分;  3、 使用Class Wizard为添加、修改数据库记录的按钮添加消息响应函数;  4、 添加成程序代码,编译运行程序。三、程序代码/////////////////////////////////////////////////// ADOTest1Dlg.h : header file
#if !defined(AFX_ADOTEST1DLG_H__29B385C0_02C0_4588_A8B4_D0EFBB4F578D__INCLUDED_)
#define AFX_ADOTEST1DLG_H__29B385C0_02C0_4588_A8B4_D0EFBB4F578D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CADOTest1Dlg : public CDialog
{
 // Construction
 public:
  BOOL m_bAutoSave;
  void SaveData();
  void LoadData();
  _variant_t vUserID,vUsername,vOld,vBirthday;
  BOOL m_bSuccess;
  int m_nCurrentSel;
  _RecordsetPtr m_pRecordset;
  CADOTest1Dlg(CWnd* pParent = NULL); // standard constructor
  // Dialog Data
  //{{AFX_DATA(CADOTest1Dlg)
   enum { IDD = IDD_ADOTEST1_DIALOG };
   CButton m_cDelItem;
   CButton m_cAddItem;
   CListCtrl m_userlist;
   UINT m_nUserID;
   UINT m_nOld;
   CString m_sUsername;
   COleDateTime m_tBirthday;
  //}}AFX_DATA
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CADOTest1Dlg)
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  //}}AFX_VIRTUAL
  // Implementation
 protected:
  HICON m_hIcon;
  // Generated message map functions
  //{{AFX_MSG(CADOTest1Dlg)
   virtual BOOL OnInitDialog();
   afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
   afx_msg void OnPaint();
   afx_msg HCURSOR OnQueryDragIcon();
   virtual void OnOK();
   afx_msg void OnAdditem();
   afx_msg void OnDelitem();
   afx_msg void OnItemchangedUserlist(NMHDR* pNMHDR, LRESULT* pResult);
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};
#endif
////////////////////////////////////////////////////// ADOTest1Dlg.cpp : implementation file
#include "stdafx.h"
#include "ADOTest1.h"
#include "ADOTest1Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern CADOTest1App theApp;
class CAboutDlg : public CDialog
{
 public:
  CAboutDlg();
  // Dialog Data
  //{{AFX_DATA(CAboutDlg)
   enum { IDD = IDD_ABOUTBOX };
  //}}AFX_DATA
  // ClassWizard generated virtual function overrides
  //{{AFX_VIRTUAL(CAboutDlg)
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  //}}AFX_VIRTUAL
  // Implementation
 protected:
  //{{AFX_MSG(CAboutDlg)
  //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
 //{{AFX_DATA_INIT(CAboutDlg)
 //}}AFX_DATA_INIT
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CAboutDlg)
 //}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 //{{AFX_MSG_MAP(CAboutDlg)
  // No message handlers
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()CADOTest1Dlg::CADOTest1Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CADOTest1Dlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CADOTest1Dlg)
  m_nUserID = 0;
  m_nOld = 0;
  m_sUsername = _T("");
  m_tBirthday = COleDateTime::GetCurrentTime();
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 m_nCurrentSel = -1;
 m_bSuccess = FALSE;
 m_bAutoSave = TRUE;
}void CADOTest1Dlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CADOTest1Dlg)
  DDX_Control(pDX, IDC_DELITEM, m_cDelItem);
  DDX_Control(pDX, IDC_ADDITEM, m_cAddItem);
  DDX_Control(pDX, IDC_USERLIST, m_userlist);
  DDX_Text(pDX, IDC_USERID, m_nUserID);
  DDX_Text(pDX, IDC_OLD, m_nOld);
  DDX_Text(pDX, IDC_USERNAME, m_sUsername);
  DDX_DateTimeCtrl(pDX, IDC_DATETIMEPICKER1, m_tBirthday);
 //}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CADOTest1Dlg, CDialog)
//{{AFX_MSG_MAP(CADOTest1Dlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_BN_CLICKED(IDC_ADDITEM, OnAdditem)
 ON_BN_CLICKED(IDC_DELITEM, OnDelitem)
 ON_NOTIFY(LVN_ITEMCHANGED, IDC_USERLIST, OnItemchangedUserlist)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()BOOL CADOTest1Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 m_cDelItem.EnableWindow(FALSE);
 ::SendMessage(m_userlist.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
 LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
 //////////为列表控件添加列//////////
 m_userlist.InsertColumn(0,"用户ID",LVCFMT_LEFT,60);
 m_userlist.InsertColumn(1,"用户名",LVCFMT_LEFT,100);
 m_userlist.InsertColumn(2,"年龄",LVCFMT_LEFT,60);
 m_userlist.InsertColumn(3,"生日",LVCFMT_LEFT,100);
 //////////读取数据库中的信息添加到列表控件///////////
 int nItem;
 _variant_t vUsername,vBirthday,vID,vOld;
 try
 {
  m_pRecordset.CreateInstance("ADODB.Recordset");
  m_pRecordset->Open("SELECT*FROM users",
  _variant_t((IDispatch*)theApp.m_pConnection,true),
  adOpenStatic,adLockOptimistic,adCmdText);
  m_bSuccess = TRUE;
  while(!m_pRecordset->adoEOF)
  {
   vID = m_pRecordset->GetCollect("ID");
   vUsername = m_pRecordset->GetCollect("username");
   vOld = m_pRecordset->GetCollect("old");
   vBirthday = m_pRecordset->GetCollect("birthday");
   nItem=m_userlist.InsertItem(0xffff,(_bstr_t)vID);
   m_userlist.SetItem(nItem,1,1,(_bstr_t)vUsername,NULL,0,0,0);
   m_userlist.SetItem(nItem,2,1,(_bstr_t)vOld,NULL,0,0,0);
   m_userlist.SetItem(nItem,3,1,(_bstr_t)vBirthday,NULL,0,0,0);
   m_pRecordset->MoveNext();
  }
 }
 catch(_com_error e)///捕捉异常
 {
  AfxMessageBox("读取数据库失败!");///显示错误信息
 }
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);
 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon
 return TRUE; // return TRUE unless you set the focus to a control
}void CADOTest1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}void CADOTest1Dlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting
  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;
  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
 }
}HCURSOR CADOTest1Dlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}void CADOTest1Dlg::OnOK()
{
 if(m_bSuccess)
 {
  m_pRecordset->Update();
  m_pRecordset->Close();
 }
 CDialog::OnOK();
}void CADOTest1Dlg::OnAdditem()
{
 if(UpdateData())
  if(m_sUsername.GetLength()>0)
  {
   m_pRecordset->AddNew();
   m_nCurrentSel = m_userlist.InsertItem(0xffff,"");
   SaveData();///保存数据
   m_userlist.SetItemState(m_nCurrentSel,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
   m_userlist.SetHotItem(m_nCurrentSel);
   m_userlist.SetFocus();
  }
  else
   AfxMessageBox("请输入用户名");
}void CADOTest1Dlg::OnDelitem()
{
 m_bAutoSave = FALSE;
 if(m_nCurrentSel >= 0)
 {
  m_userlist.DeleteItem(m_nCurrentSel);
  int count = m_userlist.GetItemCount();
  if(count <= m_nCurrentSel)
   m_nCurrentSel = count-1;
   m_pRecordset->Delete(adAffectCurrent);
   m_pRecordset->MoveNext();
   LoadData();
   m_userlist.SetItemState(m_nCurrentSel,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
   m_userlist.SetFocus();
 }
 m_bAutoSave = TRUE;
}//////////在选择列表框的时候调用///////////////////
void CADOTest1Dlg::OnItemchangedUserlist(NMHDR* pNMHDR, LRESULT* pResult)
{
 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
 if(pNMListView->uNewState&LVIS_SELECTED)
 {
  UpdateData();
  SaveData();///保存旧数据
  m_nCurrentSel = pNMListView->iItem;
  LoadData();///加载新数据
  m_cDelItem.EnableWindow();
 }
 *pResult = 0;
}//////将记录集中的数据加载到编辑框/////
void CADOTest1Dlg::LoadData()
{
 m_pRecordset->Move(m_nCurrentSel,_variant_t((long)adBookmarkFirst));
 vUserID = m_pRecordset->GetCollect("ID");
 vUsername = m_pRecordset->GetCollect("username");
 vOld = m_pRecordset->GetCollect("old");
 vBirthday = m_pRecordset->GetCollect("birthday");
 m_nUserID = vUserID.lVal;
 m_sUsername = (LPCTSTR)(_bstr_t)vUsername;
 m_nOld = vOld.lVal;
 m_tBirthday = vBirthday;
 UpdateData(FALSE);
}/////将编辑框的数据保存到记录集与列表框
void CADOTest1Dlg::SaveData()
{
 if(!m_pRecordset->adoEOF && m_nCurrentSel >= 0 && m_bAutoSave)
 {
  vUserID = (long)m_nUserID;
  vUsername = m_sUsername;
  vOld = (long)m_nOld;
  vBirthday = m_tBirthday;
  m_pRecordset->PutCollect("ID",vUserID);
  m_pRecordset->PutCollect("username",vUsername);
  m_pRecordset->PutCollect("old",vOld);
  m_pRecordset->PutCollect("birthday",vBirthday);
  m_userlist.SetItem(m_nCurrentSel,0,LVIF_TEXT,(_bstr_t)vUserID,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,1,LVIF_TEXT,(_bstr_t)vUsername,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,2,LVIF_TEXT,(_bstr_t)vOld,NULL,0,0,0);
  m_userlist.SetItem(m_nCurrentSel,3,LVIF_TEXT,(_bstr_t)vBirthday,NULL,0,0,0);
 }
}
////////////////////////////////////////////////////////////////////////
BOOL CADOTest1App::InitInstance()
{
 AfxEnableControlContainer();
 AfxOleInit();///初始化COM库
 HRESULT hr; ////////////连接数据库//////////////
 try
 {
  hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
  if(SUCCEEDED(hr))
  {
   hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;
   Data Source=test.mdb","","",adModeUnknown);///连接数据库
   ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,
   /////对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51; }
  }
 }
 catch(_com_error e)///捕捉异常
 {
  CString errormessage;
  errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
  AfxMessageBox(errormessage);///显示错误信息
  return FALSE;
 }
 #ifdef _AFXDLL
  Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
  Enable3dControlsStatic(); // Call this when linking to MFC statically
 #endif
 CADOTest1Dlg dlg;
 m_pMainWnd = &dlg;
 int nResponse = dlg.DoModal();
 if (nResponse == IDOK)
 {}
 else if (nResponse == IDCANCEL)
 {}
 return FALSE;
}/////////////////////////////////////////////
int CADOTest1App::ExitInstance()
{
 if(m_pConnection->State)
 m_pConnection->Close(); ///如果已经打开了连接则关闭它
 return CWinApp::ExitInstance();
}   四、小结  限于篇幅ADO中的许多内容还没有介绍,如绑定方式处理记录集数据、存储过程的调用、事务处理、图象在数据库中的保存与读取、与表格控件的配合使用等。如果读者对上述内容感性认识的话,可以自行参考相关编程资料。
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
vc ado 数据库2006-11-05 18:47VC用ADO访问数据库全攻略,介绍了VC用ADO来访问数据库的各个对象及各方法,很经典,也很实用,很值得一看。  
正文
一、ADO概述
ADO是Microsoft为最新和最强大的数据访问范例 OLE DB 而设计的,是一个便于使用的应用程序层接口。ADO 使您能够编写应用程序以通过 OLE. DB 提供者访问和操作数据库服务器中的数据。ADO 最主要的优点是易于使用、速度快、内存支出少和磁盘遗迹小。ADO 在关键的应用方案中使用最少的网络流量,并且在前端和数据源之间使用最少的层数,所有这些都是为了提供轻量、高性能的接口。之所以称为 ADO,是用了一个比较熟悉的暗喻,OLE 自动化接口。OLE DB是一组”组件对象模型”(COM) 接口,是新的数据库低层接口,它封装了ODBC的功能,并以统一的方式访问存储在不同信息源中的数据。OLE DB是Microsoft UDA(Universal Data Access)策略的技术基础。OLE DB 为任何数据源提供了高性能的访问,这些数据源包括关系和非关系数据库、电子邮件和文件系统、文本和图形、自定义业务对象等等。也就是说,OLE DB 并不局限于 ISAM、Jet 甚至关系数据源,它能够处理任何类型的数据,而不考虑它们的格式和存储方法。在实际应用中,这种多样性意味着可以访问驻留在 Excel 电子数据表、文本文件、电子邮件/目录服务甚至邮件服务器,诸如 Microsoft Exchange 中的数据。但是,OLE DB 应用程序编程接口的目的是为各种应用程序提供最佳的功能,它并不符合简单化的要求。您需要的API 应该是一座连接应用程序和OLE DB 的桥梁,这就是 ActiveX Data Objects (ADO)。 二、在VC中使用ADO(开发步骤好下:) 1、引入ADO库文件 使用ADO前必须在工程的stdafx.h头文件里用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下所示: 用#import引入ADO库文件 #import "c:\program files\common files\system\ado\msado15.dll"no_namespaces rename("EOF" adoEOF") 这行语句声明在工程中使用ADO,但不使用ADO的名字空间,并且为了避免常数冲突,将常数EOF改名为adoEOF。现在不需添加另外的头文件,就可以使用ADO接口了。 2、初始化OLE/COM库环境
必须注意的是,ADO库是一组COM动态库,这意味应用程序在调用ADO前,必须初始化OLE/COM库环境。在MFC应用程序里,一个比较好的方法是在应用程序主类的InitInstance成员函数里初始化OLE/COM库环境。 BOOL CMyAdoTestApp::InitInstance()
{
if(!AfxOleInit())//这就是初始化COM库
{
AfxMessageBox(“OLE初始化出错!”);
return FALSE;
} …… } 3、ADO接口简介 ADO库包含三个基本接口:_ConnectionPtr接口、_CommandPtr接口和_RecordsetPtr接口。
_ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。使用_ConnectionPtr接口返回一个记录集不是一个好的使用方法。对于要返回记录的操作通常用_RecordserPtr来实现。而用_ConnectionPtr操作时要想得到记录条数得遍历所有记录,而用_RecordserPtr时不需要。 _CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,你可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果你只执行一次或几次数据访问操作,后者是比较好的选择。但如果你要频繁访问数据库,并要返回很多记录集,那么,你应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用_CommandPtr接口执行存储过程和SQL语句。 _RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同_CommandPtr接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给_RecordsetPtr的connection成员变量,让它自己创建数据连接。如果你要使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局_ConnectionPtr接口
,然后使用_RecordsetPtr执行存储过程和SQL语句。  4、使用_ConnectionPtr接口
_ConnectionPtr主要是一个连接接口,取得与数据库的连接。它的连接字符串可以是自己直接写,也可以指向一个ODBC DSN。。 
  _ConnectionPtr pConn;
if (FAILED(pConn.CreateInstance("ADODB.Connection")))
{
AfxMessageBox("Create Instance failed!");
return;
}
CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="suppersoft";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=SA;PWD=";CString strSQL = "Insert into student(no,name,sex,address) values(3,'aaa','male','beijing')";_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);
_bstr_t bstrSRC(strSRC);if (FAILED(pConn->Open(bstrSRC,"","",-1)))
{
AfxMessageBox("Can not open Database!");
pConn.Release();
return;
}COleVariant vtOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);pConn->Execute(_bstr_t(strSQL),&vtOptional,-1);pConn.Release();AfxMessageBox("ok!");5、使用_RecordsetPtr接口(以连接SQL Server为例)
_RecordsetPtr pPtr;
if (FAILED(pPtr.CreateInstance("ADODB.Recordset")))
{
AfxMessageBox("Create Instance failed!");
return FALSE;
}CString strSRC;
strSRC="Driver=SQL Server;Server=";
strSRC+="210.46.141.145";
strSRC+=";Database=";
strSRC+="mydb";
strSRC+=";UID=sa;PWD=";
strSRC+="sa";CString strSQL = "select id,name,gender,address from personal";_variant_t varSRC(strSRC);
_variant_t varSQL(strSQL);if(FAILED(pPtr->Open(varSQL,varSRC,adOpenStatic,adLockOptimistic,adCmdText)))
{
AfxMessageBox("Open table failed!");
pPtr.Release();
return FALSE;
}while(!pPtr->GetadoEOF())
{
_variant_t varNo;
_variant_t varName;
_variant_t varSex;
_variant_t varAddress;varNo = pPtr->GetCollect ("id");
varName = pPtr->GetCollect ("name");
varSex = pPtr->GetCollect ("gender");
varAddress = pPtr->GetCollect ("address");CString strNo =(char *)_bstr_t(varNo);
CString strName =(char *)_bstr_t(varName);
CString strSex =(char *)_bstr_t(varSex);
CString strAddress =(char *)_bstr_t(varAddress);strNo.TrimRight();
strName.TrimRight();
strSex.TrimRight();
strAddress.TrimRight();int nCount = m_list.GetItemCount();
int nItem = m_list.InsertItem (nCount,_T(""));
m_list.SetItemText (nItem,0,strNo);
m_list.SetItemText (nItem,1,strName);
m_list.SetItemText (nItem,2,strSex);
m_list.SetItemText (nItem,3,strAddress);pPtr->MoveNext();
}pPtr->Close();
pPtr.Release(); 6、使用_CommandPtr接口
_CommandPtr接口返回一个Recordset对象,并且提供了更多的记录集控制功能,以下代码示例了使用_CommandPtr接口的方法: 代码11:使用_CommandPtr接口获取数据
_CommandPtr pCommand;
_RecordsetPtr pRs;
pCommand.CreateInstance(__uuidof(Command));
pCommand->ActiveConnection=pConn;
pCommand->CommandText="select * from student";
pCommand->CommandType=adCmdText;
pCommand->Parameters->Refresh();
pRs=pCommand->Execute(NULL,NULL,adCmdUnknown);
_variant_t varValue = pRs->GetCollect("name");
CString strValue=(char*)_bstr_t(varValue); 6、关于数据类型转换由于COM对象是跨平台的,它使用了一种通用的方法来处理各种类型的数据,
因此Cstring 类和COM对象是不兼容的,我们需要一组API来转换COM对象和C++类型的数据。_vatiant_t和_bstr_t就是这样两种对象。它们提供了通用的方法转换COM对象和C++类型的数据。............................................................................................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字段值显示在列表控件中。在Visual C++中用ADO进行数据库编程(中)
      
  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:未知10、邦定数据  定义一个绑定类,将其成员变量绑定到一个指定的记录集,以方便于访问记录集的字段值。  (1). 从CADORecordBinding派生出一个类:class CCustomRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
sizeof(m_szau_fname), lau_fnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
sizeof(m_szau_lname), lau_lnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone,
sizeof(m_szphone), lphoneStatus, true)
END_ADO_BINDING()public:
CHAR m_szau_fname[22];
ULONG lau_fnameStatus;
CHAR m_szau_lname[42];
ULONG lau_lnameStatus;
CHAR m_szphone[14];
ULONG lphoneStatus;
};   其中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量,一个存放字段的值,另一个存放字段的状态。字段用从1开始的序号表示,如1,2,3等等。  特别要注意的是:如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。我分析多出的2可能是为了存放字符串结尾的空字符null和BSTR字符串开头的一个字(表示BSTR的长度)。这个问题对于初学者来说可能是一个意想不到的问题。  CADORecordBinding类的定义在icrsint.h文件里,内容是:class CADORecordBinding
{
public:
STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;
};BEGIN_ADO_BINDING宏的定义也在icrsint.h文件里,内容是:
#define BEGIN_ADO_BINDING(cls) public: \
typedef cls ADORowClass; \
const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { \
static const ADO_BINDING_ENTRY rgADOBindingEntries[] = { ADO_VARIABLE_LENGTH_ENTRY2宏的定义也在icrsint.h文件里:
#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)\
{Ordinal, \
DataType, \
0, \
0, \
Size, \
offsetof(ADORowClass, Buffer), \
offsetof(ADORowClass, Status), \
0, \
classoffset(CADORecordBinding, ADORowClass), \
Modify},#define END_ADO_BINDING宏的定义也在icrsint.h文件里:
#define END_ADO_BINDING() {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}};\
return rgADOBindingEntries;}    (2). 绑定_RecordsetPtr Rs1;
IADORecordBinding *picRs=NULL;
CCustomRs rs;
......
Rs1->QueryInterface(__uuidof(IADORecordBinding),
(LPVOID*)&picRs));
picRs->BindToRecordset(&rs);   派生出的类必须通过IADORecordBinding接口才能绑定,调用它的BindToRecordset方法就行了。  (3). rs中的变量即是当前记录字段的值//Set sort and filter condition:
// Step 4: Manipulate the data
Rs1->Fields->GetItem("au_lname")->Properties->GetItem("Optimize")->Value = true;
Rs1->Sort = "au_lname ASC";
Rs1->Filter = "phone LIKE '415 5*'";Rs1->MoveFirst();
while (VARIANT_FALSE == Rs1->EndOfFile)
{
printf("Name: %s\t %s\tPhone: %s\n",
(rs.lau_fnameStatus == adFldOK ? rs.m_szau_fname : ""),
(rs.lau_lnameStatus == adFldOK ? rs.m_szau_lname : ""),
(rs.lphoneStatus == adFldOK ? rs.m_szphone : ""));
if (rs.lphoneStatus == adFldOK)
strcpy(rs.m_szphone, "777");
TESTHR(picRs->Update(&rs)); // Add change to the batch
Rs1->MoveNext();
}
Rs1->Filter = (long) adFilterNone;
......
if (picRs) picRs->Release();
Rs1->Close();
pConn->Close();   只要字段的状态是adFldOK,就可以访问。如果修改了字段,不要忘了先调用picRs的Update(注意不是Recordset的Update),然后才关闭,也不要忘了释放picRs(即picRs->Release();)。  (4). 此时还可以用IADORecordBinding接口添加新纪录if(FAILED(picRs->AddNew(&rs)))
......   11. 访问长数据  在Microsoft SQL中的长数据包括text、image等这样长类型的数据,作为二进制字节来对待。  可以用Field对象的GetChunk和AppendChunk方法来访问。每次可以读出或写入全部数据的一部分,它会记住上次访问的位置。但是如果中间访问了别的字段后,就又得从头来了。  请看下面的例子://写入一张照片到数据库:
VARIANT varChunk;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];//VT_ARRAY │ VT_UI1
CFile f("h:\\aaa.jpg",Cfile::modeRead);
BYTE bVal[ChunkSize+1];
UINT uIsRead=0;
//Create a safe array to store the array of BYTES
while(1)
{
uIsRead=f.Read(bVal,ChunkSize);
if(uIsRead==0)break;
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);
for(long index=0;index{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))
::MessageBox(NULL,"啊,又出毛病了。","提示",MB_OK │ MB_ICONWARNING);
}
varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;
try{
m_pRecordset->Fields->GetItem("photo")->AppendChunk(varChunk);
}
catch (_com_error &e)
{
CString str=(char*)e.Description();
::MessageBox(NULL,str+"\n又出毛病了。","提示",MB_OK │ MB_ICONWARNING);
}
::VariantClear(&varChunk);
::SafeArrayDestroyData( psa);
if(uIsRead}//while(1)
f.Close();//从数据库读一张照片:
CFile f;
f.Open("h:\\bbb.jpg",Cfile::modeWrite│Cfile::modeCreate);
long lPhotoSize = m_pRecordset->Fields->Item["photo"]->ActualSize;
long lIsRead=0;_variant_t varChunk;
BYTE buf[ChunkSize];
while(lPhotoSize>0)
{
lIsRead=lPhotoSize>=ChunkSize? ChunkSize:lPhotoSize;
varChunk = m_pRecordset->Fields->
Item["photo"]->GetChunk(lIsRead);
for(long index=0;index{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
f.Write(buf,lIsRead);
lPhotoSize-=lIsRead;
}//while()
f.Close();
  12. 使用SafeArray问题  学会使用SafeArray也是很重要的,因为在ADO编程中经常要用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY│...,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。   使用SafeArray的具体步骤:  方法一:  包装一个SafeArray:  (1). 定义变量,如:VARIANT varChunk;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];   (2). 创建SafeArray描述符:uIsRead=f.Read(bVal,ChunkSize);//read array from a file.
if(uIsRead==0)break;
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);   (3). 放置数据元素到SafeArray:for(long index=0;index{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))
::MessageBox(NULL,"出毛病了。","提示",MB_OK │ MB_ICONWARNING);
}   一个一个地放,挺麻烦的。  (4). 封装到VARIANT内:varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;   这样就可以将varChunk作为参数传送出去了。  读取SafeArray中的数据的步骤:  (1). 用SafeArrayGetElement一个一个地读BYTE buf[lIsRead];
for(long index=0;index{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}   就读到缓冲区buf里了。  方法二:  使用SafeArrayAccessData直接读写SafeArray的缓冲区:  (1). 读缓冲区:BYTE *buf;
SafeArrayAccessData(varChunk.parray, (void **)&buf);
f.Write(buf,lIsRead);
SafeArrayUnaccessData(varChunk.parray);   (2). 写缓冲区:BYTE *buf;
::SafeArrayAccessData(psa, (void **)&buf);
for(long index=0;index{
buf[index]=bVal[index];
}
::SafeArrayUnaccessData(psa);varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;   这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData(psa),否则会出错的。  13. 使用书签( bookmark )  书签可以唯一标识记录集中的一个记录,用于快速地将当前记录移回到已访问过的记录,以及进行过滤等等。Provider会自动为记录集中的每一条记录产生一个书签,我们只需要使用它就行了。我们不能试图显示、修改或比较书签。ADO用记录集的Bookmark属性表示当前记录的书签。  用法步骤:  (1). 建立一个VARIANT类型的变量_variant_t VarBookmark;  (2). 将当前记录的书签值存入该变量  也就是记录集的Bookmark属性的当前值。VarBookmark = rst->Bookmark;  (3). 返回到先前的记录  将保存的书签值设置到记录集的书签属性中:// Check for whether bookmark set for a record
if (VarBookmark.vt == VT_EMPTY)
printf("No Bookmark set!\n");
else
rst->Bookmark = VarBookmark;   设置完后,当前记录即会移动到该书签指向的记录。
  14、设置过滤条件  Recordset对象的Filter属性表示了当前的过滤条件。它的值可以是以AND或OR连接起来的条件表达式(不含WHERE关键字)、由书签组成的数组或ADO提供的FilterGroupEnum枚举值。为Filter属性设置新值后Recordset的当前记录指针会自动移动到满足过滤条件的第一个记录。例如:rst->Filter = _bstr_t ("姓名='赵薇' AND 性别=’女’");   在使用条件表达式时应注意下列问题:  (1)、可以用圆括号组成复杂的表达式  例如:rst->Filter = _bstr_t ("(姓名='赵薇' AND 性别=’女’) OR AGE<25");   但是微软不允许在括号内用OR,然后在括号外用AND,例如:rst->Filter = _bstr_t ("(姓名='赵薇' OR 性别=’女’) AND AGE<25");   必须修改为:rst->Filter = _bstr_t ("(姓名='赵薇' AND AGE<25) OR (性别=’女’ AND AGE<25)");   (2)、表达式中的比较运算符可以是LIKE  LIKE后被比较的是一个含有通配符*的字符串,星号表示若干个任意的字符。  字符串的首部和尾部可以同时带星号*rst->Filter = _bstr_t ("姓名 LIKE '*赵*' ");   也可以只是尾部带星号:rst->Filter = _bstr_t ("姓名 LIKE '赵*' ");   Filter属性值的类型是Variant,如果过滤条件是由书签组成的数组,则需将该数组转换为SafeArray,然后再封装到一个VARIANT或_variant_t型的变量中,再赋给Filter属性。  15、索引与排序  (1)、建立索引  当以某个字段为关键字用Find方法查找时,为了加快速度可以以该字段为关键字在记录集内部临时建立索引。只要将该字段的Optimize属性设置为true即可,例如:pRst->Fields->GetItem("姓名")->Properties->
GetItem("Optimize")->PutValue("True");
pRst->Find("姓名 = '赵薇'",1,adSearchForward);
......
pRst->Fields->GetItem("姓名")->Properties->
GetItem("Optimize")->PutValue("False");
pRst->Close();   说明:Optimize属性是由Provider提供的属性(在ADO中称为动态属性),ADO本身没有此属性。  (2)、排序  要排序也很简单,只要把要排序的关键字列表设置到Recordset对象的Sort属性里即可,例如:pRstAuthors->CursorLocation = adUseClient;
pRstAuthors->Open("SELECT * FROM mytable",
_variant_t((IDispatch *) pConnection),
adOpenStatic, adLockReadOnly, adCmdText);
......
pRst->Sort = "姓名 DESC, 年龄 ASC";   关键字(即字段名)之间用逗号隔开,如果要以某关键字降序排序,则应在该关键字后加一空格,再加DESC(如上例)。升序时ASC加不加无所谓。本操作是利用索引进行的,并未进行物理排序,所以效率较高。
但要注意,在打开记录集之前必须将记录集的CursorLocation属性设置为adUseClient,如上例所示。Sort属性值在需要时随时可以修改。  16、事务处理  ADO中的事务处理也很简单,只需分别在适当的位置调用Connection对象的三个方法即可,这三个方法是:  (1)、在事务开始时调用pCnn->BeginTrans();   (2)、在事务结束并成功时调用pCnn->CommitTrans ();   (3)、在事务结束并失败时调用pCnn->RollbackTrans ();   在使用事务处理时,应尽量减小事务的范围,即减小从事务开始到结束(提交或回滚)之间的时间间隔,以便提高系统效率。需要时也可在调用BeginTrans()方法之前,先设置Connection对象的IsolationLevel属性值,详细内容参见MSDN中有关ADO的技术资料。  三、使用ADO编程常见问题解答  以下均是针对MS SQL 7.0编程时所遇问题进行讨论。  1、连接失败可能原因  Enterprise Managemer内,打开将服务器的属性对话框,在Security选项卡中,有一个选项Authentication。  如果该选项是Windows NT only,则你的程序所用的连接字符串就一定要包含Trusted_Connection参数,并且其值必须为yes,如:"Provider=SQLOLEDB;Server=888;Trusted_Connection=yes"
";Database=master;uid=lad;";   如果不按上述操作,程序运行时连接必然失败。  如果Authentication选项是SQL Server and Windows NT,则你的程序所用的连接字符串可以不包含Trusted_Connection参数,如:"Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;";   因为ADO给该参数取的默认值就是no,所以可以省略。我认为还是取默认值比较安全一些。  2、改变当前数据库的方法  使用Tansct-SQL中的USE语句即可。  3、如何判断一个数据库是否存在  (1)、可打开master数据库中一个叫做SCHEMATA的视图,其内容列出了该服务器上所有的数据库名称。  (2) 、更简便的方法是使用USE语句,成功了就存在;不成功,就不存在。例如:try{
m_pConnect->Execute ( _bstr_t("USE INSURANCE_2002"),NULL,
adCmdText│adExecuteNoRecords );
}
catch (_com_error &e)
{
blSuccess=FALSE;
CString str="数据库INSURANCE_2002不存在!\n";
str+=e.Description();
::MessageBox(NULL,str,"警告",MB_OK │ MB_ICONWARNING);
}   4、判断一个表是否存在  (1)、同样判断一个表是否存在,也可以用是否成功地打开它来判断,十分方便,例如:try{
m_pRecordset->Open(_variant_t("mytable"),
_variant_t((IDispatch *)m_pConnection,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,"该表不存在。","提示",MB_OK │ MB_ICONWARNING);
}   (2)、要不然可以采用麻烦一点的办法,就是在MS-SQL服务器上的每个数据库中都有一个名为sysobjects的表,查看此表的内容即知指定的表是否在该数据库中。  (3)、同样,每个数据库中都有一个名为TABLES的视图(View),查看此视图的内容即知指定的表是否在该数据库中。  5、类型转换问题  (1)、类型VARIANT_BOOL  类型VARIANT_BOOL等价于short类型。The VARIANT_BOOL is equivalent to short. see it's definition below:
typdef short VARIANT_BOOL  (2)、_com_ptr_t类的类型转换  _ConnectionPtr可以自动转换成IDspatch*类型,这是因为_ConnectionPtr实际上是_com_ptr_t类的一个实例,而这个类有此类型转换函数。  同理,_RecordsetPtr和_CommandPtr也都可以这样转换。  (3)、_bstr_t和_variant_t类  在ADO编程时,_bstr_t和_variant_t这两个类很有用,省去了许多BSTR和VARIANT类型转换的麻烦。  6、打开记录集时的问题  在打开记录集时,在调用Recordset的Open方法时,其最后一个参数里一定不能包含adAsyncExecute,否则将因为是异步操作,在读取数据时无法读到数据。  7、异常处理问题  对所有调用ADO的语句一定要用try和catch语句捕捉异常,否则在发生异常时,程序会异常退出。  8、使用SafeArray问题  在初学使用中,我曾遇到一个伤脑筋的问题,一定要注意:  在定义了SAFEARRAY的指针后,如果打算重复使用多次,则在中间可以调用::SafeArrayDestroyData释放数据,但决不能调用::SafeArrayDestroyDescriptor,否则必然出错,即使调用SafeArrayCreate也不行。例如:SAFEARRAY *psa;
......
//When the data are no longer to be used:
::SafeArrayDestroyData( psa);   我分析在定义psa指针时,一个SAFEARRAY的实例(也就是SAFEARRAY描述符)也同时被自动建立了。但是只要一调用::SafeArrayDestroyDescriptor,描述符就被销毁了。  所以我认为::SafeArrayDestroyDescriptor可以根本就不调用,即使调用也必须在最后调用。  9、重复使用命令对象问题  一个命令对象如果要重复使用多次(尤其是带参数的命令),则在第一次执行之前,应将它的Prepared属性设置为TRUE。这样会使第一次执行减慢,但却可以使以后的执行全部加快。  10、绑定字符串型字段问题  如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。  11、使用AppendChunk的问题  当用AddNew方法刚刚向记录集内添加一个新记录之后,不能首先向一个长数据字段(image类型)写入数据,必须先向其他字段写入过数据之后,才能调用AppendChunk写该字段,否则出错。也就是说,AppendChunk不能紧接在AddNew之后。另外,写入其他字段后还必须紧接着调用AppendChunk,而不能调用记录集的Update方法后,才调用AppendChunk,否则调用AppendChunk时也会出错。换句话说,就是必须AppendChunk在前,Update在后。因而这个时候就不能使用带参数的AddNew了,因为带参数的AddNew会自动调用记录集的Update,所以AppendChunk就跑到Update的后面了,就只有出错了!因此,这时应该用不带参数的AddNew。  我推测这可能是MS SQL 7.0的问题,在MS SQL 2000中则不存在这些问题,但是AppendChunk仍然不能在Update之后。  四、小结  一般情况下,Connection和Command的Execute用于执行不产生记录集的命令,而Recordset的Open用于产生一个记录集,当然也不是绝对的。特别Command主要是用于执行参数化的命令,可以直接由Command对象执行,也可以将Command对象传递给Recordset的Open。
 
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
ado,全称 Activex Data Objects,他是微软公司为数据库应用程序开发的com集合。ado模型简单易学,功能强大,数据快。ado中包含7个核心对象,和4个集合:对象:Connection,Command,Recordset,Field,Parameter,Error,Property
集合:parameters,fields,properties,errors在本文中我们不对ado本身做太多的描述,只讲解其在vc++中的使用技巧。如果您对ado还很陌生,建议先阅读相关书籍。如果想使用ADO,需要在要使用ADO的c++程序的头文件(*.h)中加入如下代码,引入ado对象:#import c:programe filescommon filessystemadomsado15.dll no_namespace rename("EOF,"ADOEOF") rename("BOF","ADOBOF")
 默认情况下msado15 位于c:\program files\common files\system\ado\ 。 我们一般都是按照上面的写法书写,当然也可以指定一个名称空间,或者将EOF 和 BOF 更名为其他什么的,都可以。如果想使用ADO,还需要对OLE/COM库进行初始化。::CoInitialize ( NULL ) ;
定义并实例化一个connection对象的方法如下:注意虽然conn是一个指针,但是依然不用->,因为CreateInstance是指针的方法。_ConnectionPtr  conn;
conn.CreateInstance("adodb.connection");
打开一个数据库连接的方法如下,以access数据库为例。_bstr_t connsql="provider=microsoft.jet.oledb.4.0;data source=northsnow.mdb";
conn->Open(connsql,"","",adModeUnknown);
定义一个记录及对象,初始化,打开一个记录集的方法如下:_RecordsetPtr rs;
rs.CreateInstance("adodb.recordset");
rs->Open("select * from t order by a",conn.GetInterfacePtr(),adOpenStatic,adLockReadOnly,adCmdText);
       
利用conn对象直接执行sql语句的方法如下:_variant_t dC;
_bstr_t sql="delete from t where a=" + m_l.GetItemText(nSelected,NULL);conn->Execute(sql,&dC,adCmdText);
if(dC.lVal>=0)
...{
  //删除成功;
}由于ado会经常出现莫名其妙的错误,所以需要用try/catch捕捉错误。一个相对较完整的代码如下://初始化ado
bool CAdo1Dlg::initAdo()
...{    if(conn==NULL)
    ...{
        ::CoInitialize(NULL);
        HRESULT hr;
        hr=conn.CreateInstance("adodb.connection");
        try
        ...{
            hr=conn.CreateInstance("adodb.connection");
            if(SUCCEEDED(hr))
            ...{
                conn->Open("provider=microsoft.jet.oledb.4.0;data source=db.mdb","","",adModeUnknown);
            }
            return 1;
        }
        catch(_com_error e)
        ...{
            CString strError;
            strError.Format("error:%s",e.Description());
            AfxMessageBox(strError);
            return 0;
        }
   
    }
    else
    ...{
        return 1;
    }
} //添加listcontrol列头
     m_l.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
    m_l.InsertColumn(0,"code",LVCFMT_RIGHT,50);
    m_l.InsertColumn(1,"name",LVCFMT_LEFT,100);
    m_l.InsertColumn(2,"age",LVCFMT_RIGHT,50);
    m_l.InsertColumn(3,"birth",LVCFMT_RIGHT,200);//向listcontrol添加内容:void CAdo1Dlg::LoadData()
...{
    m_l.DeleteAllItems();
    if(initAdo()==1)
    ...{
        _RecordsetPtr rs;
        HRESULT hr;
        int i=0;
        try
        ...{
            rs.CreateInstance("adodb.recordset");
            rs->Open("select * from t order by a",conn.GetInterfacePtr(),adOpenStatic,adLockReadOnly,adCmdText);
            while(!rs->ADOEOF)
            ...{
                m_l.InsertItem(i,(LPCTSTR)(_bstr_t)rs->GetCollect("a"));
                m_l.SetItemText(i,1,(LPCTSTR)(_bstr_t)rs->GetCollect("b"));
                m_l.SetItemText(i,2,(LPCTSTR)(_bstr_t)rs->GetCollect("c"));
                m_l.SetItemText(i,3,(LPCTSTR)(_bstr_t)rs->GetCollect("d"));
                i++;
                rs->MoveNext();
            }
        }
        catch(_com_error e)
        ...{
            AfxMessageBox(e.Description());
        }
    }
}
//向数据库添加信息
    UpdateData(true);
    if(m_b.GetLength()==0)
    ...{
        AfxMessageBox("a项不能为空!",NULL,NULL);
    }
    CString strD = m_d.Format("%Y-%m-%d %H:%M:%S");
    _bstr_t sql="insert into t(b,c,d) values('" + m_b + "'," + CStr(m_c) + ",'" + strD + "')";
    AfxMessageBox(sql);
    try
    ...{
        if(initAdo()==1)
        ...{
            conn->Execute(sql,NULL,adCmdText);
        }       
    }
    catch(_com_error e)
    ...{
        CString strError;
        strError.Format("error:%s",e.Description());
        AfxMessageBox(strError,NULL,NULL);
    }//删除一条信息
    if(MessageBox("do you really want to delete the infomation?","prompt",1)==IDOK)
    ...{
        int nSelected=-1;
        nSelected=m_l.GetNextItem(nSelected,LVNI_SELECTED);
        if(nSelected>=0)
        ...{
            if(initAdo()==1)
            ...{
                _variant_t dC;
                _bstr_t sql="delete from t where a=" + m_l.GetItemText(nSelected,NULL);
                try
                ...{
                    conn->Execute(sql,&dC,adCmdText);
                    if(dC.lVal>=0)
                    ...{
                        LoadData();
                    }
                }
                catch(_com_error e)
                ...{
                    AfxMessageBox(e.Description());
                }
            }
        }
    }//两个类型转换的程序:CString CAdo1Dlg::CStr(long s)
...{
   CString mm;
   mm.Format("%d",s);
   return mm;
}CString CAdo1Dlg::DblToStr(double s)
...{   CString mm;
   mm.Format("%f",s);
   return mm;
}
上面的程序中只包含了核心的代码。没有包含一些界面设置,变量的定义等细节信息。所以请借用代码的时候注意一下。代码中还有几项需要注意:1,catch 捕捉的错误的类型为 _com_error,其产生的错误描述信息为: e.Description()2,listcontrol中获得当前选择行的方法为:int nSelected=-1;
nSelected=m_l.GetNextItem(nSelected,LVNI_SELECTED);
3,listcontrol中获得当前选择行的某个itemtext的方法为:
m_l.GetItemText ( nSelected,NULL )
//其中第二个参数表示是第几个subitem。如果为NULL,则表示为Item
当关闭记录集的时候为如下代码:if(rs!=NULL)
   rs->Close();
if(conn!=NULL)
   conn->Close();
::CoUninitialize (); 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/precipitant/archive/2006/10/10/1329166.aspx
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、//////////////////////////////////////////////////////////
【3】创建Connection对象并连接数据库  
  首先我们需要添加一个指向Connection对象的指针:  
  _ConnectionPtr   m_pConnection;  
  下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。  
   
  BOOL   CADOTest1Dlg::OnInitDialog()   {  
  CDialog::OnInitDialog();  
  HRESULT   hr;  
  try   {  
  hr   =   m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象  
   
  if(SUCCEEDED(hr))    
  {  
  hr   =   m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data   Source=test.mdb","","",adModeUnknown);  
  ///连接数据库   ///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51;  
  }  
  }    
  catch(_com_error   e)///捕捉异常    
  {  
  CString   errormessage;   errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());   AfxMessageBox(errormessage);///显示错误信息   }    
   
  在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型  
  HRESULT   Connection15::  
  Open  
  (_bstr_t   ConnectionString,   _bstr_t   UserID,   _bstr_t   Password,   long   Options   )  
  ConnectionString为连接字串,UserID是用户名,   Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权,  
   
  Options可以是如下几个常量:  
  adModeUnknown:缺省。当前的许可权未设置  
  adModeRead:只读  
  adModeWrite:只写  
  adModeReadWrite:可以读写  
  adModeShareDenyRead:阻止其它Connection对象以读权限打开连接  
  adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接  
  adModeShareExclusive:阻止其它Connection对象打开连接  
  adModeShareDenyNone:允许其它程序或对象以任何权限建立连接  
   
  我们给出一些常用的连接方式供大家参考:  
  (1)通过JET数据库引擎对ACCESS2000数据库的连接  
   
  m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data   Source=C:\\test.mdb","","",adModeUnknown);    
   
  (2)通过DSN数据源对任何支持ODBC的数据库进行连接:  
   
  m_pConnection->Open("Data   Source=adotest;UID=sa;PWD=;","","",adModeUnknown);    
   
  (3)不通过DSN对SQL   SERVER数据库进行连接:    
   
  m_pConnection->  
  Open(  
  "driver={SQLServer};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown  
  );  
   
  其中Server是SQL服务器的名称,DATABASE是库的名称  
   
  Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State  
  ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如:  
   
  m_pConnection->ConnectionTimeout   =   5;///设置超时时间为5秒  
   
  m_pConnection->Open("Data   Source=adotest;","","",adModeUnknown);  
   
  State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:  
   
  if(m_pConnection->State)   m_pConnection->Close();   ///如果已经打开了连接则关闭它