当前位置: 首页 > news >正文

VC++/MFC学习笔记(五)

 ADO技术介绍
ADO是Microsoft最新和最强大的数据访问接口OLE DB而设计的,是一个便于使用的应用程序层。OLE DB为任何数据源都提供了高性能的访问,这些数据源包括关系和非关系数据库、电子邮件、文件系统、文本和图形以及自定义业务对象等。ADO在关键的Internet方案中使用最少的网络流量,并且在前端和数据源之间使用最少的层数,所有这些都是为了提供轻量、高性能的接口。同时ADO使用与DAO相似的约定和特性,使得它更易于学习。这里先介绍一下ADO技术的历史和ADO访问数据源的特点,然后介绍访问方法。

1.ADO的历史回顾
ADO共发布了1.0、1.5和2.0三个版本。
第一个版本1.0是RDO的一个功能子集,它的目标是为了帮助开发人员在IIS(Internet Informatioin Server)上建立ASP(Active Server Pages)应用。
第二个版本1.5是随IIS4.0和Internet Explorer4.0一起发布的。它也被包含在MDAC(Microsoft Data Access Components)里。从这个版本开始,ADO成为了功能和运作效率上都高出RDO和DAO一筹的数据库界面。
最新的版本2.0加入了别的数据库客户技术从没有的新技术。ADO2.0实际上是基于MSADO15.DLL这个动态链接库的,这个库文件的名字虽然和ADO1.5的一样,但是它实现了更新的接口。ADO2.0的新技术有:
1)异步操作和事件模型。
2)数据集的持续性。
3)层次化的数据运输。
2.ADO特点概述
用ADO访问数据源的特点可概况如下:
第一,易于使用,可以说这是ADO最重要的特点之一。由于ADO是最高成数据库访问技术,所以相对于ODBC来说,具有面向对象的特点。同时,在ADO对象结构中,对象与对象之间的层次结构不是非常明显,这会给编写数据库程序带来很多便利,比如,在应用程序中如果要使用记录集对象,就不一定要先建立连接、会话对象,如果需要就可以直接构造记录集对象。总之,已经没有必要去关心对象的构造层次和构造顺序了。
第二,可以访问多种数据源。这一点和OLE DB一样的,使应用程序具有很好的通用性和灵活性。
第三,访问数据源效率高。这是由于ADO本身就是基于OLE DB的接口,自然具有OLD DB的特点。
第四,方便地Web应用。ADO可以以ActiveX控件的形式出现,这就大大方便了Web应用程序的编制。
第五,技术编程接口丰富。ADO支持Visual C++、Visual Basic、Visual J++以及VBScript和JavaScript等脚本语言。
3.ADO的对象
ADO包含了连接对象(Connection)、命令对象(Command)、记录集对象(RecordSet)、字段对象(Field)、参数对象(Parameter)、错误对象(Error)、属性对象(Property)和集合与事件等。
1)连接(Connection)
用于表示和数据源的连接,以及处理一些命令和事务。通过它可以从应用程序访问数据源,是交换数据必需的环境。
2)命令(Command)
用于执行某些命令来进行诸如查询、修改数据库结构的操作。
3)记录集(RecordSet)
用于和处理数据源的表格表,它是在表中修改、检索数据的最主要的方法。
4)字段(Field)
用于描述数据集中的列信息。
5)参数(Parameter)
用于对传递给数据源的命令赋参数值。
6)错误(Error)
用于承载说产生错误的详细信息。
7)属性(Property)
通过属性,每个ADO对象借此来让用户描述和控制自身的行为。
8)集合(Set)
集合是一种可方便地包含其他特殊类型对象的对象类型。ADO提供了4种类型的集合。
a、Connention对象具有Errors集合
b、Command对象具有Parameters集合
c、Recordset对象具有Fields集合
d、Connection、Command、Recordset、Field对象都具有Properties集合
9)事件(Event)
事件模型是异步操作的基础,这是ADO2.0引进的新特性。
4.在Visual C++中使用ADO
为在Visual C++中使用ADO,需要在头文件中加入下面几行代码:
#import "C:/Program Files/Common Files/system/ado/msado15.dll" no_namespace rename("EOF","adoEOF")
#include <adoid.h>
#include <adoint.h>
#include "icrsint.h"
第一行的#import语句告诉编译器把此指令中的动态连接库文件引入到程序中,并从库中抽取其中的对象的类的信息,并产生出两个头文件包含在工程中。其中的no_namespace用来对动态链接库的名称域进行隔离。最后的rename,把ADO中的EOF重新命名,避免和其他地方定义的EOF产生冲突。
第二、第三行引入的头文件定义了ADO2.0类和接口标识。
第四行引入了ADO2.0数据绑定扩展。
5.到数据源的连接
Connection对象用来实现和数据源的连接。创建一个Connection对象非常简单,只需声明一个_ConnectionPtr型的指针,调用它的CreateInstance方法即可(_ConnectionPtr是所谓的智能指针,包装了一个Connection接口指针,具体技术细节可以参考有关的COM书籍)。
_ConnectionPtr pConn=NULL;
try
{
    //创建Connection对象实例
    pConn.CreateInstance(__uuidof(Connection));
    _bstr_t conn="DSN=SomeDS;UID=sa;PWD=123";
    //打开连接
    pConn->Open(conn,"","",-1);
    //执行一条SQL命令
    _bstr_t sql="DELETE * FROM some_table WHERE id='111'";
    pConn->Close();
}
catch(_com_error &e)
{
    AfxMessageBox(e.ErrorMessage());
}
上边的代码演示了用Connection对象建立连接,执行一条SQL语句后关闭连接的全过程。
Open函数的原型如下:
HRESULT Open(_bstr_t ConnectionString,
    _bstr_t UID,
    _bstr_T PSWD,
    long option);
