您的位置:首页 > 编程语言 > C语言/C++

VC++ 实战OLEDB编程(七)一个完整的例子 (转)

2011-05-22 11:31 429 查看
为了方便大家总结和学习前面的章节内容,此处给出一个完整的OLEDB的例子,作为前面所有内容的一个总结,使用VS2008新建一个空的Win32项目,然后新建一个cpp文件,将所有代码都复制粘贴进去,然后编译执行即可,因为这个例子没有什么输出,所以建议使用调试方式一步步执行看程序的执行逻辑,代码中注释的部分并不表示没有用处,而是一些功能性的代码,有些事开关,有些是特殊功能,可以自己选择性恢复,然后查看下执行的效果。同时为了阅读和学习方便,例子中并没有封装任何子函数或类等代码结构,只有主入口函数,然后就是一气呵成的访问过程,重点在于帮助大家理解,并不表示这是一种被鼓励的编程风格,实际使用中,鼓励大家封装不同的功能函数,实现其中的某些关键性步骤,做到通用灵活,调用简单。另外请注意,因为使用了goto语句,所以所有的变量都在一开始处进行了定义和初始化,以方便最后的释放操作能做出正确的判断,这影响了代码的可读性,因为变量的定义,和变量的使用发生了分离,对于阅读和学习来说我想这个不是太大的问题,请读者自己注意每个变量的用途和类型。

有任何问题请跟帖。

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501

#include <Windows.h>
#include <ole2.h>
#include <TCHAR.h>
#define COM_NO_WINDOWS_H //如果已经包含了Windows.h或不使用其他Windows
//库函数时
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0270
#include <oledb.h>
#include <oledberr.h>
//#include <msdaguid.h>
#include <msdasc.h>

#define GRS_ROUNDUP_AMOUNT 8
#define GRS_ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1))
#define GRS_ROUNDUP(size) GRS_ROUNDUP_(size, GRS_ROUNDUP_AMOUNT)

#define GRS_ALLOC(size) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size)
#define GRS_FREE(p) if( NULL != (p) ){HeapFree(GetProcessHeap(),0,p);(p) = NULL;}

#define GRS_COM_RELEASE(p) if(p){(p)->Release();(p)=NULL;}
#define GRS_COM_CHECK(a) if(FAILED(a)){::DebugBreak();goto GRS_CLEARUP;}

#define GRS_USE_IDBCREATECOMMAND //使用IDBCreateCommand接口创建Session对象 注释后使用习惯的IOpenRowset接口
#define GRS_USE_IMULTIPLERESULTS //使用IMultipleResults接口得到结果集 注释后直接使用

int WINAPI _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
::CoInitialize(NULL);

IDBPromptInitialize* pIDBPromptInitialize = NULL;
IDBInitialize* pIDBInitialize = NULL;
IDBCreateSession* pIDBCreateSession = NULL;
IOpenRowset* pIOpenRowSet = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;
ICommandProperties* pICommandProperties = NULL;
IMultipleResults* pIMultipleResults = NULL;
IRowset* pIRowset = NULL;
IColumnsInfo* pIColumnsInfo = NULL;
IAccessor* pIAccessor = NULL;
IRowsetChange* pIRowsetChange = NULL;

DBPROPSET ps[1];
DBPROP prop[2];
//==============================================================================================
//注意改写这个SQL语句,首先保证SQL语句是正确的
TCHAR* pSQL = _T("SELECT 地址.名字, 地址.姓氏 FROM 地址;");
//==============================================================================================
ULONG cColumns = 0;
DBCOLUMNINFO* rgColumnInfo = NULL;
LPWSTR pStringBuffer = NULL;

DBROWCOUNT cRowsAffected = 0;//注意不要被这个参数迷惑大多数情况下它是没用的
//并不能通过它知道结果集中实际包含多少行

ULONG iCol = 0;
ULONG dwOffset = 0;
DBBINDING* rgBindings = NULL;
HACCESSOR phAccessor = NULL;

