文章來源:
李世平的專欄
一、問題由來
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 |
|
|
下面讓我們看看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);
最後,如果想知道詳細實現細節的話,可以在下載示例源碼後,仔細查看源碼既可(內有詳細註釋)。 |
|
|