各个参数的意义为:
a、ConnectionString:包含连接信息的字符串
b、UID:访问数据库的用户名。
c、PSWD:访问数据库的口令。
d、option:可选参数。
在用Open函数建立连接之前,还可以先设置一些Connection对象的属性。其中用的有ConnectionTimeOut属性和Mode属性。前者用来设置建立连接时的等待时间,后者用来设置数据库的打开模式。打开模式可以是下列值的组合。
a、adModeRead:读模式。
b、adModeWrite:写模式。
c、adModeReadWrite:读写模式。
d、adModeShareDenyNone:防止用户用任何权限打开连接。
e、adModeShareDenyRead:防止用户以读权限打开连接。
f、adModeShareDenyWrite:防止用户以写权限打开连接。
g、adModeShareExclusive:防止其他用户打开连接。
h、adModeUnknown:未知模式。

6.使用记录集
记录集对象是ADO中最常用的对象,以后下面将详细地介绍它的功能。
首先是Recordset对象的创建函数Open,它的原型如下:
HRESULT Open(const_variant_t &source,
    const_variant & connection,
    enum CursorTypeenum cursorType,
    enum LockTypeEnum lockType,
    long options);
对其参数解释如下:
source参数是一个变体类型,它可以是一个Command对象、一个SQL语句、一个表名或一个存储过程,甚至可以是一个URL、一个文件名、一个流对象。
connection也是一个变体类型,它可以是一个Connection对象,也可以是一个指明连接目标的字符串。
cursorType指明了数据集游标的类型。它可以是下列几个值:adOpenDynamic,adOpenForwardOnly,adOpenKeyset,adOpenStatic,adOpenUnspecified。
lockType参数可以是下列几个值之一:adLockBatchOptimistic,adLockOptimistic,adLockPessimistic,adLockReadOnly,adLockUnspecified。
options参数指明了第一个参数source的类型,其值可以是adCmdUnspecified,adCmdText,adCmdTable,adCmdStoredProc,adCmdUnkown,adCmdFile,adCmdTableDirect。
Recordset最常用的3个方法涉及到数据库的增加、更改和删除操作。下面的代码解释了他们的用法。
删除函数Delete的用法。
HRESULT hr;
_bstr_t bstrQuery("SELECT * FROM Products WHERE PartNumber='8TRACK-003'");
_variant_t vNull;
vNull.vt=VT_ERROR;
vNull.scode=DISP_E_PARAMNOTFOUND;
try
{
    hr=pRecordSet.CreateInstance(__uuidof(Recordset));
    if(SUCCEEDED(hr))
    {
        //这里假设Connection对象pConnection已经初始化。
        pRecordSet->PutRefActiveConnectin(pConnection);
        hr=pRecordSet->Open(_variant_t(bstrQuery),vNUll,
            adOpenForwardOnly,adLockOptimistic,adCmdText);
        if(!pRecordSet->GetadoEOF())
        {
            //删除当前游标所指的记录
            pRecordSet->Delete(adAffectCurrent);
            pRecordSet->Close();
        }
    }
}
catch(_com_error &e)
{
    //异常处理部分,省略
}
下面的代码演示了修改函数Update的用法。
_RecordsetPtr pRcordSet;
HRESULT hr;
_bstr_t bstrQuery("SELELCT * FROM Products WHERE PartNumber='8TRACK-003'");
_variant_t vNull;
vNull.vt=VT_ERROR;
vNull.scode=DISP_E_PARAMNOTFOUND;
try
{
    hr=pRecordSet.CreateInstance(__uuidof(Recordset));
    if(SUCCEEDED(hr))
    {
        //这里假设Connection对象pConnection已经初始化。
        pRecordSet->PutRefActiveConnectin(pConnection);
        hr=pRecordSet->Open(_variant_t(bstrQuery),vNUll,
            adOpenForwardOnly,adLockOptimistic,adCmdText);
        if(!pRecordSet->GetadoEOF())
        {
            pRecordSet->PutCollect(L"ProductName",L"Bell Botooms and Bass Guitar");
            //执行更新操作
            pRecordSet->Update(vNull,vNull);
            pRecordSet->Close();
        }
    }
}
catch(_com_error &e)
{
    //异常处理部分,省略
}
下面的代码演示了AddNew函数的用法。
HRESULT hr;
_bstr_t bstrQuery("SELELCT * FROM Products WHERE PartNumber= IS NULL");
_variant_t vNull;
vNull.vt=VT_ERROR;
vNull.scode=DISP_E_PARAMNOTFOUND;
try
{
    hr=pRecordSet.CreateInstance(__uuidof(Recordset));
    if(SUCCEEDED(hr))
    {
        //这里假设Connection对象pConnection已经初始化。
        pRecordSet->PutRefActiveConnectin(pDoc->pConnection);
        hr=pRecordSet->Open(_variant_t(bstrQuery),vNUll,
            adOpenForwardOnly,adLockOptimistic,adCmdText);

        if(SUCCEEDED(h))
        {
            //创建一个记录字段信息的数组
            COleSafeArray vaFieldlist;
            vaFieldlist.CreateOneDim(VT_VARIANT,3);
            long lArrayIndex[1];
            lArrayIndex[0]=0;
            vaFieldlist.PutElement(lArrayIndex,&(_variatn_t("PartNumber")));
            lArrayIndex[0]=1;
            vaFieldlist.PutElement(lArrayIndex,&(_variatn_t("ProductName")));
            lArrayIndex[0]=2;
            vaFieldlist.PutElement(lArrayIndex,&(_variatn_t("Price")));
            //创建一个保存字段值的数组
            COleSafeArry vaValuelist;
            vaValuelist.CreateOneDim(VT_VARIANT,3);
            lArrayIndex[0]=0;
            vaValuelist.PutElement(lArrayIndex,&(_variatn_t("8TRACK-003")));
            lArrayIndex[0]=1;
            vaValuelist.PutElement(lArrayIndex,&(_variatn_t("Bell Botoom Hits")));
            lArrayIndex[0]=2;
            vaValuelist.PutElement(lArrayIndex,&(_variatn_t((float)19.95)));
            //执行添加操作
            pRecordSet->AddNew(vaFieldlist,vaValuelist);
            pRecordSet->Close();
        }
    }
}
catch(_com_error &e)
{
    //异常处理部分,省略
}
从上面的示例代码看出,AddNew和Update方法的参数都是两个变体类型的数组。这种参数在编程的时候比较麻烦,需要编写从VARIANT类型到C++类型的转换代码。而且,使用VARIANT类型检索C/C++数据的过程也有损性能。ADO For VC++ Extensions引入了数据绑定技术,解决了上述的问题。它能把数据直接绑定到C++类型上;通过提供简化接口使用的宏,使它成为一个灵活、易用、高效的工具。
为了绑定一个数据库里的表格到一个C++对象,要求这个C++对象必须继承CADORecordBinding类。通过宏,使我们能很容易地构造出一个这样的对象。例子代码如下:
class CCPRs : public CADORecordBinding
{
    BEGIN_ADO_BINDING(CCPRs)
        ADO_VARIABLE_LENGTH_ENTRY2(1,adVarChar,m_sz_no,
            sizeof(m_sz_no),m_sts_no,TRUE)
        ADO_VARIABLE_LENGTH_ENTRY2(2,adVarChar,m_sz_name,
            sizeof(m_sz_name),m_sts_name,TRUE)
        ADO_VARIABLE_LENGTH_ENTRY2(3,adVarChar,m_sz_kind,
            sizeof(m_sz_kind),m_sts_kind,TRUE)
        ADO_VARIABLE_LENGTH_ENTRY2(4,adSingle,m_f_price,
            sizeof(m_f_price),m_sts_price,TRUE)
        ADO_VARIABLE_LENGTH_ENTRY2(5,adVarChar,m_sz_detail,
            sizeof(m_sz_detail),m_sts_detail,TRUE)
    END_ADO_BINGDING()
public:
    ULONG m_sts_no;
    ULONG m_sts_name;
    ULONG m_sts_kind;
    ULONG m_sts_price;
    ULONG m_sts_detail;

