OLEDB数据源
數據源在oledb中指數據提供者,這里可以簡單的理解為數據庫程序。數據源對象代表數據庫的一個連接,是需要創建的第一個對象。而數據源對象主要用于配置數據庫連接的相關屬性如連接數據庫的用戶名密碼等等
數據源主要完成的功能如下:
1. 進行數據庫身份認證
2. 為每個連接準備對應的資源,如對應的數據緩沖,網絡連接資源
3. 設置連接屬性,給訪問者何種權限,設置連接的超時值等等,對象會根據對應的屬性打開對應的接口。它的這些設置都是通過屬性進行的
OLEDB屬性與屬性設置
OLEDB雖然是基于COM的一組接口,但是它與標準的COM接口有點不同,它的一大特色在于它自身的屬性設置,有的接口雖然對象中存在但是調用QueryInterface是查詢不出來的,只有設置相應的接口才會打開,有的接口可以根據屬性值表現不同的行為。比如設置了對應的只讀屬性則不允許使用更新接口。
每個屬性都有值、類型、說明和讀寫屬性,對于行集對象,還有一個用于指示是否可以逐列應用它的指示器。
屬性由一個GUID和一個整數ID進行唯一標識。
屬性集是所有具有相同 組GUID 的一組屬性。在邏輯上它們都用于同一種功能,比如有的屬性集用于設置數據源連接屬性,有的用于設置行集屬性等等。它們是應用在同一個特定對象上的一組屬性。在每個這樣的屬性組中都有屬性每個屬性屬于一個或者多個屬性組。
屬性定義如下:
dwOptions:屬性的操作方式有3種,但是一般只使用其中的兩種:DBPROPOPTIONS_REQUIRED表示必須設置成功,如果設置失敗,則設置屬性的操作失敗,DBPROPOPTIONS_OPTIONAL,表示可選,即即使該屬性設置失敗,設置屬性的操作也返回成功。DBPROPOPTIONS_SETIFCHEAP表示如果在設置屬性操作時在在dwStatus參數中返回該屬性設置的狀態,是否成功,失敗的原因等等。
屬性集的定義如下:
目前屬性組包括初始化屬性組、數據源屬性組、會話屬性組、行集屬性組、表屬性組和列屬性組等等。
設置屬性一般包含如下幾個步驟:
1. 分配一個屬性類型DBPRO的數組,一般傾向于多分配一個,最后一個數組元素全0,作為結尾
2. 確定每個屬性的屬性GUID,即明確我們需要設置的是對象的哪個屬性
3. 填充對應的屬性值,屬性操作方式
4. 填充對應的屬性集DBPROPSET結構。設置該屬性集的GUID
5. 調用對應的接口設置屬性
數據源對象接口
數據源對象的接口定義如下:
CoType TDataSource {[mandatory] interface IDBCreateSession; //創建回話對象[mandatory] interface IDBInitialize; //創建數據源連接對象[mandatory] interface IDBProperties; ///創建數據源的屬性操作對象[mandatory] interface IPersist;[optional] interface IConnectionPointContainer;[optional] interface IDBAsynchStatus;[optional] interface IDBDataSourceAdmin;[optional] interface IDBInfo;[optional] interface IObjectAccessControl;[optional] interface IPersistFile;[optional] interface ISecurityInfo;[optional] interface ISupportErrorInfo;[optional] interface ITrusteeAdmin;[optional] interface ITrusteeGroupAdmin; }在上面代碼中,mandatory表示是數據源必須提供的接口,optional表示的是可選性提供的接口,在創建對應的接口時盡量使用必須實現的接口,如果需要使用可選擇的接口,一定要判斷數據源是否支持。在數據源對象中最主要的還是前三個必須提供的接口
連接到數據庫
連接到數據源一般使用IDBInitialize接口的Initialize方法,但是生成IDBInitialize接口有幾種不同的方式,下面一一列舉出來
直接創建IDBInitialize接口
這種方式一般調用CoCreateInstance函數創建,下面是具體的代碼
#include <tchar.h> #include <windows.h> #include <strsafe.h>#define COM_NO_WINDOWS_H //如果已經包含了Windows.h或不使用其他Windows庫函數時 #define OLEDBVER 0x0260 //MSDAC2.6版 #include <oledb.h> #include <oledberr.h>#define GRS_ALLOC(sz) HeapAlloc(GetProcessHeap(),0,sz) #define GRS_CALLOC(sz) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sz) #define GRS_SAFEFREE(p) if(NULL != p){HeapFree(GetProcessHeap(),0,p);p=NULL;}#define GRS_USEPRINTF() TCHAR pBuf[1024] = {} //定義輸出宏 #define GRS_PRINTF(...) \GRS_USEPRINTF();\StringCchPrintf(pBuf,1024,__VA_ARGS__);\WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),pBuf,lstrlen(pBuf),NULL,NULL);//安全釋放,為了養成良好的編碼習慣,特作此宏定義 #define GRS_SAFERELEASE(I)\if(NULL != (I))\{\(I)->Release();\(I)=NULL;\} //檢測上一步的操作是否成功 #define GRS_COM_CHECK(hr,...)\if(FAILED(hr))\{\GRS_PRINTF(__VA_ARGS__);\goto CLEAR_UP;\}int _tmain(int argc, TCHAR* argv[]) {CoInitialize(NULL);//創建OLEDB init接口IDBInitialize *pDBInit = NULL;IDBProperties *pIDBProperties = NULL;//設置鏈接屬性DBPROPSET dbPropset[1] = {0};DBPROP dbProps[5] = {0};CLSID clsid_MSDASQL = {0}; //sql server 的數據源對象HRESULT hRes = CLSIDFromProgID(_T("SQLOLEDB"), &clsid_MSDASQL);GRS_COM_CHECK(hRes, _T("獲取SQLOLEDB的CLSID失敗,錯誤碼:0x%08x\n"), hRes);hRes = CoCreateInstance(clsid_MSDASQL, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize,(void**)&pDBInit);GRS_COM_CHECK(hRes, _T("無法創建IDBInitialize接口,錯誤碼:0x%08x\n"), hRes);//指定數據庫實例名,這里使用了別名local,指定本地默認實例dbProps[0].dwPropertyID = DBPROP_INIT_DATASOURCE;dbProps[0].dwOptions = DBPROPOPTIONS_REQUIRED;dbProps[0].vValue.vt = VT_BSTR;dbProps[0].vValue.bstrVal = SysAllocString(OLESTR("LIU-PC\\SQLEXPRESS"));dbProps[0].colid = DB_NULLID;//指定數據庫庫名dbProps[1].dwPropertyID = DBPROP_INIT_CATALOG;dbProps[1].dwOptions = DBPROPOPTIONS_REQUIRED;dbProps[1].vValue.vt = VT_BSTR;dbProps[1].vValue.bstrVal = SysAllocString(OLESTR("Study"));dbProps[1].colid = DB_NULLID;//指定鏈接數據庫的用戶名dbProps[2].dwPropertyID = DBPROP_AUTH_USERID;dbProps[2].vValue.vt = VT_BSTR;dbProps[2].vValue.bstrVal = SysAllocString(OLESTR("sa"));//指定鏈接數據庫的用戶密碼dbProps[3].dwPropertyID = DBPROP_AUTH_PASSWORD;dbProps[3].vValue.vt = VT_BSTR;dbProps[3].vValue.bstrVal = SysAllocString(OLESTR("123456"));//設置屬性hRes = pDBInit->QueryInterface(IID_IDBProperties, (void**)&pIDBProperties);GRS_COM_CHECK(hRes, _T("查詢IDBProperties接口失敗, 錯誤碼:%08x\n"), hRes);dbPropset->guidPropertySet = DBPROPSET_DBINIT;dbPropset[0].cProperties = 4;dbPropset[0].rgProperties = dbProps;hRes = pIDBProperties->SetProperties(1, dbPropset);GRS_COM_CHECK(hRes, _T("設置屬性失敗, 錯誤碼:%08x\n"), hRes);//鏈接數據庫hRes = pDBInit->Initialize();GRS_COM_CHECK(hRes, _T("鏈接數據庫失敗:錯誤碼:%08x\n"), hRes);//do somethingpDBInit->Uninitialize();GRS_PRINTF(_T("數據庫操作成功!!!!!\n")); CLEAR_UP:GRS_SAFEFREE(pDBInit);GRS_SAFEFREE(pIDBProperties);CoUninitialize();return 0; }這是一份完整的可執行代碼,后續的部分對于重復的代碼將不再給出。
在上述代碼中我們首先根據字符串SQLOLEDB查找到SQL Server對應的數據源對象,然后根據數據源對象查詢出IDBProperties對象,接著分配一些空間來設置屬性和屬性集,調用IDBProperties接口的SetProperties函數來設置對應的數據源對象的接口。最后調用IDBInitialize接口的Initialize鏈接數據源,調用Uninitialize函數來斷開連接。
一般數據源對象的屬性集合的GUID為DBPROPSET_DBINIT,下面包含的屬性最主要的有:
1. DBPROP_INIT_DATASOURCE:數據連接實例(具體的DBMS實例名)
2. DBPROP_INIT_CATALOG:目錄名(在SQL Server中對應的是具體的數據庫名稱,對于ORACLE來說沒有意義)
3. DBPROP_AUTH_USERID: 用戶名
4. DBPROP_AUTH_PASSWORD: 密碼
我們也注意到上面調用SysAllocString的BSTR類型的字符串并沒有調用對應的函數進行釋放,會不會發生內存泄露?其實不用擔心OLEDB在斷開連接的時候已經幫助我們釋放了這部分空間。
使用IDBPromptInitialize接口來創建數據源對象
上述方法是依托于標準的COM,雖然也成功創建的數據源連接,但是無法在標準的com之上進行更多的初始化操作,導致了有些特定的高級功能無法使用,所以在實踐中常用的還是利用IDBPromptInitialize和IDataInitialize的方式比較多。
IDBPromptInitialize創建時會彈出一個數據源選擇的對話框,供用戶選擇相關配置信息(數據源/用戶名/密碼等)然后根據這些配置自動生成連接對象。
下面看一個彈出數據源對話框的例子:
除了這種方式,他還可以直接創建出IDBInitialize接口,利用之前設置屬性的方式來連接到數據庫,下面是一個演示的例子:
HRESULT hRes = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_IDataInitialize, (void**)&pIDataInitialize);COM_CHECK_SUCCESS(hRes, _T("創建接口IDBInitialize失敗:%08x\n"), hRes);hRes = CLSIDFromProgID(_T("SQLOLEDB"), &clsid);COM_CHECK_SUCCESS(hRes, _T("查詢SQLOLEDB CLSID 失敗:%08x\n"), hRes);hRes = pIDataInitialize->CreateDBInstance(clsid, NULL,CLSCTX_INPROC_SERVER, NULL, IID_IDBInitialize,(IUnknown**)&pIDBInitialize);COM_CHECK_SUCCESS(hRes, _T("創建IDBInitialize接口失敗:%08x\n"), hRes);//后續的代碼就是我們之前寫的那段定義屬性,設置屬性,連接數據庫的代碼使用IDataInitialize接口來創建數據源對象
使用IDataInitialize接口可以直接使用連接字串連接到數據庫,下面是使用連接字串的例子:
void ConnectSQLServerByConnstr() //通過連接字符串連接數據庫 {DECLARE_OLEDB_INTERFACE(IDataInitialize);DECLARE_OLEDB_INTERFACE(IDBInitialize);DECLARE_BUFFER();HRESULT hRes = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_IDataInitialize, (void**)&pIDataInitialize);COM_CHECK_SUCCESS(hRes, _T("創建IDataInitialize接口失敗:%08x!\n"), hRes);hRes = pIDataInitialize->GetDataSource(NULL, CLSCTX_INPROC_SERVER, OLESTR("Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Password = 123456;Initial Catalog=Study;Data Source=LIU-PC\\SQLEXPRESS;"), IID_IDBInitialize, (IUnknown**)&pIDBInitialize);COM_CHECK_SUCCESS(hRes, _T("獲取IDBInitialize接口失敗:%08x!\n"), hRes);hRes = pIDBInitialize->Initialize();COM_CHECK_SUCCESS(hRes, _T("連接數據庫失敗:%08x!\n"), hRes);COM_PRINTF(_T("連接數據庫成功\n"));pIDBInitialize->Uninitialize(); __CLEAN_UP:SAFE_RELEASE(pIDataInitialize);SAFE_RELEASE(pIDBInitialize); }獲取連接字串
其實除了上面這種直接創建IDataInitialize接口的方法外,還可以使用IDBPromptInitialize接口Query出一個IDataInitialize接口,然后再設置連接字串連接到數據庫。
其實在OLEDB中,可以認為連接字串最終被翻譯為對應的屬性,也就是說OLEDDB保存著對應連接的屬性,我們可以通過不同的方式來獲取不同類型的屬性,比如使用IDBProperties接口來獲取對應的鏈接屬性,或者使用IDataInitialize的GetInitializationString函數來獲取連接的鏈接字串。
既然它保存著每個連接的對應屬性,那么是不是可以將用戶在數據源對話框上的操作最終保存為數據連接字串呢,答案是肯定的。實現的思路如下:
1. 調用IDBPromptInitialize接口的PromptDataSourc方法彈出數據源對話框,讓用戶操作
2. 根據IDBPromptInitialize接口Query出IDataInitialize接口
3. 調用IDataInitialize接口的GetInitializationString來獲取連接字串
下面是具體實現的代碼:
為了節約篇幅,這些筆記內容只會列舉部分關鍵的代碼,至完整的代碼我會隨著博客內容的進度慢慢上傳到GitHub項目中,并在博文的最末尾給出對應文件的地址
本次代碼地址1
本次代碼地址2
總結
- 上一篇: Flex4皮肤制作
- 下一篇: 58、【网络工程师精华篇】50种网络故障