void* pData = NULL;
ULONG cRowsObtained = 0;
HROW* rghRows = NULL;
ULONG iRow = 0;
LONG cRows = 10;//一次读取10行
DBROWSTATUS pdwStatus = 0; //行状态 实际就是DWORD类型
void* pCurData = NULL;
void* pNewData = NULL;
HROW hNewRows = NULL;

//============================================================================================================
//1、初始化部分,数据库连接
GRS_COM_CHECK(CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER,
IID_IDBPromptInitialize, (void **)&pIDBPromptInitialize));
//下面这句将弹出前面所说的对话框
GRS_COM_CHECK(pIDBPromptInitialize->PromptDataSource(NULL, ::GetDesktopWindow(),
DBPROMPTOPTIONS_PROPERTYSHEET, 0, NULL, NULL, IID_IDBInitialize,
(IUnknown **)&pIDBInitialize));
GRS_COM_CHECK(pIDBInitialize->Initialize());//根据对话框采集的参数连接到指定的数据库
//============================================================================================================

//============================================================================================================
//2、创建事务部分
GRS_COM_CHECK( pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession) );
//创建一个IOpenRowset接口 或者直接创建一个IDBCreateCommand 接口
#ifdef GRS_USE_IDBCREATECOMMAND
//使用IDBCreateCommand的方式
GRS_COM_CHECK(pIDBCreateSession->CreateSession(NULL,IID_IDBCreateCommand,(IUnknown**)&pIDBCreateCommand));
#else
//使用IOpenRowset的方式
GRS_COM_CHECK(pIDBCreateSession->CreateSession(NULL,IID_IOpenRowset,(IUnknown**)&pIOpenRowSet) );
GRS_COM_CHECK(pIOpenRowSet->QueryInterface(IID_IDBCreateCommand,(void**)&pIDBCreateCommand) );
#endif
//与IDBInitialize等价的IDBCreateSession可以释放了,需要时再Query出来就行了
GRS_COM_RELEASE(pIDBCreateSession);
//============================================================================================================

//============================================================================================================
//3、创建Command对象
GRS_COM_CHECK(pIDBCreateCommand->CreateCommand(NULL,IID_ICommandText,(IUnknown**)&pICommandText));
#ifndef GRS_USE_IDBCREATECOMMAND
//当使用IOpenRowset接口创建Session对象时 IDBCreateCommand接口使用完毕就可以释放了 因为我们有IOpenRowset接口
GRS_COM_RELEASE(pIDBCreateCommand);
#endif
//============================================================================================================

//============================================================================================================
//4、设置属性 主要是打开可更新的属性
ZeroMemory(ps,1 * sizeof(DBPROPSET));
ZeroMemory(prop, 2 * sizeof(DBPROP));

prop[0].dwPropertyID = DBPROP_UPDATABILITY;
prop[0].vValue.vt = VT_I4;
prop[0].vValue.lVal=DBPROPVAL_UP_CHANGE //打开Update属性
|DBPROPVAL_UP_DELETE //打开Delete属性
|DBPROPVAL_UP_INSERT; //打开Insert属性
prop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
prop[0].colid = DB_NULLID;

ps[0].guidPropertySet = DBPROPSET_ROWSET; //注意属性集合的名称
ps[0].cProperties = 1;
ps[0].rgProperties = prop;

GRS_COM_CHECK(pICommandText->QueryInterface(IID_ICommandProperties,(void**)& pICommandProperties));
GRS_COM_CHECK(pICommandProperties->SetProperties(1,ps));//注意必须在Execute前设定属性
GRS_COM_RELEASE(pICommandProperties); //没有用了 就释放掉
//============================================================================================================