    CHAR m_sz_no[10];
    CHAR m_sz_name[20];
    CHAR m_sz_kind[202];
    float m_f_price;
    CHAR m_sz_detail[50];
};
由于宏的帮助,这种类的定义是相当简单而清晰的。数据绑定写在BEGIN_ADO_BINGDING和END_ADO_BINDING之间。
ADO_VARIABLE_LENGTH_ENTRY2宏的第一个参数指明了字段在表中的顺序,该顺序不能填错,否则运行时会出现错误。这个宏的第二个参数指明了说绑定的字段的类型。有趣的是,adVarChar可以应付大部分类型,哪怕是日期或整数。浮点数这样的类型。当然,为了提高效率和方便编程,还是设置成正确的类型为好。第四个参数用来刻画字段的状态--对每一个字段,还要定义与之对应的参整数变量来记录此字段的状态。最后一个参数指明了此字段是否可写,如果是FALSE,说明是只读的字段。
下面的例子演示了使用这种方法读取表格,并把其中的price字段加倍保存回数据库中。其中数据绑定的部分见上例。
_RecordsetPtr pRst=NULL;
IADORecordBinding *picRs=NULL;
CCPRs rs;

try
{
    _bstr_t strSQL="SELECT * FROM CP";

    TESTHR(pRst.CreateInstrance(__uuidof(Recordset)));
    pRst=m_DBCnt->Execute(strSQL,NULL,adCmdText);

    TESTHR(pRst->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));
    TESTHR(picRs->BindToRecordset(&rs));

    while(!pRst->adoEOF)
    {
        //打印出读取的记录
        printf("%s/n",rs.m_sz_no);
        printf("%s/n",rs.m_sz_name);
        printf("%s/n",rs.m_sz_kind);
        printf("%f/n",rs.m_sz_price);
        printf("%s/n",rs.m_sz_detail);
        //价格翻倍后保存
        rs.m_f_price*=2;
        picRs->Update(&rs);
        //游标移动到下一个记录
        pRst->MoveNext();
    }
    picRs->Release();
    pRst->Close();
}
catch(_com_err &e)
{
    AfxMessageBox(e.ErrorMessage());
    return;
}

