數據庫操作組件的ATL實現
COM表示Component Object Model即組件對象模型,是Microsoft生成軟件組件的標準。它最大的優點在於它是二進制兼容軟件組件的規範,即不論用什麼語言生成COM組件,它都與其它COM組件兼容和可供其它組件使用。用VC++進行組件開發通常使用ATL (Active Template Library)來進行,在教材的第11章對數據庫的操作我們使用了ADO技術,在此我們使用COM組件來實現數據庫的操作。
下面詳細介紹數據庫操作組件的實現步驟:
1. 服務器端的實現
第一步建立ATL工程,選ATL Project(如圖1-1所示),輸入工程名字DBMan,接下來再選Dynamic-link library (DLL) 並保留所有默認選項,確定。編譯該工程,會生成組件的宿主DLL和IDL相關文件。
圖1-1
表1-1列舉出了ATL AppWizard生成的工程文件:
文件名稱 |
說明 |
DBMan.cpp |
主工程文件,包含了COM所需的支持函數,爲組件提供宿主文件支持。 |
DBMan.h |
組件在宿主文件裏的接口聲明,MIDL編譯器自動生成了該文件。編譯工程的IDL文件就是爲了生成該文件。 |
DBMan.idl |
工程的IDL文件,可以在該文件中添加接口和方法的定義。MIDL編譯器處理該文件併產生工程的類型庫文件。一個工程只能有一個IDL文件,工程中的所有組件共享該IDL文件。 |
DBMan.def |
DLL工程的def文件。 |
DBMan_i.c |
該文件包含了工程中所有CLSID和IID的定義,由IDL文件經MIDL編譯後生成。 |
DBMan_p.c |
工程的代理/駐留(proxy/stub)代碼,由MIDL編譯器生成。 |
Dlldata.c |
代理/駐留(proxy/stub)DLL文件的數據結構定義。 |
DBMan.rc |
工程的資源文件。 |
DBMan.rgs |
工程的註冊腳本。 |
Resource.h |
工程的資源定義文件。 |
DBManps.def |
DLL工程的擴展def文件。 |
Stdafx.h Stdafx.cpp |
ATL的框架定義和包含信息。 |
表1-1
第二步添加DataAcqDBMan組件,選擇Project | Add Class...菜單項,彈出Add Class對話框,選擇ATL | ATL Simple Object選項,如圖1-2 所示:
圖1-2
點擊Add按鈕,彈出ATL Simple Object Wizard屬性對話框,在Names屬性頁的Short name項鍵入DataAcqDBMan,該頁面的其它項目會自動填入,如圖1-3所示,其中上邊C++組中,Short name爲本頁面的其它選項提供一個基礎名稱,Class是實現該組件的C++類的名稱,
.h file 和.cpp file爲組件類的頭文件和實現文件名稱。下邊COM組中,Coclass表示COM類的名稱,Interface爲所創建對象的接口名稱,Type是存放在註冊表中可讀的組件名稱,ProgID是該組件的progid名稱。
圖1-3
點擊Next按鈕,彈出Options對話框,如圖1-4所示,在Options屬性對話框中可以給組件指定基本的COM支持選項,下邊給出簡單的描述。具體細節請參見MSDN文檔。
圖1-4
1. Threading model
a) Single,組件的實例只能在某一進程的主線程裏創建
b) Apartment,組件的實例存在於其自身的單元線程中(Sigle Threaded Apartment, STA)
c) Free,組件必須和其他線程一起位於多線程單元裏(MultiThreaded Apartment, MTA)
d) Neutral,指定對象遵守多線程單元的指南,但它可以在任何線程類型上執行
2. Interface
a) Custom,指定對象支持自定義接口(其vtable具有自定義接口函數)
b) Dual,即實現了vtable接口也實現標準的自動化接口
l Automation compatible,允許自動化控制器訪問具有自定義接口支持的對象
3. Aggregation,該項技術可使得一個組件可以兼併或複用其他組件的功能
a) Yes,對組件可以被其他組件聚合(Aggregation)提供支持
b) No,不允許組件被其他組件聚合
c) Only,只允許組件的實例充當聚合
4.Support ISupportErrorInfo,該選項提供了一個服務器到客戶端(Server to Client)的錯誤彙報機制
5.Free Threaded Marshaler,提供了在單進程的線程裏以默認的方式對接口指針進行線程間的參數調度的支持
6.Connection points,提供了服務器的回叫信號或事件,使客戶程序和服務器可以在對等的基礎上彼此通訊
7. IObjectWithSite (IE object support),實現 IObjectWithSiteImpl,它爲支持對象和它在容器中的站點之間的通信提供了一種簡單的方法。
按照圖1-3和圖1-4所示進行設置,然後點擊Finish按鈕,圖1-5顯示了工程的變化。
圖1-5
由ObjectWizard創建的文件:
文件名稱 |
說明 |
DataAcqDBMan.h DataAcqDBMan.cpp |
組件對象的頭文件和實現文件。 |
DataAcqDBMan.rgs |
組件對象的註冊腳本。 |
第三步利用嚮導給IDataAcqDBMan接口添加方法,如圖1-6所示:
圖1-6
此接口共添加三個方法,實現如下:
1) 連接數據庫
STDMETHODIMP CDataAcqDBMan::ConnectDB(BSTR ConnStr, BSTR UserId, BSTR Password, VARIANT_BOOL* pbResult)
{
// TODO: 在此添加實現代碼
_bstr_t ConnectStr;
ConnectStr=ConnStr;
*pbResult = FALSE ;
HRESULT hr ;
try
{
hr = pConnection.CreateInstance(__uuidof(Connection));
if (hr == S_OK)
{
pConnection->Mode = adModeReadWrite;
pConnection->ConnectionTimeout = ConnectTimeOut;
pConnection->CursorLocation = adUseClient;
pConnection->Open(ConnectStr, UserId, Password, adOpenUnspecified);
if (pConnection->State == adStateOpen )
{
*pbResult = TRUE ;
}
}
}
catch(_com_error &e)
{
GenerateError(e.Description());
LogAdoErrorImport(pConnection);
return S_FALSE;
}
return S_OK;
}
2)斷開與數據庫的連接
STDMETHODIMP CDataAcqDBMan::DisConnect(void)
{
// TODO: 在此添加實現代碼
try
{
if (pConnection->State == adStateOpen)
{
pConnection->Close() ;
pConnection = NULL;
}
}
catch(_com_error &e)
{
GenerateError(e.Description());
return S_FALSE;
}
return S_OK;
}
3)執行SQL語句
STDMETHODIMP CDataAcqDBMan::Execute(BSTR strSql, VARIANT_BOOL* pbResult)
{
// TODO: 在此添加實現代碼
*pbResult = FALSE;
try
{
if(pConnection->Execute(strSql, NULL, -1))
*pbResult = TRUE;
}
catch(_com_error &e)
{
GenerateError(e.Description());
return S_FALSE;
}
return S_OK;
}
其它成員函數可參考示例代碼和MSDN文檔。
2.客戶端的實現
1)初始化COM組件、創建實例
在應用程序類中添加成員函數 void CoInitInstance(void),按照示例代碼錄入函數的實現體。並在應用程序初始化時調用。
2)連接數據庫
在主業務類中添加成員函數void ConnectToDb(),按照示例代碼錄入函數的實現體,並在主對話框初始化時調用。
3)數據庫寫日誌操作
void CSysLogRec::BeginProcess() { VARIANT_BOOL pbResult = FALSE;
EnterCriticalSection(&CBaseThread::m_csNumLock); { m_pDBMan->Execute(_bstr_t(GetLogWriteSql()), &pbResult); if(!pbResult) AfxMessageBox(STR_LOGREC_ROUTE_ERR); } LeaveCriticalSection(&CBaseThread::m_csNumLock); } |
4)斷開與數據庫的連接、釋放接口引用並刪除COM初始化
在應用程序退出實例中添加以下代碼:
int CDataAcqApp::ExitInstance() { // TODO: Add your specialized code here and/or call the base class CBaseThread::m_pDBMan->DisConnect(); CBaseThread::m_pDBMan->Release(); CoUninitialize(); DeleteCriticalSection(&CBaseThread::m_csNumLock); CloseHandle(CBaseThread::m_hAnotherDead); return CWinApp::ExitInstance(); } |
其它成員函數的使用方法可參考MSDN和實例程序.
示例程序附後.