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的通知事件,綁定數據,設置過濾條件,索引和排序,事務處理。(略去,請參考其它資料)

 


相關專題: 原創文章
專題信息:
   Textarea中防止尖括號被替換 (2004-12-30 16:29:17)[414 ]
   在視圖(CEditView)中設置字體 (2004-12-29 10:52:20)[324 ]
   隨機產生23個4位數(每位數字不能重複) (2004-12-28 10:24:56)[266 ]
   輸入密碼不回顯(顯示*號)驗證密碼 (2004-12-28 10:03:15)[433 ]
   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);
最後,如果想知道詳細實現細節的話,可以在下載示例源碼後,仔細查看源碼既可(內有詳細註釋)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章