7.使用命令
在ADO中,可以用Connection对象的Execute()方法执行命令,也可以用Command对象。
第一条路径相对简单。Execute函数的原型如下:
_RecordsetPtr Execute(_bstr_t cmd,
    VARIANT* rcds,
    long optn);
第一个参数cmd用一个字符串作为参数,第三个参数说明了cmd的类型:
adCmdText:cmd是一个SQL命令。
adCmdTable:cmd指明了一个表名。
adCmdStoredProc:cmd指明了一个存储过程。
Execute返回的结果是一个Recordset指针。下面是一段Execute的简单例子:
_bstr_t sql="SELECT id FROM tb1 WHERE id='Tom' AND pswd='111'";
_RecordsetPtr pRst;
pRst=pDBCnt->Execute(sql,NULL,adCmdText);
if(!pRst->adoEOF)
        Message("登录成功! ");
第二条路径使用的Command对象相对复杂,需要设置很多属性和参数,因而功能也更为强大。这里通过一段示例来解释它的用法。
HRESULT hr=S_OK;
//定义字符串常量
_bstr_t strSQLChange("UPDATE Titles SET Type= "
    "'self_help' WHERE Type='psychology'");
_bstr_t strSQLChange("UPDATE Titles SET Type= "
    "'psychology' WHERE Type='self_help'");
