登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Gamebaby Rock Sun的博客

我只知道一件事情,就是我一无所知。

 
 
 

日志

 
 
关于我

曾经拥有的,不要忘记, 已经得到的,更要珍惜, 属于自己的,不要放弃, 已经失去的,留着回忆, 想要得到的,必须努力, 但最重要的,是好好爱惜自己!

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

2009-11-13 14:15:14|  分类: OLEDB编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

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

 

  评论这张
 
阅读(3276)| 评论(20)

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018