//============================================================================================================
//5、设置SQL 并执行得到Rowset对象
GRS_COM_CHECK(pICommandText->SetCommandText(DBGUID_DEFAULT,pSQL));
#ifdef GRS_USE_IMULTIPLERESULTS
GRS_COM_CHECK(pICommandText->Execute(NULL,IID_IMultipleResults,NULL,NULL,(IUnknown**)& pIMultipleResults));
//-----------------------------------------------------------------------------------------------------------------------
//例子代码循环的得到每个结果集并处理之
//循环处理每一个结果集,当然这需要你起码知道你执行的多条SQL语句的顺序
//特别要注意使用S_OK直接判断返回值,而不是使用SUCCEEDED或FAILED宏来判断,否则会变成死循环
//while( S_OK == pIMultipleResults->GetResult(NULL,DBRESULTFLAG_DEFAULT,IID_IRowset,cRowsAffected,(IUnknown**)&pIRowset) )
//{
// ......//处理每一个IRowset
// pIRowset->Release();
// pIRowset = NULL;
//}
//-----------------------------------------------------------------------------------------------------------------------
if(S_OK != pIMultipleResults->GetResult(NULL,DBRESULTFLAG_DEFAULT,IID_IRowset,&cRowsAffected,(IUnknown**)&pIRowset) )
{
goto GRS_CLEARUP;
}
#else

//原文中此处为:
//GRS_COM_CHECK(pICommandText->Execute(NULL,IID_IRowsetChange,NULL,NULL,(IUnknown**)&pIRowsetChange));

//2009年11月23日纠正为,请同时注意所有相关修改的地方,这样就可以使用IRowsetChange接口了

GRS_COM_CHECK(pICommandText->Execute(NULL,IID_IRowsetChange,NULL,NULL,(IUnknown**)&pIRowsetChange));
#endif
//============================================================================================================

//============================================================================================================
//取得IRowset接口(注意修改的地方)
GRS_COM_CHECK(pIRowsetChange->QueryInterface(IID_IRowset,(void**)&pIRowset));
//============================================================================================================

//============================================================================================================
//6、得到列信息生成绑定,这里使用了一个非常简单的动态绑定的方法,实际使用中这个地方还要仔细的修改
//通常需要根据Column Info 的 wType字段使用switch语句详细设定一些特殊类型的绑定方法
//有时为了方便,可以将绑定结构中的wType字段直接设置为我们想要的类型,让数据提供者在输出数据时直接进行数据类型转换
//通常我比较喜欢设置为字符串类型,让所有的类型都直接转换成字符串类型,以方便数据的显示
GRS_COM_CHECK(pIRowset->QueryInterface(IID_IColumnsInfo,(void**)&pIColumnsInfo));
GRS_COM_CHECK(pIColumnsInfo->GetColumnInfo(&cColumns,&rgColumnInfo,&pStringBuffer));
//IColumnsInfo接口可以直接释放了,使用时重新检索出来即可
GRS_COM_RELEASE(pIColumnsInfo);
//如果成功了,那么rgColumnInfo中已经包含了一个关于列信息数据结构的数组,
//数组元素的个数即cColumns 也就是最终的列数
//动态分配绑定结构
rgBindings = (DBBINDING*)GRS_ALLOC(cColumns * sizeof(DBBINDING));
for( iCol = 0; iCol < cColumns; iCol++ )
{
rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;
rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
rgBindings[iCol].obStatus = dwOffset;
rgBindings[iCol].obLength = dwOffset + sizeof(DBSTATUS);
rgBindings[iCol].obValue = dwOffset+sizeof(DBSTATUS)+sizeof(ULONG);
rgBindings[iCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[iCol].eParamIO = DBPARAMIO_NOTPARAM;
rgBindings[iCol].bPrecision = rgColumnInfo[iCol].bPrecision;
rgBindings[iCol].bScale = rgColumnInfo[iCol].bScale;
rgBindings[iCol].wType = rgColumnInfo[iCol].wType;
rgBindings[iCol].cbMaxLen = rgColumnInfo[iCol].ulColumnSize;

dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;
dwOffset = GRS_ROUNDUP(dwOffset);
}
//============================================================================================================

//============================================================================================================
//7、创建访问器
GRS_COM_CHECK(pIRowset->QueryInterface(IID_IAccessor,(void**)&pIAccessor));
GRS_COM_CHECK(pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA,cColumns,rgBindings,0,&phAccessor,NULL));
//============================================================================================================
//8、得到并访问数据
//分配cRows行数据的缓冲,然后反复读取cRows行到这里,然后逐行处理之
pData = GRS_ALLOC(dwOffset * cRows);

