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

       为了方便大家总结和学习前面的章节内容,此处给出一个完整的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;
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GamebabyRockSun_QQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值