使用OLEDB编程
本文整理自博客:http://gamebabyrocksun.blog.163.com/blog/static/571534632008101083957499/
一,打開(kāi)數(shù)據(jù)庫(kù)連接
1,要完成一個(gè)數(shù)據(jù)庫(kù)訪問(wèn)任務(wù),第一步則是打開(kāi)連接,在OLEDB2.0以前的版本中,可以如下操作:
#define COM_NO_WINDOWS_H //如果已經(jīng)包含了Windows.h或不使用其他Windows 庫(kù)函數(shù)時(shí)#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以上的數(shù)據(jù)庫(kù),則有以下可選方式:
#include <sqlncli.h>CoCreateInstance( CLSID_SQLNCLI10,NULL,CLSCTX_INPROC_SERVER, IID_IDBInitialize,(void **) &pIDBInitialize);
這是OLEDB2.0以前的做法。
在OLEDB2.0以后,推出了以下兩個(gè)接口:IDataInitialize和IDBPromptInitialize,其中,后者將彈出以下對(duì)話框:
其創(chuàng)建代碼如下:
IDBPromptInitialize* pIDBPromptInitialize = NULL;CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER, IID_IDBPromptInitialize, (void **)&pIDBPromptInitialize);
IDBInitialize *pIDBInitialize = NULL;
//下面這句將彈出前面所說(shuō)的對(duì)話框
pIDBPromptInitialize->PromptDataSource(NULL, hWndParent, DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize (IUnknown **)&pIDBInitialize);
pIDBInitialize->Initialize();//根據(jù)對(duì)話框采集的參數(shù)連接到指定的數(shù)據(jù)庫(kù)
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,在創(chuàng)建了IDBInitialize接口之后(使用IDBPromptInitialize這種方式創(chuàng)建的除外),我們需要詳細(xì)指定連接數(shù)據(jù)的各種參數(shù),在OLEDB中這些參數(shù)稱為屬性,屬性以組劃分,每組屬性稱為屬性集合。
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 //指定數(shù)據(jù)庫(kù)實(shí)例名,這里使用了別名local,指定本地默認(rèn)實(shí)例
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 //指定數(shù)據(jù)庫(kù)名
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 //指定身份驗(yàn)證方式為集成安全模式“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 //創(chuàng)建一個(gè)GUID為DBPROPSET_DBINIT的屬性集合,這也是初始化連接時(shí)需要的唯一一個(gè)屬性集合
32 rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
33 rgInitPropSet[0].cProperties = 4;
34 rgInitPropSet[0].rgProperties = InitProperties;
35
36 //得到數(shù)據(jù)庫(kù)初始化的屬性接口
37 IDBProperties* pIDBProperties = NULL;
38 hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)&pIDBProperties);
39 if (FAILED(hr))
40 {//無(wú)法得到IDBProperties接口,詳細(xì)的錯(cuò)誤信息可以使用IerrorRecords接口得到
41 return FALSE;
42 }
43
44 hr = pIDBProperties->SetProperties(1, rgInitPropSet);
45 if (FAILED(hr))
46 {//設(shè)置屬性失敗
47 return -1;
48 }
49
50 //屬性一但設(shè)置完成,相應(yīng)的接口就可以釋放了
51 pIDBProperties->Release();
52
53 //根據(jù)指定的屬性連接到數(shù)據(jù)庫(kù)
54 pIDBInitialize->Initialize();
在OLEDB中也可以通過(guò)IDataInitialize接口的GetDataSource方法來(lái)實(shí)現(xiàn),例子如下:
1 IDataInitialize* pIDataInitialize = NULL;2 CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
3 IID_IDataInitialize,(void**)&pIDataInitialize);
4
5 IDBInitialize *pIDBInitialize = NULL;
6 //使用連接字符串得到一個(gè)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 //連接到數(shù)據(jù)庫(kù)
12 pIDBInitialize ->Initialize();
?二,創(chuàng)建事務(wù)對(duì)象
在創(chuàng)建了連接對(duì)象之后,我們需要?jiǎng)?chuàng)建一個(gè)事務(wù)對(duì)象。值得注意的是,一個(gè)連接對(duì)象可以與多個(gè)事務(wù)對(duì)象相對(duì)應(yīng)。
最常用的一個(gè)事務(wù)對(duì)象接口是IOpenRowset,用于打開(kāi)一個(gè)結(jié)果集。以下代碼顯示了如何從一個(gè)數(shù)據(jù)連接對(duì)象創(chuàng)建一個(gè)IOpenRowset接口。
1 IOpenRowset* pIOpenRowSet = NULL;2 IDBCreateSession * pIDBCreateSession = NULL;
3
4 //首先從連接對(duì)象接口找到等價(jià)的CreateSession接口
5 pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession));
6
7 //創(chuàng)建一個(gè)IOpenRowset接口
8 pIDBCreateSession->CreateSession(
9 NULL,
10 IID_IOpenRowset,
11 (Iunknown**)&pIOpenRowSet));
12
13 //與IDBInitialize等價(jià)的IDBCreateSession可以釋放了,需要時(shí)再Q(mào)uery出來(lái)就行了
14 if( pIDBCreateSession )
15 {
16 pIDBCreateSession->Release();
17 }
以上代碼通過(guò)創(chuàng)建IOpenRowset接口的方法創(chuàng)建了一個(gè)Session對(duì)象,其他的Session對(duì)象的接口可以通過(guò)QueryInterface來(lái)得到。(關(guān)于接口等價(jià)性、Session對(duì)象的接口列表,詳見(jiàn)原文:http://gamebabyrocksun.blog.163.com/blog/static/571534632008101783231453/)
值得注意的是Session對(duì)象有個(gè)IGetDataSource接口,可以通過(guò)Session對(duì)象來(lái)找回原先的數(shù)據(jù)連接對(duì)象。你可以在創(chuàng)建了IOpenRowset接口之后丟掉之前創(chuàng)建的所有接口,甚至是數(shù)據(jù)連接對(duì)象的接口,而通過(guò)IGetDataSource接口則可以方便的找回?cái)?shù)據(jù)連接對(duì)象的接口,以下代碼演示了這一過(guò)程:
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);
如同數(shù)據(jù)連接對(duì)象,Session對(duì)象也有很多屬性。你可以通過(guò)Query出ISessionProperties接口再創(chuàng)建一個(gè)屬性集并對(duì)其進(jìn)行設(shè)置,以下代碼演示了這一過(guò)程:
1 //注意Session對(duì)象只有一個(gè)屬性集合的一個(gè)屬性,就是并發(fā)級(jí)別2 //并發(fā)級(jí)別是控制數(shù)據(jù)庫(kù)并發(fā)操作的關(guān)鍵,在OLEDB中它是一系列值的位或結(jié)果
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 //創(chuàng)建一個(gè)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 //設(shè)置屬性
24 pISessionProperties->SetProperties(1,rgSessionPropSet);
在Session對(duì)象的諸多接口中,如果我們要使用SQL語(yǔ)句來(lái)操作數(shù)據(jù)源,那么我們經(jīng)常使用的一個(gè)接口就是IDBCreateCommand.這個(gè)接口并不是強(qiáng)制實(shí)現(xiàn)的。這是因?yàn)?#xff1a;OLEDB的目標(biāo)就是無(wú)論應(yīng)用系統(tǒng)要使用何種數(shù)據(jù)(結(jié)構(gòu)化的如數(shù)據(jù)庫(kù)、半結(jié)構(gòu)化的如XML,無(wú)結(jié)構(gòu)化的如互聯(lián)網(wǎng)),它都能輕松勝任,而你無(wú)需再去尋找別的接口。大多數(shù)的數(shù)據(jù)源都提供了這個(gè)接口,以便對(duì)數(shù)據(jù)庫(kù)進(jìn)行SQL操作。
三,Command對(duì)象及SQL語(yǔ)句處理
1,Command對(duì)象全貌:
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,創(chuàng)建Command對(duì)象。
通常我們使用IDBCreateCommand事務(wù)接口來(lái)創(chuàng)建Command對(duì)象,也可以直接從IDBCreateSession接口創(chuàng)建。以下代碼顯示了從連接數(shù)據(jù)庫(kù)到創(chuàng)建Command對(duì)象的完整過(guò)程:
1 //1、一大堆頭文件和定義,作為前面例子代碼的一個(gè)相對(duì)完整的總結(jié)2
3 #define COM_NO_WINDOWS_H //如果已經(jīng)包含了Windows.h或不使用其他Windows庫(kù)函數(shù)時(shí)
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、創(chuàng)建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、直接創(chuàng)建IDBProperties接口
25 pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL,
26 CLSCTX_INPROC_SERVER, NULL, IID_IDBProperties,
27 (IUnknown**)&pIDBProperties);
28
29 //3、定義屬性集合,屬性(略),設(shè)置屬性
30 ......
31 //4、連接到數(shù)據(jù)源
32 pIDBProperties->QueryInterface(IID_IDBInitialize,(void**)&pIDBInitialize);
33 pIDBInitialize->Initialize();
34
35 //5、得到事務(wù)對(duì)象
36 IDBCreateSession* pIDBCreateSession = NULL;
37 pIDBInitialize->QueryInterface(IID_IDBCreateSession,(void**)& pIDBCreateSession);
38
39 //注意這次直接創(chuàng)建了IDBCreateCommand對(duì)象作為事務(wù)對(duì)象,注意一般情況下因?yàn)檫@個(gè)接口是個(gè)可選接口,所以直接創(chuàng)建有可能會(huì)失敗,所以要檢驗(yàn)返回值
40 //在這一系列文章中我省略這些檢驗(yàn)性的操作,在實(shí)際的代碼中一定要包含豐富的錯(cuò)誤處理代碼,有關(guān)錯(cuò)誤處理的話題我將在以后的專門(mén)文章中詳細(xì)闡述,這里先
41 //聚焦于我們所關(guān)心的問(wèn)題
42
43 IDBCreateCommand* pIDBCreateCommand = NULL;
44 pIDBCreateSession->CreateSession(NULL,IID_IDBCreateCommand,(IUnknown**)&pIDBCreateCommand);
45
46 //6、創(chuàng)建Command對(duì)象,我們直接創(chuàng)建ICommandText接口
47 ICommandText* pICommandText = NULL;
48 pIDBCreateCommand->CreateCommand(NULL,IID_ICommandText,(IUnknown**)&pICommandText);
49
50 ......
51 }
在Command對(duì)象中,ICommandText接口實(shí)際是從ICommand接口派生而來(lái)的,因此,ICommandText接口實(shí)際包括了ICommand接口的全部方法。ICommandText接口有如下方法:
1 HRESULT SetCommandText(REFGUID rguidDialect,LPCOLESTR pwszCommand);2 HRESULT GetCommandText(GUID* pguidDialect,LPOLESTR* ppwszCommand);
3
4 //以下的方法實(shí)際也是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);
以下語(yǔ)法顯示了如果設(shè)置和執(zhí)行一個(gè)SQL語(yǔ)句:
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);
以下方法演示了如果一次執(zhí)行多條SQL語(yǔ)句:
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對(duì)象只有IMultipleResults這一個(gè)接口,可以通過(guò)其得到多個(gè)結(jié)果集對(duì)象及其接口(IRowset)。一般情況下,作為自己對(duì)OLEDB的封裝或是應(yīng)用,都應(yīng)用使用IMultipleResults接口,而不是直接使用IRowset。這樣做的好處是,你既可以一次只執(zhí)行一條SQL語(yǔ)句,也可以一次執(zhí)行多條SQL語(yǔ)句。以下代碼顯示了這一特性的使用:
1 IMultipleResults* pIMultipleResults = NULL;2 pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL,(IUnknown**)& pIMultipleResults);
3
4 IRowset* pIRowset = NULL;
5 DBROWCOUNT cRowsAffected = 0;//注意不要被這個(gè)參數(shù)迷惑大多數(shù)情況下它是沒(méi)用的,并不能通過(guò)它知道結(jié)果集中實(shí)際包含多少行。循環(huán)處理每一個(gè)結(jié)果集,當(dāng)然這需要你起碼知道你執(zhí)行SQL語(yǔ)句的順序
6 while( S_OK == pIMultipleResults->GetResult(NULL,
7 DBRESULTFLAG_DEFAULT,
8 IID_IRowset,
9 cRowsAffected,
10 (IUnknown**)&pIRowset) )
11 {
12 ......//處理每一個(gè)IRowset
13 pIRowset->Release();
14 pIRowset = NULL;
15 }
Command對(duì)象也有自己的屬性集,其屬性會(huì)影響因執(zhí)行SQL語(yǔ)句而得到的結(jié)果集對(duì)象的屬性(這不同于先前的兩個(gè)對(duì)象),比如結(jié)果集是否可以修改,是否可以插入,是否可以更新等等。此外,需要注意的是,一個(gè)事務(wù)對(duì)象可以創(chuàng)建n個(gè)命令對(duì)象。以下代碼顯示了如果設(shè)置命令對(duì)象(其實(shí)是結(jié)果集對(duì)象)的屬性:
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 //打開(kāi)Update屬性
7 |DBPROPVAL_UP_DELETE //打開(kāi)Delete屬性
8 |DBPROPVAL_UP_INSERT; //打開(kāi)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前設(shè)定屬性
20
21 IRowset* pIRowset = NULL;
22 pICommandText->Execute(NULL,IID_IRowset,NULL,NULL,(IUnknown**)&pIRowset)
以上代碼演示了如何打開(kāi)五個(gè)帶有更新、插入、刪除屬性的結(jié)果集。這樣,我們可以直接利用結(jié)果集對(duì)象的相關(guān)方法來(lái)修改數(shù)據(jù)并提交,同時(shí)可以繞過(guò)使用等價(jià)的SQL語(yǔ)句(UPDATE, DELETE, INSERT等),而是使用純粹代碼的方式修改數(shù)據(jù)并提交到數(shù)據(jù)庫(kù),這種方式要比直接使用SQL語(yǔ)句更加高效。
四,處理結(jié)果集
1,關(guān)于Rowset和MutipleResults對(duì)象的接口列表,可以參見(jiàn)原文:http://gamebabyrocksun.blog.163.com/blog/static/57153463200811882756317/
在Rowset對(duì)象中,我們經(jīng)常使用的接口就是IRowset, IColumnInfo和IAccessor。通過(guò)這幾個(gè)接口,才可以訪問(wèn)到我們查詢得到的數(shù)據(jù)結(jié)果。這個(gè)過(guò)程通常很復(fù)雜。
2,得到結(jié)果集:
pICommandText->Execute(NULL,IID_IRowset,NULL,NULL (IUnknown**)&pIRowset)3,得到列信息:
通過(guò)列信息可以知道結(jié)果集的完整數(shù)據(jù)結(jié)果,這為后續(xù)的創(chuàng)建訪問(wèn)器、準(zhǔn)備數(shù)據(jù)緩沖奠定了基礎(chǔ)。多數(shù)時(shí)候,在SQL的查詢語(yǔ)句是是沒(méi)有關(guān)于數(shù)據(jù)結(jié)構(gòu)的信息的,這個(gè)信息是隱含在數(shù)據(jù)庫(kù)中的,而我們查詢得到結(jié)果集時(shí)就必須要知道這個(gè)數(shù)據(jù)結(jié)構(gòu)的信息,否則對(duì)數(shù)據(jù)的訪問(wèn)就無(wú)從談起。這個(gè)過(guò)程通過(guò)IColumnsInfo接口實(shí)現(xiàn)。以下代碼演示了如何得到列信息:
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中已經(jīng)包含了一個(gè)關(guān)于列信息數(shù)據(jù)結(jié)構(gòu)的數(shù)組,
11 //數(shù)組元素的個(gè)數(shù)即cColumns 也就是最終的列數(shù)
12 //使用完畢后釋放所有的資源及接口
13 CoTaskMemFree(rgColumnInfo);
14 CoTaskMemFree(pStringBuffer);
15 if( NULL != pIColumnsInfo )
16 {
17 pIColumnsInfo->Release();
18 }
這里使用了CoTaskMemFree這個(gè)COM庫(kù)函數(shù)來(lái)完成對(duì)rgColumnInfo和pStringBuffer內(nèi)存的釋放。注意,在OLEDB的幫助文檔中說(shuō)要使用IMalloc接口的Free方法來(lái)釋放。其實(shí)二者是等價(jià)的。在使用CoTaskMemFree這個(gè)函數(shù)時(shí),并不需要檢查被釋放的指針是否為空,它內(nèi)部有檢查是否為空的機(jī)制,直接調(diào)用即可。
DBCOLUMNINFO這個(gè)結(jié)構(gòu)的原型如下:
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字符串。如果查詢中沒(méi)有明確為列指定名稱時(shí),這個(gè)字段為空,即沒(méi)有列名。
pTypeInfo:保留接口,一般為NULL。
iOrdinal:字段在結(jié)果集中的序號(hào),即表示該字段是結(jié)果集的第幾列。從1開(kāi)始,序號(hào)0是為一些特殊用途保留的(比如結(jié)果集的書(shū)簽功能)。
dwFlags:描述了列的狀態(tài)。其類型為DBCOLUMNFLAGS,是個(gè)枚舉類型,在MSDN中可以找到詳細(xì)描述。
ulColumnSize:列的大小,單位為字節(jié)。需要注意的是,這個(gè)大小僅對(duì)字符型列(即wType = DBTYPE_STR或wType = DBTYPE_WSTR)有意義。而對(duì)于其他類型的字段則設(shè)置為一個(gè)~0值(即FFFFFFFF)。
wType:結(jié)果集列的數(shù)據(jù)類型。OLEDB中定義了完整的被支持的數(shù)據(jù)類型,此處將所有類型含義及欺直列出如下:
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 };
需要特別注意的是,這里的數(shù)據(jù)類型不是數(shù)據(jù)庫(kù)支持的數(shù)據(jù)類型,或者說(shuō)并不是所有的數(shù)據(jù)庫(kù)都支持這些所有的數(shù)據(jù)類型,這僅是一個(gè)所有可能數(shù)據(jù)類型的全部概括,當(dāng)然也有一些數(shù)據(jù)庫(kù)中的類型并不在這個(gè)列表中,這時(shí)往往數(shù)據(jù)庫(kù)對(duì)應(yīng)的OLEDB接口提供程序都做了很好的轉(zhuǎn)換,已經(jīng)轉(zhuǎn)換成了這個(gè)列表中所具有的類型,因此不用擔(dān)心會(huì)碰到不支持的數(shù)據(jù)類型。
bPrecision:精度。
bScale:小數(shù)位數(shù)。
columnid:該列在數(shù)據(jù)庫(kù)系統(tǒng)字典表中的id號(hào)。
4,創(chuàng)建綁定:
綁定是OLEDB中最重要最核心的概念之一,它是我們?cè)L問(wèn)數(shù)據(jù)的關(guān)鍵操作。對(duì)其最簡(jiǎn)單的理解就是:安排得到數(shù)據(jù)的內(nèi)存擺放方式,并將這一方式告訴數(shù)據(jù)提供者,讓它按要求將數(shù)據(jù)擺放到我們指定的內(nèi)存中。為此,我們就需要自己創(chuàng)建一個(gè)被稱作DBBINDING的數(shù)組,該結(jié)構(gòu)體聲明如下:
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;
可以看出,這個(gè)結(jié)構(gòu)和前面的DBCOLUMNINFO結(jié)構(gòu)很相似。其實(shí)它們的字段大多數(shù)確實(shí)是一致的,甚至可以直接使用DBCOLUMNINFO對(duì)DBBINDING進(jìn)行賦值。但是二者字段的含義是完全不同的。首先DBCOLUMNINFO是數(shù)據(jù)提供者給你的信息,它是固定的,對(duì)相同的查詢來(lái)說(shuō),列總是相同的,因此數(shù)據(jù)提供者返回的DBCOLUMNINFO數(shù)組也是固定的。而DBBINDING是你作為數(shù)據(jù)消費(fèi)者創(chuàng)建之后給數(shù)據(jù)提供者的一個(gè)結(jié)構(gòu)數(shù)組,它的內(nèi)容完全有你來(lái)控制。通過(guò)這個(gè)結(jié)構(gòu)我們可以指定數(shù)據(jù)提供者最終將數(shù)據(jù)擺放成我們指定的格式,或者進(jìn)行指定的數(shù)據(jù)類型轉(zhuǎn)換。以下例子展示了如何完成這個(gè)過(guò)程:
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 }
需要注意的是用紅色標(biāo)的四行。其中第一句指明數(shù)據(jù)提供者最終提交數(shù)據(jù)時(shí)必須包含的信息,這里指定了列值(DBPART_VALUE),長(zhǎng)度(DBPART_LENGTH)和狀態(tài)(DBPART_STATUS)共3個(gè)信息,列值不用說(shuō)就是要放字段的最終結(jié)果值,長(zhǎng)度就是這個(gè)字段占用的字節(jié)長(zhǎng)度,列狀態(tài)中將存放的是值的狀態(tài)比如是否為空等。
后面3句則明確的指出了以上三個(gè)信息在內(nèi)存中擺放的偏移位置,通常按習(xí)慣,我們要求數(shù)據(jù)提供者先擺放狀態(tài),再擺放長(zhǎng)度,最后擺放數(shù)據(jù)。
5,創(chuàng)建訪問(wèn)器:
有了綁定結(jié)構(gòu)之后,接下來(lái)的工作就是通知數(shù)據(jù)提供者按照我們的要求對(duì)數(shù)據(jù)進(jìn)行“格式化”。這個(gè)過(guò)程就是創(chuàng)建一個(gè)訪問(wèn)器。創(chuàng)建訪問(wèn)器就要使用IAccessor接口,同樣這個(gè)接口也是從IRowset查詢得來(lái),代表訪問(wèn)器的標(biāo)志則是一個(gè)類型為HACCESSOR的句柄。以下代碼演示了如果創(chuàng)建一個(gè)訪問(wèn)器:
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接口,表示創(chuàng)建了訪問(wèn)器后這個(gè)接口也就沒(méi)有用了。如果需要我們其實(shí)也可以隨時(shí)從同一IRowset接口再查詢出這個(gè)接口。因?yàn)檫@兩個(gè)接口所表示的結(jié)果集對(duì)象是同一個(gè)。
6,得到數(shù)據(jù):
以下代碼演示了得到數(shù)據(jù)的過(guò)程:
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行數(shù)據(jù)的緩沖,然后反復(fù)讀取cRows行到這里,然后逐行處理之
9 pData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwOffset * cRows);
10 while( S_OK == pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0,cRows,&cRowsObtained, &rghRows)) )
11 {//循環(huán)讀取數(shù)據(jù),每次循環(huán)默認(rèn)讀取cRows行,實(shí)際讀取到cRowsObtained行
12 for( iRow = 0; iRow < cRowsObtained; iRow++ )
13 {
14 pCurData = (BYTE*)pData + (dwOffset * iRow);
15 pIRowset->GetData( rghRows[iRow],hAccessor,pCurData));
16 //pCurData中已經(jīng)包含了結(jié)果數(shù)據(jù),顯示或者進(jìn)行處理
17 ......
18 }
19 if( cRowsObtained )
20 {//釋放行句柄數(shù)組
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的數(shù)據(jù),讀出了多少行,數(shù)組中應(yīng)有多少行的句柄,然后再使用句柄調(diào)用GetData將數(shù)據(jù)讀到指定的內(nèi)存位置。最后每次都釋放了這個(gè)數(shù)據(jù),因?yàn)閿?shù)據(jù)已經(jīng)詩(shī)刊了我們分配的緩存中,這個(gè)句柄數(shù)組也就沒(méi)有什么意義了。最終數(shù)據(jù)讀取完畢,我們也就釋放了IRowset接口。
EOF。
轉(zhuǎn)載于:https://www.cnblogs.com/cmleung/archive/2012/03/22/2411646.html
超強(qiáng)干貨來(lái)襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
- 上一篇: 获取Windows Shell的简单示例
- 下一篇: SDNU 1406.A+B问题(水题)