title: OLEDB存取BLOB型數(shù)據(jù)
tags: [OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), BLOB型數(shù)據(jù)]
date: 2018-04-20 20:49:37
categories: windows 數(shù)據(jù)庫(kù)編程
keywords: OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), 打開(kāi)數(shù)據(jù)源對(duì)象
現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)除了支持一些標(biāo)準(zhǔn)的通用數(shù)據(jù)類(lèi)型以外小槐,大多數(shù)還支持一種稱之為BLOB型的數(shù)據(jù)群发。
BLOB全稱為big large object bytes, 大二進(jìn)制對(duì)象類(lèi)型,這種類(lèi)型的數(shù)據(jù)通常用于存儲(chǔ)文檔刘急、圖片、音頻等文件,這些文件一般體積較大,保存這些文件可以很方便的管理和檢索這類(lèi)信息朗徊。在MS SQLSERVER中常見(jiàn)的BLOB數(shù)據(jù)類(lèi)型有text、ntext(n表示unicode)偎漫、image爷恳、nvarchar、varchar骑丸、varbinary等。其中image基本可以用來(lái)保存一切二進(jìn)制文件妒貌,比如word通危、Excel、音頻灌曙、視頻等等類(lèi)型菊碟。
針對(duì)BLOB型數(shù)據(jù),OLEDB也提供了對(duì)它的支持
使用BLOB型數(shù)據(jù)的利弊
一般數(shù)據(jù)庫(kù)對(duì)BLOB型數(shù)據(jù)有特殊的處理方式在刺,比如壓縮等等逆害,在數(shù)據(jù)庫(kù)中存儲(chǔ)BLOB數(shù)據(jù)可以方便的進(jìn)行檢索,展示蚣驼,備份等操作魄幕。但是由于BLOB型數(shù)據(jù)本身比較大,存儲(chǔ)量太大時(shí)數(shù)據(jù)量太大容易拖慢數(shù)據(jù)庫(kù)性能颖杏,所以一般的說(shuō)法都是盡量不要在數(shù)據(jù)庫(kù)中存儲(chǔ)這類(lèi)信息纯陨。特別是圖片,音視頻留储。針對(duì)這類(lèi)文件一般的做法是將其保存在系統(tǒng)的某個(gè)路徑鐘中翼抠,而在數(shù)據(jù)庫(kù)中存儲(chǔ)對(duì)應(yīng)的路徑
操作BLOB型數(shù)據(jù)的一般方法
一般針對(duì)BLOB不能像普通數(shù)據(jù)那樣操作,而需要一些特殊的操作获讳,在OLEDB中通過(guò)設(shè)置綁定結(jié)構(gòu)中的一些特殊值最終指定獲取BLOB型數(shù)據(jù)的一個(gè)ISequentialStream接口指針,最終會(huì)通過(guò)這個(gè)接口來(lái)進(jìn)行BLOB型數(shù)據(jù)的讀寫(xiě)操作
判斷一個(gè)列是否是BLOB型數(shù)據(jù)
判斷某個(gè)列是否是BLOB型數(shù)據(jù)一般通過(guò)如下兩個(gè)條件:
- pColumnInfo[i].wType == DBTYPE_IUNKNOW : 包含當(dāng)列信息的DBCOLUMNSINFO 結(jié)構(gòu)體對(duì)象的wType值為DBTYPE_IUNKNOW阴颖,該列的類(lèi)型為DBTYPE_IUNKNOW,該條件也被稱為列類(lèi)型判定
- pColumnInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG :當(dāng)列信息中的dwFlag值為DBCOLUMNFLAGS_ISLONG,也就是說(shuō)該列的標(biāo)識(shí)中包含DBCOLUMNFLAGS_ISLONG屬性丐膝,該判定條件也被稱之為列標(biāo)識(shí)判定
當(dāng)這兩個(gè)條件之一成立之時(shí)量愧,我們就可以斷定這列為BLOB型數(shù)據(jù)
BLOG型數(shù)據(jù)的綁定
在進(jìn)行BLOB型數(shù)據(jù)的綁定也有特殊要求钾菊,主要體現(xiàn)在下面幾點(diǎn):
- 綁定結(jié)構(gòu)的cbMaxLength 需要設(shè)置為0
- 綁定結(jié)構(gòu)的wType設(shè)置為DBTYPE_IUNKNOW
- 為結(jié)構(gòu)的pObject指針?lè)峙鋬?nèi)存,大小等于DBOBJECT結(jié)構(gòu)的大小
- 指定pObject的成員
pObject->iid = IID_ISequentialStream
pObject->dwFlags = STGM_READ - 為行緩沖長(zhǎng)度加上一個(gè)IStream指針的長(zhǎng)度侠畔,此時(shí)數(shù)據(jù)源不再提供查詢到的數(shù)據(jù)而提供一個(gè)接口指針结缚,后續(xù)對(duì)BLOB數(shù)據(jù)的操作都使用該指針進(jìn)行
最后使用完后記得釋放pObject所指向的內(nèi)存空間
讀取BLOB數(shù)據(jù)
根據(jù)前面所說(shuō)的創(chuàng)建綁定結(jié)構(gòu),并為綁定結(jié)構(gòu)賦值软棺,最終可以從結(jié)果集中獲取到一個(gè)ISequentialStream接口指針红竭。調(diào)用接口的Read方法可以讀取到BLOB列中的數(shù)據(jù),而B(niǎo)LOB數(shù)據(jù)的長(zhǎng)度存儲(chǔ)在綁定時(shí)指定的數(shù)據(jù)長(zhǎng)度內(nèi)存偏移處,這與普通列的長(zhǎng)度存放返回方式是一樣的喘落,一般BLOB數(shù)據(jù)都比較長(zhǎng)茵宪,這個(gè)時(shí)候就需要分段讀取。
在使用ISequentialStream接口操作BLOB型數(shù)據(jù)時(shí)需要注意的一個(gè)問(wèn)題是瘦棋,有的數(shù)據(jù)庫(kù)不支持在一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB數(shù)據(jù)列稀火。一般BLOB數(shù)據(jù)列及其的消耗資源,并且數(shù)據(jù)庫(kù)鼓勵(lì)我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)表結(jié)構(gòu)的時(shí)候做到一行只有一列BLOB數(shù)據(jù)赌朋,因此很多數(shù)據(jù)庫(kù)并不支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù)凰狞。
要判斷數(shù)據(jù)庫(kù)是否支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù),可以獲取DBPROP_MULTIPLESTORAGEOBJECTS屬性沛慢,該屬性屬于屬性集DBPROPSET_ROWSET赡若,它是一個(gè)只讀屬性,如果該屬性的值為T(mén)RUE表示支持团甲,為FALSE表示不支持逾冬。
下面是一個(gè)讀取BLOB型數(shù)據(jù)的例子,數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)為:id(int)、text(image)躺苦、png(image)身腻、jpg(image)
void ReadBLOB(IRowset *pIRowset)
{
COM_DECLARE_INTERFACE(IColumnsInfo);
COM_DECLARE_INTERFACE(IAccessor);
DBORDINAL cColumns = 0;
DBCOLUMNINFO* rgColumnsInfo = NULL;
LPOLESTR lpszColumnsName = NULL;
DBBINDING* rgBindings = NULL;
DBBINDING** ppBindings = NULL; //綁定結(jié)構(gòu)數(shù)組
DWORD *puDataLen = NULL; //當(dāng)前訪問(wèn)器所需內(nèi)存大小
DWORD *pulColCnt = NULL; //當(dāng)前訪問(wèn)器中包含的項(xiàng)
ULONG ulBindCnt = 0; //訪問(wèn)器的數(shù)量
ULONG uBlob = 0; //當(dāng)前有多少blob數(shù)據(jù)
HACCESSOR* phAccessor = NULL;
HROW* hRow = NULL;
DBCOUNTITEM ulGetRows = 0;
ULONG uCols = 0;
PVOID pData1 = NULL; //第1個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
PVOID pData2 = NULL; //第2個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
PVOID pData3 = NULL; //第3個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
HRESULT hRes = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
COM_SUCCESS(hRes, _T("查詢接口pIColumnsInfo失敗,錯(cuò)誤碼為:%08x\n"), hRes);
hRes = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnsInfo, &lpszColumnsName);
COM_SUCCESS(hRes, _T("獲取結(jié)果集列信息失敗匹厘,錯(cuò)誤碼為:%08x\n"), hRes);
ppBindings = (DBBINDING**)COM_ALLOC(sizeof(DBBINDING*));
rgBindings = (DBBINDING*)COM_ALLOC(sizeof(DBBINDING) * cColumns);
pulColCnt = (DWORD*)COM_ALLOC(sizeof(DWORD));
puDataLen = (DWORD*)COM_ALLOC(sizeof(DWORD));
for (int i = 0; i < cColumns; i++)
{
//如果當(dāng)前訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)的數(shù)組的首地址為空,將當(dāng)前綁定結(jié)構(gòu)指針作為綁定結(jié)構(gòu)數(shù)組的首地址
if (NULL == ppBindings[ulBindCnt])
{
ppBindings[ulBindCnt] = &rgBindings[i];
}
++pulColCnt[ulBindCnt];
rgBindings[i].bPrecision = rgColumnsInfo[i].bPrecision;
rgBindings[i].bScale = rgBindings[i].bScale;
rgBindings[i].cbMaxLen = 10 * sizeof(WCHAR);
rgBindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[i].dwPart = DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE;
rgBindings[i].eParamIO = DBPARAMIO_NOTPARAM;
rgBindings[i].iOrdinal = rgColumnsInfo[i].iOrdinal;
rgBindings[i].obStatus = puDataLen[ulBindCnt];
rgBindings[i].obLength = puDataLen[ulBindCnt] + sizeof(DBSTATUS);
rgBindings[i].obValue = rgBindings[i].obLength + sizeof(ULONG);
rgBindings[i].wType = DBTYPE_WSTR;
if (rgColumnsInfo[i].wType == DBTYPE_IUNKNOWN ||
rgColumnsInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
{
rgBindings[i].cbMaxLen = 0;
rgBindings[i].wType = DBTYPE_IUNKNOWN;
rgBindings[i].pObject = (DBOBJECT*)COM_ALLOC(sizeof(DBOBJECT));
rgBindings[i].pObject->iid = IID_ISequentialStream;
rgBindings[i].pObject->dwFlags = STGM_READ;
uBlob++;
}
//記錄下每個(gè)訪問(wèn)器所需內(nèi)存的大小
puDataLen[ulBindCnt] = rgBindings[i].obValue + rgBindings[i].cbMaxLen;
if (rgBindings[i].wType == DBTYPE_IUNKNOWN)
{
puDataLen[ulBindCnt] = rgBindings[i].obValue + sizeof(ISequentialStream*);
}
puDataLen[ulBindCnt] = UPGROUND(puDataLen[ulBindCnt]);
//判斷當(dāng)前是否需要?jiǎng)?chuàng)建單獨(dú)的訪問(wèn)器
if ((uBlob || rgBindings[i].iOrdinal == 0))
{
ulBindCnt++;
ppBindings = (DBBINDING**)COM_REALLOC(ppBindings, sizeof(DBBINDING*) * (ulBindCnt + 1));
puDataLen = (DWORD*)COM_REALLOC(puDataLen, sizeof(DWORD) * (ulBindCnt + 1));
pulColCnt = (DWORD*)COM_REALLOC(pulColCnt, sizeof(DWORD) * (ulBindCnt + 1));
}
}
//創(chuàng)建訪問(wèn)器
phAccessor = (HACCESSOR*)COM_ALLOC( (ulBindCnt + 1) * sizeof(HACCESSOR));
hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
COM_SUCCESS(hRes, _T("查詢IAccessor接口失敗,錯(cuò)誤碼為:%08x\n"), hRes);
for (int i = 0; i < ulBindCnt; i++)
{
hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pulColCnt[i], ppBindings[i], 0, &phAccessor[i], NULL);
COM_SUCCESS(hRes, _T("創(chuàng)建訪問(wèn)器失敗扰路,錯(cuò)誤碼為:%08x\n"), hRes);
}
//讀取其中的一行數(shù)據(jù)
hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &ulGetRows, &hRow);
COM_SUCCESS(hRes, _T("讀取行數(shù)據(jù)失敗尤溜,錯(cuò)誤碼為:%08x\n"), hRes);
//讀取第一個(gè)綁定結(jié)構(gòu)中的信息
pData1 = COM_ALLOC(puDataLen[0]);
hRes = pIRowset->GetData(hRow[0], phAccessor[0], pData1);
for(int i = 0; i < pulColCnt[0]; i++)
{
if (ppBindings[0][i].wType == DBTYPE_IUNKNOWN)
{
DBSTATUS dbStatus = *(DBSTATUS*)((BYTE*)pData1 + ppBindings[0][i].obStatus);
if (dbStatus == DBSTATUS_S_OK)
{
ULONG uFileLen = *(ULONG*)((BYTE*)pData1 + ppBindings[0][i].obLength);
if (uFileLen > 0)
{
DWORD dwReaded = 0;
PVOID pFileData = COM_ALLOC(uFileLen);
ZeroMemory(pFileData, uFileLen);
ISequentialStream *pSeqStream = *(ISequentialStream**)((BYTE*)pData1 + ppBindings[0][i].obValue);
pSeqStream->Read(pFileData, uFileLen, &dwReaded);
WriteFileData(_T("1.txt"), pFileData, dwReaded);
}
}
}
}
//后續(xù)的部分就不再寫(xiě)出來(lái)了,寫(xiě)法與上面的代碼類(lèi)似
pIRowset->ReleaseRows(1, hRow, NULL, NULL, NULL);
__CLEAR_UP:
//后面是清理的代碼
由于我們事先知道數(shù)據(jù)表的結(jié)構(gòu)汗唱,它有3個(gè)BLOB型數(shù)據(jù),所以這里直接定義了3個(gè)緩沖用來(lái)接收3個(gè)BLOB型數(shù)據(jù)哩罪。為了方便檢測(cè)授霸,我們另外寫(xiě)了一個(gè)的函數(shù)巡验,將讀取出來(lái)的BLOB數(shù)據(jù)寫(xiě)入到文件中碘耳,事后以文件顯示是否正確來(lái)測(cè)試這段代碼
首先還是與以前一樣,獲取數(shù)據(jù)表的結(jié)構(gòu)捕捂,然后進(jìn)行綁定斗搞,注意這里由于使用的是SQL Server,它不支持一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB允悦,所以這里沒(méi)有判斷直接綁定不同的訪問(wèn)器虑啤。
在綁定的時(shí)候使用ulBindCnt作為當(dāng)前訪問(wèn)器的數(shù)量,在循環(huán)里面有一個(gè)判斷當(dāng)(uBlob || rgBindings[i].iOrdinal == 0) && (ulBindCnt != cColumns - 1)條件成立時(shí)將訪問(wèn)器的數(shù)量加1全闷,該條件表示之前已經(jīng)有blob型數(shù)據(jù)(之前SQL不支持一個(gè)訪問(wèn)器訪問(wèn)多個(gè)BLOB铣墨,如果之前已經(jīng)有BLOB數(shù)據(jù)了伊约,就需要另外創(chuàng)建訪問(wèn)器)或者當(dāng)前是第0行(因?yàn)榈?行只允許讀孕蝉,所以將其作為與BLOB型數(shù)據(jù)一樣處理)降淮,當(dāng)這些條件成立時(shí)會(huì)新增一個(gè)訪問(wèn)器超埋,而隨著訪問(wèn)器的增加佳鳖,需要改變ppBindings數(shù)組中的元素,該數(shù)組存儲(chǔ)的是訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)開(kāi)始的指針来庭。數(shù)組puDataLen表示的是當(dāng)前訪問(wèn)器所需內(nèi)存的大小穿挨,pulColCnt表示當(dāng)前訪問(wèn)器中共有多少列,針對(duì)這個(gè)表最終這些結(jié)構(gòu)的內(nèi)容大致如下圖:
綁定完成之后菜皂,后面就是根據(jù)數(shù)組中的內(nèi)容創(chuàng)建對(duì)應(yīng)的訪問(wèn)器厉萝,然后綁定冀泻、讀取數(shù)據(jù),針對(duì)BLOB數(shù)據(jù)胳施,我們還是一樣從對(duì)應(yīng)緩沖的obValue偏移處得到接口指針肢专,然后調(diào)用接口的Read方法讀取博杖,最后寫(xiě)入文件
BLOB數(shù)據(jù)的寫(xiě)入:
要寫(xiě)入BLOB型數(shù)據(jù)也需要使用ISequentialStream接口,但是它不像之前可以直接使用接口的Write方法,寫(xiě)入的對(duì)象必須要自己從ISequentialStream接口派生哩盲,并指定一段內(nèi)存作為緩沖狈醉,以便供OLEDB組件調(diào)用寫(xiě)方法時(shí)作為數(shù)據(jù)緩沖。這段緩沖必須要保證分配在COM堆上抒线,也就是要使用CoTaskMemory分配內(nèi)存渣慕。
這里涉及到的對(duì)象主要有IStream逊桦、ISequentialStream、IStorage睡陪、ILockBytes夕凝,同樣,并不是所有數(shù)據(jù)源都支持這4類(lèi)對(duì)象逮矛,具體支持哪些可以查詢DBPROPSET_DATASOURCEINFO屬性集中的DBPROP_STRUCTUREDSTORAGE屬性來(lái)判定须鼎,目前SQL Server中支持ISequentialStream接口。
雖然我們可以使用這種方式來(lái)實(shí)現(xiàn)讀寫(xiě)B(tài)LOB汞窗,但是每種數(shù)據(jù)源支持的程度不同赡译,而且有的數(shù)據(jù)源甚至不支持這種方式蝌焚,為了查詢對(duì)讀寫(xiě)B(tài)LOB數(shù)據(jù)支持到何種程度,可以查詢DBPROPSET_DATASOURCEINFO屬性集合的DBPROP_OLEOBJECTS屬性來(lái)判定
通常有以下幾種支持方式(DBPROP_OLEOBJECTS屬性的值,按位設(shè)置):
- DBPROPVAL_OO_BLOB: 就是之前介紹的接口方式许帐,使用接口的方式來(lái)讀寫(xiě)B(tài)LOB數(shù)據(jù)
DBPROPVAL_OO_DIRECTBIND: 可以直接綁定在行中,通過(guò)行訪問(wèn)器像普通列一樣訪問(wèn)毕谴,也就是說(shuō)它不需要獲取專門(mén)的指針來(lái)操作涝开,他可以就像操作普通數(shù)據(jù)那樣,分配對(duì)應(yīng)內(nèi)存就可以訪問(wèn)惧浴,但是要注意分配內(nèi)存的大小奕剃,每行中對(duì)應(yīng)列中BLOB的數(shù)據(jù)長(zhǎng)度差別可能會(huì)很明顯纵朋,比如有的可能是一部長(zhǎng)達(dá)2小時(shí)的電影文件茄袖,而有的可能是一部短視頻宪祥,它們之間的差距可能會(huì)達(dá)到上G家乘,而按照最小的來(lái)可能會(huì)發(fā)生截?cái)嗖匕模醋畲蟮姆峙淇赡軙?huì)發(fā)生多達(dá)好幾個(gè)G的內(nèi)存浪費(fèi)
DBPROPVAL_OO_IPERSIST:通過(guò)IPersistStream, IPersistStreamInit, or IPersistStorage三個(gè)接口的Persist對(duì)象訪問(wèn)
DBPROPVAL_OO_ROWOBJECT: 支持整行作為一個(gè)對(duì)象來(lái)訪問(wèn),通過(guò)結(jié)果集對(duì)象的IGetRow接口來(lái)獲得行對(duì)象翔悠,但是這種模式會(huì)破壞第三范式,所以一般數(shù)據(jù)庫(kù)都不支持
DBPROPVAL_OO_SCOPED: 通過(guò)IScopedOperations接口來(lái)暴露行對(duì)象,通過(guò)這個(gè)接口可以暴露一個(gè)樹(shù)形的結(jié)果集對(duì)象
DBPROPVAL_OO_SINGLETON: 直接通過(guò)ICommand::Execute和IOpenRowset::OpenRowset來(lái)打開(kāi)行對(duì)象
下面是插入BLOB數(shù)據(jù)的一個(gè)實(shí)例
//自定義一個(gè)
class CSeqStream : public ISequentialStream
{
public:
// Constructors
CSeqStream();
virtual ~CSeqStream();
public:
virtual BOOL Seek(ULONG iPos); //將當(dāng)前內(nèi)存指針偏移到指定位置
virtual BOOL CompareData(void* pBuffer); //比較兩段內(nèi)存中的值
virtual ULONG Length()
{
return m_cBufSize;
};
virtual operator void* const()
{
return m_pBuffer;
};
public:
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv);
//讀寫(xiě)內(nèi)存的操作,這些是必須實(shí)現(xiàn)的函數(shù)
STDMETHODIMP Read(
/* [out] */ void __RPC_FAR *pv,
/* [in] */ ULONG cb,
/* [out] */ ULONG __RPC_FAR *pcbRead);
STDMETHODIMP Write(
/* [in] */ const void __RPC_FAR *pv,
/* [in] */ ULONG cb,
/* [out]*/ ULONG __RPC_FAR *pcbWritten);
private:
ULONG m_cRef; // reference count
void* m_pBuffer; // buffer
ULONG m_cBufSize; // buffer size
ULONG m_iPos; // current index position in the buffer
};
//插入數(shù)據(jù)第一列BLOB數(shù)據(jù)
//這里由于已經(jīng)事先知道每列的數(shù)據(jù)結(jié)構(gòu)妇斤,因此采用偷懶的方法丹拯,一行行的插入
pData1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pdwDataLen[nCol]);
for(int i = 0; i < pulColCnt[nCol]; i++)
{
if (DBTYPE_IUNKNOWN == ppBindings[nCol][i].wType)
{
*(DBSTATUS*)((BYTE*)pData1 + ppBindings[nCol][i].obStatus) = DBSTATUS_S_OK;
CSeqStream *pSeqStream = new CSeqStream();
GetFileData(_T("test.txt"), dwFileLen, pFileData);
pSeqStream->Write(pFileData, dwFileLen, &dwWritten);
pSeqStream->Seek(0); //寫(xiě)這個(gè)操作將緩存的指針偏移到了最后咽笼,需要調(diào)整一下,以便OLEDB組件在插入BLOB數(shù)據(jù)時(shí)從緩存中讀取
HeapFree(GetProcessHeap(), 0, pFileData);
*(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = dwFileLen;
*(ISequentialStream**)((BYTE*)pData1 + ppBindings[nCol][i].obValue) = pSeqStream;
//此處不用release pSeqStream媳纬,COM組件會(huì)自動(dòng)釋放
}else
{
//根據(jù)數(shù)據(jù)庫(kù)定義钮惠,此處應(yīng)該為ID
*(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = 10;
if (DBTYPE_WSTR == ppBindings[nCol][i].wType)
{
StringCchCopy((LPOLESTR)((BYTE*)pData1 + ppBindings[nCol][i].obValue), 10, SysAllocString(OLESTR("1")));
}
}
}
hRes = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, phAccessor[nCol], pData1, &hNewRow);
COM_SUCCESS(hRes, _T("插入第1列BLOB數(shù)據(jù)失敗七芭,錯(cuò)誤碼為:%08x\n"), hRes);
在上面的代碼中首先定義一個(gè)派生類(lèi)狸驳,用來(lái)進(jìn)行BLOB數(shù)據(jù)的讀寫(xiě),然后在后面的代碼中演示了如何使用它
在后面的一段代碼中撰糠,基本步驟和之前一樣辩昆,經(jīng)過(guò)連接數(shù)據(jù)源、創(chuàng)建回話對(duì)象术辐,打開(kāi)表辉词,然后綁定,獲取行訪問(wèn)器隧魄,這里由于代碼基本不變隘蝎,為了節(jié)約篇幅所以省略它們嘱么,只貼出最重要的部分。
在插入的代碼中几迄,首先查找訪問(wèn)器中的各個(gè)列的屬性冰评,如果是BLOB數(shù)據(jù)就采用BLOB數(shù)據(jù)的插入辦法甲雅,否則用一般數(shù)據(jù)的插入辦法。插入BLOB數(shù)據(jù)時(shí)弛姜,首先創(chuàng)建一個(gè)派生類(lèi)的對(duì)象妖枚,注意此處由于后續(xù)要交給OLEDB組件調(diào)用绝页,所以不能用棧內(nèi)存。我們先調(diào)用類(lèi)的Write方法將內(nèi)存寫(xiě)入對(duì)應(yīng)的緩沖中结啼,然后調(diào)用Seek函數(shù)將內(nèi)存指針偏移到緩沖的首地址屈芜,這個(gè)指針的作用就相當(dāng)于文件的文件指針井佑,COM組件在調(diào)用對(duì)應(yīng)函數(shù)將它插入數(shù)據(jù)庫(kù)時(shí)會(huì)采用這個(gè)內(nèi)存的指針,所以必須將其置到首地址處焦蘑。讓后將對(duì)象的指針?lè)湃氲綄?duì)應(yīng)的obvalues偏移中例嘱,設(shè)置對(duì)應(yīng)的數(shù)據(jù)大小為BLOB數(shù)據(jù)的大小宁舰,最后只要像普通數(shù)據(jù)類(lèi)型那樣調(diào)用對(duì)應(yīng)的更新方法即可實(shí)現(xiàn)BLOB數(shù)據(jù)的插入
最后貼上兩個(gè)例子的詳細(xì)代碼地址
示例1:BLOB數(shù)據(jù)的讀取
示例2:BLOB數(shù)據(jù)的插入