GRS_COM_CHECK(pIRowset->GetNextRows(DB_NULL_HCHAPTER,0,cRows,&cRowsObtained, &rghRows) );
//注意下面的循环 一定使用S_OK判定GetNextRow的值,否则会形成一个死循环,因为到达行集末尾不是一个错误
do
{//循环读取数据,每次循环默认读取cRows行,实际读取到cRowsObtained行
for( iRow = 0; iRow < cRowsObtained; iRow++ )
{
pCurData = (BYTE*)pData + (dwOffset * iRow);
GRS_COM_CHECK(pIRowset->GetData(rghRows[iRow],phAccessor,pCurData));
//pCurData中已经包含了结果数据,显示或者进行处理
//......
//对行集数据进行修改,然后用SetData提交修改
GRS_COM_CHECK(pIRowsetChange->SetData(rghRows[iRow],phAccessor,pCurData));

//删除一些行 需要测试时还原下面的行
//GRS_COM_CHECK(pIRowsetChange->DeleteRows(NULL,1,&rghRows[iRow],&pdwStatus[0]));

//复制插入一些行的例子
pNewData = (BYTE*)GRS_ALLOC(dwOffset);
CopyMemory(pNewData,pCurData,dwOffset);
GRS_COM_CHECK(pIRowsetChange->InsertRow(NULL,phAccessor,pNewData,&hNewRows));
//插入成功释放新行的行句柄 以及新行的数据缓冲也一同释放
GRS_COM_CHECK(pIRowset->ReleaseRows(1,&hNewRows,NULL,NULL,NULL));
GRS_FREE(pNewData);
}
//注意下面两次释放代表不同的含义
if( cRowsObtained )
{//释放行句柄指向的行
GRS_COM_CHECK(pIRowset->ReleaseRows(cRowsObtained,rghRows,NULL,NULL,NULL));
}
//释放行句柄数组的内存
CoTaskMemFree(rghRows);
rghRows = NULL;
}
while( S_OK == pIRowset->GetNextRows(DB_NULL_HCHAPTER,0,cRows,&cRowsObtained, &rghRows) );

//============================================================================================================
//清理释放部分
GRS_CLEARUP:
//GRS_FREE(pCurData); //这行多余 注释或删除之 2009-11-23修改
GRS_FREE(pData);
GRS_FREE(rgBindings);
if(pIAccessor)
{
pIAccessor->ReleaseAccessor(phAccessor,NULL);
}
GRS_COM_RELEASE(pIAccessor);
CoTaskMemFree(rgColumnInfo);
CoTaskMemFree(pStringBuffer);
GRS_COM_RELEASE(pIColumnsInfo);
GRS_COM_RELEASE(pIRowsetChange);
GRS_COM_RELEASE(pIRowset);
GRS_COM_RELEASE(pIMultipleResults);
GRS_COM_RELEASE(pICommandText);
GRS_COM_RELEASE(pICommandProperties);
GRS_COM_RELEASE(pIDBCreateCommand)
GRS_COM_RELEASE(pIOpenRowSet);
GRS_COM_RELEASE(pIDBCreateSession);
GRS_COM_RELEASE(pIDBInitialize);
GRS_COM_RELEASE(pIDBPromptInitialize);
//============================================================================================================

::CoUninitialize();
return 0;
}

转自: http://gamebabyrocksun.blog.163.com/blog/static/571534632009101321514116/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: