天天看點

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);

最後,如果想知道詳細實作細節的話,可以在下載下傳示例源碼後,仔細檢視源碼既可(内有詳細注釋)。