天天看点

VC++ ADO连接SQL Server问题与解决方案

转自:http://blogger.org.cn/blog/more.asp?name=fishyqd&id=13038

(1)建立ODBC数据源。

参考方法:http://www.gz9f.com/jiaocai/hcc/hcc6/hcc6-p1/hcc6-p1.htm

牢记:在此之前要把自己的数据库服务器启动,不然在服务器选择的时候看不到自己的服务器。

(2)

   在工程的stdafx.h里用#import引入ADO库文件。

  

  #import "C:/Program Files/Common Files/System/ado/msado15.dll" no_namespace rename("BOF","adoBOF") rename("EOF","adoEOF")

   牢记:一定要在所有的#include后加入这句话,不然会出现。fatal error C1189: #error :  WINDOWS.H already included.  MFC apps must not #:nclude <windows.h>

我是参考了如下文章:http://www.allife.org/index.php?job=art&articleid=a_20060116_134912

原文引用如下:

非MFC工程使用MFC库时的问题及解决办法

2006年1月16日13:49星期一  [笔记 ]

文章来源: 李世平的专栏

一、问题由来

vc6 和 vc71 的工程向导中都包含非 MFC 的工程,诸如 win32 console project, win32 static library 。非 MFC 工程创建时是不支持 MFC 特性的,然后我们在处理实际问题时有时会用到 MFC 相关类,如 Cstring, Cedit 等等,这是很正常的。可能有人会说,为何不在一开始就创建 MFC 工程呢?问题在于 MFC 工程会产生很多向导生成代码,如基于单文档的工程会有 View,Doc 等类,很多时候我们只需要一个空工程就可以了。

二、常见问题

非 MFC 工程使用 MFC 库时最常见的问题就是 windows.h 重复包含错误,具体如下:

fatal error C1189: #error :  WINDOWS.H already included.  MFC apps must not #include <windows.h>

三、解决办法

非 MFC 工程使用 MFC 库时,可参考以下步骤

1 、工程设置中,将 MFC 的使用由原来的“使用标准 windows 库”改为“在共享 DLL 中使用 MFC ”( VC71 )

如果是英文版,相关选项是:

Microsoft Foundation Classes: Use MFC in a shared dll, no using MFC ( VC6 )

NOTE: 因为我用的是中文版的 vc71, 英文版的 vc6.

2 、头文件包含

不同的 MFC 类需包含的头文件是不一样的。

常用的类,如 Cstring, Cedit 等,包含 afxwin.h 就可以了

如果不清楚包含什么头文件的话,可以同 msdn 进行查询, msdn 中,对于 MFC 类的介绍中,都会给出相应的 header file requirement.

3 、# include 语句一定要写在首行

这一点很重要,通常出现前面讲到的 windows.h 重复包含错误,都是因为 #include 语句没有写在首行。

另外还要注意的是,如果 #include 语句是在一个头文件里,那么对应头文件的包含也要写在首行。示例如下:

=============

test.h 文件的内容如下:

# include <afxwin.h> // 保证该语句在首行

test.cpp 的文件内容如下:

#include “test.h” // 同样也要保证该语句在首行

=============

ps: 这么做的具体原因我也不知道,我是在实际调试中琢磨出这个道理的。我自己在这个问题上花了很多冤枉时间,写下该篇,就是希望大家不要在这个问题上绊脚。

(3)在进行连接之前要初始化OLE环境,初始化语句

   ::CoInitialize(NULL); //初始化OLE/COM库环境 

  

  AfxOleInit();//初始化OLE/COM库环境(MFC自带的)

    这两句话缺一不可,不然会有运行期错误

    程序运行结束后记住::CoUninitialize();  //关闭OLE/COM库环境,释放资源

最后推荐一篇文章:http://farwen.com/ReadNews.asp?NewsID=3279

全文引用如下:

ADO数据库编程入门
2004-11-16 19:55:41  天宇网苑  Keaton  阅读301次

1、使用ADO编程的方法有三种:

(1)使用预处理指令#import,例如:

#import "c:/Program Files/Common Files/System/ado/msado15.dll" no_namespace rename("EOF", "adoEOF") rename("BOF", "adoBOF")

程 序在编译时读取msado15.dll中的类型库信息,自动生成两个该类型库的头文件和实现文件msado15.tlh和msado15.tli(在 Debug或Release目录下)。两个文件中定义了ADO的所有对象和方法,以及一些枚举类型的变量,程序只要直接调用这些方法即可。

(2)通过读取msado15.dll中的类型库信息,建立一个ColeDispatchDriver类的派生类,然后通过它调用ADO对象。

(3)直接使用COM提供的API,例如:

 CLSID clsid;

 HRESULT hr = ::CLSIDFromProgID(L"ADODB.Connection", &clsid);

 if (FAILED(hr))

 {

  ...

 }

 ::CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDispatch);

 if (FAILED(hr))

 {

  ...

 }

前两种方法类似,第3种方法编程可能最麻烦,但效率最高,程序尺寸最小,并且对ADO的控制能力也最强。

2、以下使用#import方法操作数据库