_bstr_t strCnn("Provider=sqloledb;Data Source=MyServer; "
    "Initial Catalog=pubs;User Id=sa;Password=;");

//定义ADO对象
_ConnectioinPtr pConnection=NULL;
_CommandPtr pCmdChange=NULL;
_RecordsetPtr pRstTitle=NULL;
try
{
    //建立连接
    TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
    pConnection->Open(strCnn,"","",adConnectionUnspecified);

    //创建命令对象
    TESTHR(pCmdChange.CreateInstance(__uuidof(Command)));
    pCmdChange->ActiveConnection=pConnection;
    pCmdChange->CommandText=strSQLChagne;

    //打开titles表
    TESTHR(pRstTitles.CreateInstance(__uuidof(Recordset)));
    pRstTitles->Open("Title",_variant_t((IDispatch*)pConnection,
        true),adOpenStatic,adLockOptimistic,adCmdTable);

    //打印出原有的数据
    printf("/n/nData in Titles table before executing the query:/n");

    //这里省略这个函数的实现
    PrintOutput(pRstTitles);

    //清除原有错误记录
    pConnection->Errors->Clear();

    //执行命令
    pCmdChange->Execute(NULL,NULL,adCmdText);
    pRstTitles->Requery(adCmdUnknown);

    //输出新数据
    printf("/n/n/tData in Titles table after executing the query:/n");
    PrintOutput(pRstTitles);

    //恢复记录数据
    pConnection->Execute(strSQLRestore,NULL,adExecuteNoRecords);
    pRstTitles->Requery(adCmdUnknown);
    printf("/n/n/tData after exec. query to restore original info:/n");
    PrintOutput(pRstTitles);

    //结束程序,释放对象
    pRstTitles->Close();
    pConnection->Close();
}
catch(_com_error &e)
{
    //异常处理部分,省略
}

8.使用事务
在数据库中,事务的概念可以把多个操作作为单一的最基本的活动来进行。例如,在一个银行数据库中,需要从一个账户取钱汇入另一个账户中。这里涉及了至少两个操作:减少A的账户,增加B的账户。如果第一个操作顺利完成而第二个操作不幸失败的化,数据库数据的完整性将受到破坏--操作前后银行里的钱的总数会变少,说这里就要用到ADO中事务处理的概念。在所有操作开始之前调用Connection对象的Begintrans方法来开始一个事务:
pDBCnt->BeginTrans();
在所有操作成功之后,调用Connection对象的CommitTrans方法提交此事务,这时数据库的内容菜作实质新的改变:
pDBCnt->CommitTrans();
如果操作中途出现异常,则在异常处理中使用RollBackTrans取消这次事务,数据库将回到BeginTrans之前的状态。
catch(_com_error &e)
{
    AfxMessageBox(e.ErrorMessage());
    pDBCnt->RollBackTrans();
}
9.使用ADO事件
ADO从2.0版本开始加入了对事件的支持。ADO事件是由某些操作在开始之前或结束之后发出的通知;所谓通知,实质上是对预定义的时间处理回调函数的调用。
ADO事件分两类:ConnectionEvent和RecordsetEvent。前者出现在连接(Connection)打开、切断,事务(Transaction)开始、提交或命令(Command)被执行等与Connection对象有关的操作处;后者则出现在与记录集(Recordset)对象有关的操作处,如在记录集中定位,修改记录、删除记录集行等。
若按时间性质来分,ADO事件又可以分为Will事件、Complete事件和其他事件3类。顾名思义,Will事件发生在某个操作之前,Complete 事件发生在某个操作完成之后。
表1和表2是一些典型的ADO事件
表1:                                    ConnectionEvent
---------------------------------------------------------------------------------------------
        连接事件(ConnectionEvent)                |                说明
---------------------------------------------------------------------------------------------
BeginTransComplete                                |事务管理有关的事件
CommitTransComplete                                |
RollbackTransComplete                            |
---------------------------------------------------------------------------------------------
WillConnect                                        |连接相关的事件
ConnectionComplete                                |
Disconnect                                        |
---------------------------------------------------------------------------------------------
WillExecute                                        |命令相关的事件
ExecuteComplete                                    |
---------------------------------------------------------------------------------------------
InfoMessage                                        |关于当前操作有附加信息的通知
---------------------------------------------------------------------------------------------


表2:                                    RecordsetEvent
---------------------------------------------------------------------------------------------
        记录集事件(RecordsetEvent)            |                说明
