使用OLEDB编程
本文整理自博客:http://gamebabyrocksun.blog.163.com/blog/static/571534632008101083957499/
一,打開數據庫連接
1,要完成一個數據庫訪問任務,第一步則是打開連接,在OLEDB2.0以前的版本中,可以如下操作:
#define COM_NO_WINDOWS_H //如果已經包含了Windows.h或不使用其他Windows 庫函數時#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250
#include <oledb.h>
#include <oledberr.h>
IDBInitialize *pIDBInitialize = NULL;
CoCreateInstance(CLSID_MSDASQL, NULL,CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void**)&pIDBInitialize);
?
如果你使用的是SQL Server 2005以上的數據庫,則有以下可選方式:
#include <sqlncli.h>CoCreateInstance( CLSID_SQLNCLI10,NULL,CLSCTX_INPROC_SERVER, IID_IDBInitialize,(void **) &pIDBInitialize);
這是OLEDB2.0以前的做法。
在OLEDB2.0以后,推出了以下兩個接口:IDataInitialize和IDBPromptInitialize,其中,后者將彈出以下對話框:
其創建代碼如下:
IDBPromptInitialize* pIDBPromptInitialize = NULL;CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER, IID_IDBPromptInitialize, (void **)&pIDBPromptInitialize);
IDBInitialize *pIDBInitialize = NULL;
//下面這句將彈出前面所說的對話框
pIDBPromptInitialize->PromptDataSource(NULL, hWndParent, DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize (IUnknown **)&pIDBInitialize);
pIDBInitialize->Initialize();//根據對話框采集的參數連接到指定的數據庫
IDataInitialize的使用方法如下:
IDataInitialize* pIDataInitialize = NULL;CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_IDataInitialize,(void**)&pIDataInitialize);
IDBInitialize *pIDBInitialize = NULL;
pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER, NULL, IID_IDBInitialize (IUnknown**)&pIDBInitialize);
?2,在創建了IDBInitialize接口之后(使用IDBPromptInitialize這種方式創建的除外),我們需要詳細指定連接數據的各種參數,在OLEDB中這些參數稱為屬性,屬性以組劃分,每組屬性稱為屬性集合。
1 DBPROP InitProperties[4];2 DBPROPSET rgInitPropSet[1];
3
4 //初始化屬性值變量
5 for ( i = 0 ; i < 4 ; i++ )
6 {
7 VariantInit(&InitProperties[i].vValue);
8 }
9
10 //指定數據庫實例名,這里使用了別名local,指定本地默認實例
11 InitProperties[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
12 InitProperties[0].vValue.vt = VT_BSTR;
13 InitProperties[0].vValue.bstrVal= SysAllocString(L"(local)");
14 InitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
15 InitProperties[0].colid = DB_NULLID;
16
17 //指定數據庫名
18 InitProperties[1].dwPropertyID = DBPROP_INIT_CATALOG;
19 InitProperties[1].vValue.vt = VT_BSTR;
20 InitProperties[1].vValue.bstrVal = SysAllocString(L"MyTest");
21 InitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
22 InitProperties[1].colid = DB_NULLID;
23
24 //指定身份驗證方式為集成安全模式“SSPI”
25 InitProperties[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
26 InitProperties[2].vValue.vt = VT_BSTR;
27 InitProperties[2].vValue.bstrVal = SysAllocString(L"SSPI");
28 InitProperties[2].dwOptions = DBPROPOPTIONS_REQUIRED;
29 InitProperties[2].colid = DB_NULLID;
30
31 //創建一個GUID為DBPROPSET_DBINIT的屬性集合,這也是初始化連接時需要的唯一一個屬性集合
32 rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
33 rgInitPropSet[0].cProperties = 4;
34 rgInitPropSet[0].rgProperties = InitProperties;
35
36 //得到數據庫初始化的屬性接口
37 IDBProperties* pIDBProperties = NULL;
38 hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
39 if (FAILED(hr))
40 {//無法得到IDBProperties接口,詳細的錯誤信息可以使用IerrorRecords接口得到
41 return FALSE;
42 }
43
44 hr = pIDBProperties->SetProperties(1, rgInitPropSet);
45 if (FAILED(hr))
46 {//設置屬性失敗
47 return -1;
48 }
49
50 //屬性一但設置完成,相應的接口就可以釋放了
51 pIDBProperties->Release();
52
53 //根據指定的屬性連接到數據庫
54 pIDBInitialize->Initialize();
在OLEDB中也可以通過IDataInitialize接口的GetDataSource方法來實現,例子如下:
1 IDataInitialize* pIDataInitialize = NULL;2 CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
3 IID_IDataInitialize,(void**)&pIDataInitialize);
4
5 IDBInitialize *pIDBInitialize = NULL;
6 //使用連接字符串得到一個IDBInitialize接口
7 pIDataInitialize->GetDataSource(NULL, CLSCTX_INPROC_SERVER,
8 L"Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=D:\\My Document\\db1.mdb;Mode=Share Deny None;", __uuidof(IDBInitialize),
9 IUnknown**)&pIDBInitialize);
10
11 //連接到數據庫
12 pIDBInitialize ->Initialize();
?二,創建事務對象
在創建了連接對象之后,我們需要創建一個事務對象。值得注意的是,一個連接對象可以與多個事務對象相對應。
最常用的一個事務對象接口是IOpenRowset,用于打開一個結果集。以下代碼顯示了如何從一個數據連接對象創建一個IOpenRowset接口。
1 IOpenRowset* pIOpenRowSet = NULL;2 IDBCreateSession * pIDBCreateSession = NULL;
3
4 //首先從連接對象接口找到等價的CreateSession接口
5 pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession));
6
7 //創建一個IOpenRowset接口
8 pIDBCreateSession->CreateSession(
9 NULL,
10 IID_IOpenRowset,
11 (Iunknown**)&pIOpenRowSet));
12
13 //與IDBInitialize等價的IDBCreateSession可以釋放了,需要時再Query出來就行了
14 if( pIDBCreateSession )
15 {
16 pIDBCreateSession->Release();
17 }
以上代碼通過創建IOpenRowset接口的方法創建了一個Session對象,其他的Session對象的接口可以通過QueryInterface來得到。(關于接口等價性、Session對象的接口列表,詳見原文:http://gamebabyrocksun.blog.163.com/blog/static/571534632008101783231453/)
值得注意的是Session對象有個IGetDataSource接口,可以通過Session對象來找回原先的數據連接對象。你可以在創建了IOpenRowset接口之后丟掉之前創建的所有接口,甚至是數據連接對象的接口,而通過IGetDataSource接口則可以方便的找回數據連接對象的接口,以下代碼演示了這一過程:
1 IGetDataSource* pIGetDataSource = NULL;2 IDBInitialize * pIDBInitialize = NULL;
3
4 pIOpenRowset->QueryInterface( IID_IGetDataSource,
5 (void**) &pIGetDataSource );
6
7 pIGetDataSource-> GetDataSource (
8 IID_IDBInitialize,(IUnknown**)&pIDBInitialize);
如同數據連接對象,Session對象也有很多屬性。你可以通過Query出ISessionProperties接口再創建一個屬性集并對其進行設置,以下代碼演示了這一過程:
1 //注意Session對象只有一個屬性集合的一個屬性,就是并發級別2 //并發級別是控制數據庫并發操作的關鍵,在OLEDB中它是一系列值的位或結果
3 //屬性:
4
5 SessionProperties[0].dwPropertyID = DBPROP_SESS_AUTOCOMMITISOLEVELS;
6
7 SessionProperties[0].vValue.vt = VT_I4;
8 SessionProperties[0].vValue.lVal= DBPROPVAL_TI_READCOMMITTED;
9 SessionProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
10 SessionProperties[0].colid = DB_NULLID;
11
12 //創建一個GUID為DBPROPSET_SESSION的屬性集合
13 rgSessionPropSet[0].guidPropertySet = DBPROPSET_SESSION;
14 rgSessionPropSet[0].cProperties = 1;
15 rgSessionPropSet[0].rgProperties = SessionProperties;
16
17
18
19 ISessionProperties* pISessionProperties = NULL;
20 //找到接口
21 pIOpenRowset->QueryInterface( IID_ISessionProperties, (void**) &pISessionProperties );
22
23 //設置屬性
24 pISessionProperties->SetProperties(1,rgSessionPropSet);
在Session對象的諸多接口中,如果我們要使用SQL語句來操作數據源,那么我們經常使用的一個接口就是IDBCreateCommand.這個接口并不是強制實現的。這是因為:OLEDB的目標就是無論應用系統要使用何種數據(結構化的如數據庫、半結構化的如XML,無結構化的如互聯網),它都能輕松勝任,而你無需再去尋找別的接口。大多數的數據源都提供了這個接口,以便對數據庫進行SQL操作。
三,Command對象及SQL語句處理
1,Command對象全貌:
1 CoType TCommand2 {
3 [mandatory] interface IAccessor;
4 [mandatory] interface IColumnsInfo;
5 [mandatory] interface ICommand;
6 [mandatory] interface ICommandProperties;
7 [mandatory] interface ICommandText;
8 [mandatory] interface IConvertType;
9 [optional] interface IColumnsRowset;
10 [optional] interface ICommandPersist;
11 [optional] interface ICommandPrepare;
12 [optional] interface ICommandWithParameters;
13 [optional] interface ISupportErrorInfo;
14 [optional] interface ICommandStream;
15 }
2,創建Command對象。
通常我們使用IDBCreateCommand事務接口來創建Command對象,也可以直接從IDBCreateSession接口創建。以下代碼顯示了從連接數據庫到創建Command對象的完整過程:
1 //1、一大堆頭文件和定義,作為前面例子代碼的一個相對完整的總結2
3 #define COM_NO_WINDOWS_H //如果已經包含了Windows.h或不使用其他Windows庫函數時
4 #define DBINITCONSTANTS
5 #define INITGUID
6 #define OLEDBVER 0x0250
7
8 #include "oledb.h" // OLE DB Header
9 #include "oledberr.h" // OLE DB Errors
10 #include "msdasc.h" // OLE DB Service Component header
11 #include "msdaguid.h" // OLE DB Root Enumerator
12 #include "msdasql.h" // MSDASQL - Default provider
13 int main()
14 {
15 CoInitialize(NULL);
16 //2、創建IDataInitialize接口
17 IDataInitialize* pIDataInitialize = NULL;
18 CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
19 IID_IDataInitialize,(void**)&pIDataInitialize);
20
21 IDBProperties *pIDBProperties = NULL;
22 IDBInitialize *pIDBInitialize = NULL;
23
24 //2、直接創建IDBProperties接口
25 pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL,
26 CLSCTX_INPROC_SERVER, NULL, IID_IDBProperties,
27 (IUnknown**)&pIDBProperties);
28
29 //3、定義屬性集合,屬性(略),設置屬性
30 ......
31 //4、連接到數據源
32 pIDBProperties->QueryInterface(IID_IDBInitialize,(void**)&pIDBInitialize);
33 pIDBInitialize->Initialize();
34
35 //5、得到事務對象
36 IDBCreateSession* pIDBCreateSession = NULL;
37 pIDBInitialize->QueryInterface(IID_IDBCreateSession,(void**)& pIDBCreateSession);
38
39 //注意這次直接創建了IDBCreateCommand對象作為事務對象,注意一般情況下因為這個接口是個可選接口,所以直接創建有可能會失敗,所以要檢驗返回值
40 //在這一系列文章中我省略這些檢驗性的操作,在實際的代碼中一定要包含豐富的錯誤處理代碼,有關錯誤處理的話題我將在以后的專門文章中詳細闡述,這里先
41 //聚焦于我們所關心的問題
42
43 IDBCreateCommand* pIDBCreateCommand = NULL;
44 pIDBCreateSession->CreateSession(NULL,IID_IDBCreateCommand,(IUnknown**)&pIDBCreateCommand);
45
46 //6、創建Command對象,我們直接創建ICommandText接口
47 ICommandText* pICommandText = NULL;
48 pIDBCreateCommand->CreateCommand(NULL,IID_ICommandText,(IUnknown**)&pICommandText);
49
50 ......
51 }
在Command對象中,ICommandText接口實際是從ICommand接口派生而來的,因此,ICommandText接口實際包括了ICommand接口的全部方法。ICommandText接口有如下方法:
1 HRESULT SetCommandText(REFGUID rguidDialect,LPCOLESTR pwszCommand);2 HRESULT GetCommandText(GUID* pguidDialect,LPOLESTR* ppwszCommand);
3
4 //以下的方法實際也是ICommand的方法:
5
6 HRESULT Cancel();
7 HRESULT Execute(IUnknown* pUnkOuter,REFIID riid,DBPARAMS* pParams,
8 DBROWCOUNT *pcRowsAffected,IUnknown** ppRowset);
9 HRESULT GetDBSession(REFIID riid,IUnknown** ppSession);
以下語法顯示了如果設置和執行一個SQL語句:
1 TCHAR* pSQL = _T("Select * From SomeTable");2
3 pICommandText->SetCommandText(DBGUID_DEFAULT,pSQL);
4 IRowset* pIRowset = NULL;
5
6 pICommandText->Execute(NULL,IID_IRowset,NULL,NULL,(IUnknown**)&pIRowset);
以下方法演示了如果一次執行多條SQL語句:
1 TCHAR* pSQL = _T("Select * From SomeTable1\nSelect * From SomeTable2");2
3 pICommandText->SetCommandText(DBGUID_DEFAULT,pSQL);
4
5 IMultipleResults* pIMultipleResults = NULL;
6
7 pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL , (IUnknown**)& pIMultipleResults);
MultipleResults對象只有IMultipleResults這一個接口,可以通過其得到多個結果集對象及其接口(IRowset)。一般情況下,作為自己對OLEDB的封裝或是應用,都應用使用IMultipleResults接口,而不是直接使用IRowset。這樣做的好處是,你既可以一次只執行一條SQL語句,也可以一次執行多條SQL語句。以下代碼顯示了這一特性的使用:
1 IMultipleResults* pIMultipleResults = NULL;2 pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL,(IUnknown**)& pIMultipleResults);
3
4 IRowset* pIRowset = NULL;
5 DBROWCOUNT cRowsAffected = 0;//注意不要被這個參數迷惑大多數情況下它是沒用的,并不能通過它知道結果集中實際包含多少行。循環處理每一個結果集,當然這需要你起碼知道你執行SQL語句的順序
6 while( S_OK == pIMultipleResults->GetResult(NULL,
7 DBRESULTFLAG_DEFAULT,
8 IID_IRowset,
9 cRowsAffected,
10 (IUnknown**)&pIRowset) )
11 {
12 ......//處理每一個IRowset
13 pIRowset->Release();
14 pIRowset = NULL;
15 }
Command對象也有自己的屬性集,其屬性會影響因執行SQL語句而得到的結果集對象的屬性(這不同于先前的兩個對象),比如結果集是否可以修改,是否可以插入,是否可以更新等等。此外,需要注意的是,一個事務對象可以創建n個命令對象。以下代碼顯示了如果設置命令對象(其實是結果集對象)的屬性:
2 DBPROP prop[2];
3
4 prop[0].dwPropertyID = DBPROP_UPDATABILITY;
5 prop[0].vValue.vt = VT_I4;
6 prop[0].vValue.lVal=DBPROPVAL_UP_CHANGE //打開Update屬性
7 |DBPROPVAL_UP_DELETE //打開Delete屬性
8 |DBPROPVAL_UP_INSERT; //打開Insert屬性
9
10 prop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
11 prop[0].colid = DB_NULLID;
12 ps[0].guidPropertySet = DBPROPSET_ROWSET; //注意屬性集合的名稱
13 ps[0].cProperties = 1;
14 ps[0].rgProperties = prop;
15
16 ICommandProperties * pICommandProperties = NULL;
17 pICommandText->QueryInterface(IID_ICommandProperties,
18 (void**)& pICommandProperties);
19 pICommandProperties->SetProperties(1,ps);//注意必須在Execute前設定屬性
20
21 IRowset* pIRowset = NULL;
22 pICommandText->Execute(NULL,IID_IRowset,NULL,NULL,(IUnknown**)&pIRowset)
以上代碼演示了如何打開五個帶有更新、插入、刪除屬性的結果集。這樣,我們可以直接利用結果集對象的相關方法來修改數據并提交,同時可以繞過使用等價的SQL語句(UPDATE, DELETE, INSERT等),而是使用純粹代碼的方式修改數據并提交到數據庫,這種方式要比直接使用SQL語句更加高效。
四,處理結果集
1,關于Rowset和MutipleResults對象的接口列表,可以參見原文:http://gamebabyrocksun.blog.163.com/blog/static/57153463200811882756317/
在Rowset對象中,我們經常使用的接口就是IRowset, IColumnInfo和IAccessor。通過這幾個接口,才可以訪問到我們查詢得到的數據結果。這個過程通常很復雜。
2,得到結果集:
pICommandText->Execute(NULL,IID_IRowset,NULL,NULL (IUnknown**)&pIRowset)3,得到列信息:
通過列信息可以知道結果集的完整數據結果,這為后續的創建訪問器、準備數據緩沖奠定了基礎。多數時候,在SQL的查詢語句是是沒有關于數據結構的信息的,這個信息是隱含在數據庫中的,而我們查詢得到結果集時就必須要知道這個數據結構的信息,否則對數據的訪問就無從談起。這個過程通過IColumnsInfo接口實現。以下代碼演示了如何得到列信息:
1 IColumnsInfo * pIColumnsInfo = NULL;2 ULONG cColumns = 0;
3 DBCOLUMNINFO * rgColumnInfo = NULL;
4 LPWSTR pStringBuffer = NULL;
5
6 HRESULT hr = pIRowset->QueryInterface(IID_IColumnsInfo,(void**)&pIColumnsInfo));
7
8 hr = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo, &pStringBuffer));
9
10 //如果成功了,那么rgColumnInfo中已經包含了一個關于列信息數據結構的數組,
11 //數組元素的個數即cColumns 也就是最終的列數
12 //使用完畢后釋放所有的資源及接口
13 CoTaskMemFree(rgColumnInfo);
14 CoTaskMemFree(pStringBuffer);
15 if( NULL != pIColumnsInfo )
16 {
17 pIColumnsInfo->Release();
18 }
這里使用了CoTaskMemFree這個COM庫函數來完成對rgColumnInfo和pStringBuffer內存的釋放。注意,在OLEDB的幫助文檔中說要使用IMalloc接口的Free方法來釋放。其實二者是等價的。在使用CoTaskMemFree這個函數時,并不需要檢查被釋放的指針是否為空,它內部有檢查是否為空的機制,直接調用即可。
DBCOLUMNINFO這個結構的原型如下:
1 typedef struct tagDBCOLUMNINFO2 {
3 LPOLESTR pwszName;
4 ITypeInfo *pTypeInfo;
5 DBORDINAL iOrdinal;
6 DBCOLUMNFLAGS dwFlags;
7 DBLENGTH ulColumnSize;
8 DBTYPE wType;
9 BYTE bPrecision;
10 BYTE bScale;
11 DBID columnid;
12 }DBCOLUMNINFO;
pswzName:字段名稱,為UNICODE字符串。如果查詢中沒有明確為列指定名稱時,這個字段為空,即沒有列名。
pTypeInfo:保留接口,一般為NULL。
iOrdinal:字段在結果集中的序號,即表示該字段是結果集的第幾列。從1開始,序號0是為一些特殊用途保留的(比如結果集的書簽功能)。
dwFlags:描述了列的狀態。其類型為DBCOLUMNFLAGS,是個枚舉類型,在MSDN中可以找到詳細描述。
ulColumnSize:列的大小,單位為字節。需要注意的是,這個大小僅對字符型列(即wType = DBTYPE_STR或wType = DBTYPE_WSTR)有意義。而對于其他類型的字段則設置為一個~0值(即FFFFFFFF)。
wType:結果集列的數據類型。OLEDB中定義了完整的被支持的數據類型,此處將所有類型含義及欺直列出如下:
1 enum DBTYPEENUM2 {
3 // The following values exactly match VARENUM
4 // in Automation and may be used in VARIANT.
5 DBTYPE_EMPTY = 0,
6 DBTYPE_NULL = 1,
7 DBTYPE_I2 = 2,
8 DBTYPE_I4 = 3,
9 DBTYPE_R4 = 4,
10 DBTYPE_R8 = 5,
11 DBTYPE_CY = 6,
12 DBTYPE_DATE = 7,
13 DBTYPE_BSTR = 8,
14 DBTYPE_IDISPATCH = 9,
15 DBTYPE_ERROR = 10,
16 DBTYPE_BOOL = 11,
17 DBTYPE_VARIANT = 12,
18 DBTYPE_IUNKNOWN = 13,
19 DBTYPE_DECIMAL = 14,
20 DBTYPE_UI1 = 17,
21 DBTYPE_ARRAY = 0x2000,
22 DBTYPE_BYREF = 0x4000,
23 DBTYPE_I1 = 16,
24 DBTYPE_UI2 = 18,
25 DBTYPE_UI4 = 19,
26
27 // The following values exactly match VARENUM in Automation but cannot be used in VARIANT.
28 DBTYPE_I8 = 20,
29 DBTYPE_UI8 = 21,
30 DBTYPE_GUID = 72,
31 DBTYPE_VECTOR = 0x1000,
32 DBTYPE_FILETIME = 64,
33 DBTYPE_RESERVED = 0x8000,
34
35 // The following values are not in VARENUM in OLE.
36 DBTYPE_BYTES = 128,
37 DBTYPE_STR = 129,
38 DBTYPE_WSTR = 130,
39 DBTYPE_NUMERIC = 131,
40 DBTYPE_UDT = 132,
41 DBTYPE_DBDATE = 133,
42 DBTYPE_DBTIME = 134,
43 DBTYPE_DBTIMESTAMP = 135
44 DBTYPE_HCHAPTER = 136
45 DBTYPE_PROPVARIANT = 138,
46 DBTYPE_VARNUMERIC = 139
47 };
需要特別注意的是,這里的數據類型不是數據庫支持的數據類型,或者說并不是所有的數據庫都支持這些所有的數據類型,這僅是一個所有可能數據類型的全部概括,當然也有一些數據庫中的類型并不在這個列表中,這時往往數據庫對應的OLEDB接口提供程序都做了很好的轉換,已經轉換成了這個列表中所具有的類型,因此不用擔心會碰到不支持的數據類型。
bPrecision:精度。
bScale:小數位數。
columnid:該列在數據庫系統字典表中的id號。
4,創建綁定:
綁定是OLEDB中最重要最核心的概念之一,它是我們訪問數據的關鍵操作。對其最簡單的理解就是:安排得到數據的內存擺放方式,并將這一方式告訴數據提供者,讓它按要求將數據擺放到我們指定的內存中。為此,我們就需要自己創建一個被稱作DBBINDING的數組,該結構體聲明如下:
1 typedef struct tagDBBINDING2 {
3 DBORDINAL iOrdinal;
4 DBBYTEOFFSET obValue;
5 DBBYTEOFFSET obLength;
6 DBBYTEOFFSET obStatus;
7 ITypeInfo *pTypeInfo;
8 DBOBJECT *pObject;
9 DBBINDEXT *pBindExt;
10 DBPART dwPart;
11 DBMEMOWNER dwMemOwner;
12 DBPARAMIO eParamIO;
13 DBLENGTH cbMaxLen;
14 DWORD dwFlags;
15 DBTYPE wType;
16 BYTE bPrecision;
17 BYTE bScale;
18 } DBBINDING;
可以看出,這個結構和前面的DBCOLUMNINFO結構很相似。其實它們的字段大多數確實是一致的,甚至可以直接使用DBCOLUMNINFO對DBBINDING進行賦值。但是二者字段的含義是完全不同的。首先DBCOLUMNINFO是數據提供者給你的信息,它是固定的,對相同的查詢來說,列總是相同的,因此數據提供者返回的DBCOLUMNINFO數組也是固定的。而DBBINDING是你作為數據消費者創建之后給數據提供者的一個結構數組,它的內容完全有你來控制。通過這個結構我們可以指定數據提供者最終將數據擺放成我們指定的格式,或者進行指定的數據類型轉換。以下例子展示了如何完成這個過程:
1 ULONG cColumns;2 DBCOLUMNINFO * rgColumnInfo = NULL;
3 LPWSTR pStringBuffer = NULL;
4
5 IColumnsInfo * pIColumnsInfo = NULL;
6 ULONG iCol;
7 ULONG dwOffset = 0;
8 DBBINDING * rgBindings = NULL;
9
10 pIRowset->QueryInterface(IID_IColumnsInfo,(void**)&pIColumnsInfo));
11 pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnInfo,&pStringBuffer));
12
13 rgBindings = (DBBINDING*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,cColumns * sizeof(DBBINDING));
14 for( iCol = 0; iCol < cColumns; iCol++ )
15 {
16 rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;
17 rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
18 rgBindings[iCol].obStatus = dwOffset;
19 rgBindings[iCol].obLength = dwOffset + sizeof(DBSTATUS);
20 rgBindings[iCol].obValue = dwOffset+sizeof(DBSTATUS)+sizeof(ULONG);
21 rgBindings[iCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
22 rgBindings[iCol].eParamIO = DBPARAMIO_NOTPARAM;
23 rgBindings[iCol].bPrecision = rgColumnInfo[iCol].bPrecision;
24 rgBindings[iCol].bScale = rgColumnInfo[iCol].bScale;
25 rgBindings[iCol].wType = rgColumnInfo[iCol].wType;
26 rgBindings[iCol].cbMaxLen = rgColumnInfo[iCol].ulColumnSize;
27 dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;
28 dwOffset = ROUNDUP(dwOffset);
29 }
30
31 CoTaskMemFree(rgColumnInfo);
32 CoTaskMemFree(pStringBuffer);
33
34 if( pIColumnsInfo )
35 {
36 pIColumnsInfo->Release();
37 }
需要注意的是用紅色標的四行。其中第一句指明數據提供者最終提交數據時必須包含的信息,這里指定了列值(DBPART_VALUE),長度(DBPART_LENGTH)和狀態(DBPART_STATUS)共3個信息,列值不用說就是要放字段的最終結果值,長度就是這個字段占用的字節長度,列狀態中將存放的是值的狀態比如是否為空等。
后面3句則明確的指出了以上三個信息在內存中擺放的偏移位置,通常按習慣,我們要求數據提供者先擺放狀態,再擺放長度,最后擺放數據。
5,創建訪問器:
有了綁定結構之后,接下來的工作就是通知數據提供者按照我們的要求對數據進行“格式化”。這個過程就是創建一個訪問器。創建訪問器就要使用IAccessor接口,同樣這個接口也是從IRowset查詢得來,代表訪問器的標志則是一個類型為HACCESSOR的句柄。以下代碼演示了如果創建一個訪問器:
2 IAccessor * pIAccessor = NULL;
3
4 pIRowset->QueryInterface(IID_IAccessor,(void**)&pIAccessor));
5 pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA,cColumns,rgBindings,0,phAccessor,NULL));
6 if( pIAccessor )
7 {
8 pIAccessor->Release();
9 }
在這段代碼中,我們最后直接釋放了IAccessor接口,表示創建了訪問器后這個接口也就沒有用了。如果需要我們其實也可以隨時從同一IRowset接口再查詢出這個接口。因為這兩個接口所表示的結果集對象是同一個。
6,得到數據:
以下代碼演示了得到數據的過程:
1 void * pData = NULL;2 ULONG cRowsObtained;
3 HROW * rghRows = NULL;
4 ULONG iRow;
5 LONG cRows = 10;//一次讀取10行
6 void * pCurData;
7
8 //分配cRows行數據的緩沖,然后反復讀取cRows行到這里,然后逐行處理之
9 pData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwOffset * cRows);
10 while( S_OK == pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0,cRows,&cRowsObtained, &rghRows)) )
11 {//循環讀取數據,每次循環默認讀取cRows行,實際讀取到cRowsObtained行
12 for( iRow = 0; iRow < cRowsObtained; iRow++ )
13 {
14 pCurData = (BYTE*)pData + (dwOffset * iRow);
15 pIRowset->GetData( rghRows[iRow],hAccessor,pCurData));
16 //pCurData中已經包含了結果數據,顯示或者進行處理
17 ......
18 }
19 if( cRowsObtained )
20 {//釋放行句柄數組
21 pIRowset->ReleaseRows(cRowsObtained,rghRows,NULL,NULL,NULL));
22 }
23 CoTaskMemFree(rghRows);
24 rghRows = NULL;
25 }
26 HeapFree(GetProcessHeap(),0,pData);
27 if( pIRowset )
28 {
29 pIRowset->Release();
30 }
代碼中,HROW表示行的句柄,這里使用的是HROW的數據,讀出了多少行,數組中應有多少行的句柄,然后再使用句柄調用GetData將數據讀到指定的內存位置。最后每次都釋放了這個數據,因為數據已經詩刊了我們分配的緩存中,這個句柄數組也就沒有什么意義了。最終數據讀取完畢,我們也就釋放了IRowset接口。
EOF。
轉載于:https://www.cnblogs.com/cmleung/archive/2012/03/22/2411646.html
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
- 上一篇: 获取Windows Shell的简单示例
- 下一篇: JSP中文乱码问题