(1)可以在stdafx.h的所有include指令之后import

(2)使用AfxOleInit()初始化COM库,通常在CwinApp::InitInstance的重载函数中添加。

(也可以使用::CoInitialize(NULL),之后在ExitInstance中调用::CoUninitialize)

(3)定义_ConnectionPtr变量后调用Connection对象的Open方法建立与服务器的连接。

数据类型_ConnectionPtr实际上是由类模板_com_ptr_t得到的一个具体的实例类。_ConnectionPtr类封装了Connection对象的Idispatch接口指针及其一些必要的操作。可以通过这个指针操纵Connection对象。

例如连接SQLServer数据库,代码如下:

 // 连接到MS SQL Server

 _ConnectionPtr pMyConnect = NULL;

 HRESULT hr = pMyConnect.CreateInstance(__uuidof(Connection));

 if (FAILED(hr))

  return;

 _bstr_t strConnect = "Provider=SQLOLEDB; Server=hch; Database=mytest; uid=sa; pwd=sa;";

 try

 {

  // Open方法连接字串必须四BSTR或者_bstr_t类型

  pMyConnect->Open(strConnect, "", "", NULL);

 }

 catch(_com_error &e)

 {

  MessageBox(e.Description(), "警告", MB_OK|MB_ICONINFORMATION);

 }

(4)

 //定义_RecordsetPtr变量,调用它Recordset对象的Open,即可打开一个数据集

 _RecordsetPtr pRecordset;

 if (FAILED(pRecordset.CreateInstance(__uuidof(Recordset))))

 {

  return;

 }

 try

 {

  pRecordset->Open(_variant_t("userinfo"), _variant_t((IDispatch*)pMyConnect),

   adOpenKeyset, adLockOptimistic, adCmdTable);

 }

 catch (_com_error &e)

 {

  MessageBox("无法打开userinfo表", "系统提示", MB_OK|MB_ICONINFORMATION);

 }

(5)

 //定义_RecordsetPtr变量,调用它Recordset对象的Open,即可打开一个数据集

 _RecordsetPtr pRecordset;

 if (FAILED(pRecordset.CreateInstance(__uuidof(Recordset))))

 {

  return;

 }

 try

 {

  pRecordset->Open(_variant_t("userinfo"), _variant_t((IDispatch*)pMyConnect),

   adOpenKeyset, adLockOptimistic, adCmdTable);

 }

 catch (_com_error &e)

 {

  MessageBox("无法打开userinfo表", "系统提示", MB_OK|MB_ICONINFORMATION);

 }

(6)

 // 读取当前记录集

 try

 {

  pRecordset->MoveFirst();

  while (pRecordset->adoEOF == VARIANT_FALSE)

  {

   // Fields是Recordset对象的容器,GetItem方法返回Field对象,Value是Field对象的属性,也可以用GetValue方法

   CString sName = (char*)(_bstr_t)(pRecordset->Fields->GetItem(_variant_t("UserName"))->Value);

   // 或者使用GetValue()

   //CString sName = (char*)(_bstr_t)(pRecordset->Fields->GetItem(_variant_t("UserName"))->GetValue());

   AfxMessageBox(sName);

   pRecordset->MoveNext();

  }

 }

 catch (_com_error &e)

 {

  MessageBox(e.Description(), "系统提示", MB_OK|MB_ICONINFORMATION);

 }

(7)

 // 修改记录

 try

 {

  pRecordset->MoveFirst();

  while(pRecordset->adoEOF == VARIANT_FALSE)

  {

   pRecordset->Fields->GetItem(_variant_t("Address"))->Value = _bstr_t("北京大学");

   pRecordset->Update();

   pRecordset->MoveNext();

  }

 }

 catch (_com_error &e)

 {

  MessageBox(e.Description(), "系统提示", MB_OK|MB_ICONINFORMATION);

 }

(8)添加,删除,使用带参数的命令,相应ADO的通知事件,绑定数据,设置过滤条件,索引和排序,事务处理。(略去,请参考其它资料)

相关专题: 原创文章
专题信息:
VC++ ADO连接SQL Server问题与解决方案
 Textarea中防止尖括号被替换 (2004-12-30 16:29:17)[414 ]
VC++ ADO连接SQL Server问题与解决方案
 在视图(CEditView)中设置字体 (2004-12-29 10:52:20)[324 ]
VC++ ADO连接SQL Server问题与解决方案
 随机产生23个4位数(每位数字不能重复) (2004-12-28 10:24:56)[266 ]
VC++ ADO连接SQL Server问题与解决方案
 输入密码不回显(显示*号)验证密码 (2004-12-28 10:03:15)[433 ]
VC++ ADO连接SQL Server问题与解决方案
 cin输入字符串时的经典Bug (2004-12-27 15:01:48)[310 ]
[更多... ]
相关信息:
 没有相关信息
相关评论:
发表人:keaton
发表人邮件:[email protected] 发表时间:2005-5-9 13:47:13

下面让我们看看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);

最后,如果想知道详细实现细节的话,可以在下载示例源码后,仔细查看源码既可(内有详细注释)。