---------------------------------------------------------------------------------------------
FetchProgress                                    |数据检索相关的事件
FetchComplete                                    |
---------------------------------------------------------------------------------------------
WillChangeField                                    |字段更改相关的事件
FieldChangeComplete                                |
---------------------------------------------------------------------------------------------
WillMove                                        |定位操作相关的事件
MoveComplete                                    |
EndOfRecordset                                    |
---------------------------------------------------------------------------------------------
WillChangeRecord                                |行更改相关的事件
RecordChangeComplete                            |
---------------------------------------------------------------------------------------------
WillChangeRecordset                                |数据集更改相关的事件
RecordsetChangeComplete                            |
---------------------------------------------------------------------------------------------


下面结合一个历程的片段来说明如何在应用程序中使用ADO事件。
//从ConnectionEventsVt派生出一个连接事件类的对象
class CConnEvent:public ConnectionEventVt
{
private:
    //接口引用计数
    ULONG m_cRef;
public:
    CConnEvent()    {m_cRef=0;};
    ~CConnEvent() {};
    //实现IUnknown的三个标准方法
    STDMETHODIMP QueryInterface(REFIID riid,void**ppv);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    //处理InfoMessage事件的回调函数
    STDMETHODIMP raw_InfoMessage(
        struct Error*pError;
        EventStatusEnum * adStatus,
        struct_Connection* pConnection);
    //处理BeginTransComplete事件的回调函数
    STDMETHODIMP raw_BeginTransComplete(
        LONG TransactionLevel,
        struct Error*pError;
        EventStatusEnum*adStatus,
        struct_Connection*pConnection);
    //处理CommitTransComplete事件的回调函数
    STDMETHODIMP raw_CommitTransComplete(
        struct Error*pError,
        EventStatusEnum *adStatus,
        struct_Connection *pConnection);
    //以下还有若干事件函数,在此省略
    //......
};
上面定义的这个类继承了ConnectionEventVt接口,实现了Connection事件类所有事件的回调函数。读者可以仿照它定义自己的RecordsetEvent事件类。这两个类是所谓的COM组件类,所以它们必须实现IUnknown的三个标准方法:QueryInterface、AddRef和Release。读者可以使用合适的工具(如ATL)简化这部分工作,这里只给出和这一节内容有关系最大的几个回调函数的代码。
//CConnEvent类的实现
STDMETHODIMP CConnEvent::raw_InfoMessage(
    struct Error*pError,
    EventStatusEnum*adStatus,
    struct_Connection *pConnection)
{
    *adStatus=adStatuUnwantedEvent;
    return S_OK;
};
STDMETHODIMP raw_BeginTransComplete(
    LONG TransactionLevel,
    struct Error*pError;
    EventStatusEnum*adStatus,
    struct_Connection*pConnection)
{
    *adStatus=adStatuUnwantedEvent;
    return S_OK;
};
STDMETHODIMP raw_CommitTransComplete(
    struct Error*pError,
    EventStatusEnum *adStatus,
    struct_Connection *pConnection)
{
    *adStatus=adStatuUnwantedEvent;
    return S_OK;
};
由于是演示,这几个函数没有做任何有意义的事情。需要注意每个函数的第一句,在这里把adStatus的值设为adStatusUnwantedEnvet,这样这个函数在第一次被调用之后将不会被再次调用。如果用户需要这个事件处理函数的工作,就必须去掉第一句。
下面的代码演示了怎样在程序中使用上面定义的ADO事件类。
int main(int argc,char*argv[])
{
    HRESULT hr;
    DWORD dwConnEvt;
    DWORD dwRstEvt;
    IConnectionPointContainer *pCRC =NULL;
    IConnectionPoint*pCP=NULL;
    lUnkown*pUnk=NULL;
    CRstEvent*pRstEvent=NULL;
    CConnEvent*pConnEvent=NULL;
    int rc=0;
    _RecordsetPtr pRst;
    _ConnectionPtr pConn;

    ::CoInitialize(NULL);

    hr=pConn.CreateInstance(__uuidof(Connection));
    if(FAILED)(hr)) return rc;

    hr=pRst.CreateInstance(__uuidof(Recordset));
    if(FAILED(hr)) return rc;

    //开始使用Connection事件
    hr=pConn->QueryInterface(__uuidof(IConnectionPointContainer),
        (void**)&pCPC);
    if (FAILED)(hr)) return rc;
    hr=pCRC->FindConnectionPoint(__uuidof(ConnectionEvents),&pCP);
    pCPC->Release();
    if (FAILED(hr)) return rc;

    //开始使用Recordset事件
    hr=pRst->QueryInterface(__uuidof(IConnectionPointContainter),
        (void**)&pCPC);
    if(FAILED(hr)) return rc;
    hr=pCRC->FindConnetionPoint(__uuidof(RecordsetEvents),&pCP);
    pCPC->Release();
    if (FAILED(hr)) return rc;

    pRstEvent=new CRstEvent();
    hr=pRstEvent->QueryInterface(__uuidof(IUnknown),(void**)&pUnk);
    if (FAILED(hr)) return rc;
    hr=pCP->Advise(pUnk,&dwRstEvt);
    pCP->Release();
    if (FAILED(hr)) return rc;

    //进行一些数据库操作
    pConn->Open("DSN=Pubs;","sa","",adConnectionUnspecified);
    pRst->Open("SELECT * FROM authors",(IDispatch*)pConn,
        adOpenStatic,adLockReadOnly,adCmdText);
    pRst->MoveFirst();
    while(pRst->EndOfFile==FALSE)
    {
        wprintf(L"Name='%s'/n",(wchar_t*)
            ((_bstr_t)pRst->Fields->GetItem("au_lname")->Value));
        pRst->MoveNext();
    }

    pRst->Close();
    pConn->Close();

    //停止使用Connection事件类
    hr=pConn->QueryInterface(__uuidof(IConnectionPointContainter),
        (void**)&pCPC);
    if(FAILED(hr)) return rc;
    hr=pCPC->FindConnectionPoint(__uuidof(ConnectionEvents),&pCP);
    pCPC->Release();
    if (FAILED(hr)) return rc;

    //停止使用Recordset事件类
    hr=pConn->QueryInterface(__uuidof(IConnectionPointContainter),
        (void**)&pCPC);
    if(FAILED(hr)) return rc;
    hr=pCPC->FindConnectionPoint(__uuidof(RecordsetEvents),&pCP);
    pCPC->Release();
    if (FAILED(hr)) return rc;
    hr=pCP->Unabvise(dwRstEvt);

    pCP->Release();
    if(FAILED(hr)) return rc;

    CoUninitialize();
    return 1;
}

注: 本文内容基本来自网络,自己只做少量修改.

转载于:https://www.cnblogs.com/ainima/archive/2008/07/21/6332069.html

相关文章:

  • 自动提示的文本框
  • [转]屏蔽网页右键
  • linux 上远程控制
  • 快捷键控件
  • pthread_cond_wait(条件变量)
  • 装了vs2008后再装sql server 2005碰到的问题
  • ArcGIS.Server.9.2.DotNet自带例子分析(七、一)
  • 简述GPS导航设备的定位原理
  • ASP.Net2.0 GridView 多列排序,显示排序图标,分页(转)
  • C#正则表达式类Match和Group类的理解
  • 声音音质评价专业述语
  • 使用Document_Handler_Example修改的注意事项
  • iptables 范例
  • 将asp.net站点转化为sharepoint站点
  • Silverlight是什么
  • 【391天】每日项目总结系列128(2018.03.03)
  • Angular数据绑定机制
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • extract-text-webpack-plugin用法
  • mysql_config not found
  • Python实现BT种子转化为磁力链接【实战】
  • Solarized Scheme
  • uni-app项目数字滚动
  • 力扣(LeetCode)56
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 使用SAX解析XML
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 小李飞刀:SQL题目刷起来!
  • 用Python写一份独特的元宵节祝福
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (10)STL算法之搜索(二) 二分查找
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (C++17) std算法之执行策略 execution
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (黑马C++)L06 重载与继承
  • (十六)一篇文章学会Java的常用API
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • (转)3D模板阴影原理
  • (状压dp)uva 10817 Headmaster's Headache
  • .NET 使用配置文件
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • @Autowired 与@Resource的区别
  • [20170728]oracle保留字.txt
  • [AIGC] 如何建立和优化你的工作流?
  • [APUE]进程关系(下)
  • [C#7] 1.Tuples(元组)
  • [C++]类和对象(中)
  • [C语言]——分支和循环(4)
  • [elastic 8.x]java客户端连接elasticsearch与操作索引与文档
  • [Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包
  • [IE技巧] 如何让IE 启动的时候不加载任何插件