OLEDB 数据变更通知

2018-05-04  本文已影响15人  一叶障目

title: OLEDB 数据变更通知
tags: [OLEDB, 数据库编程, VC++, 数据库, 数据库数据变更通知]
date: 2018-05-04 20:52:26
categories: windows 数据库编程
keywords: OLEDB, 数据库编程, VC++, 数据库, 数据库数据变更通知


除了之前介绍的接口,OLEDB还定义了其他一些支持回调的接口,可以异步操作OLEDB对象或者得到一些重要的事件通知,从而使应用程序有机会进行一些必要的处理。其中较有用的就是结果集对象的变更通知接口。通过这个接口可以及时得到结果集被增删改数据变化的情况,并有机会进行必要的数据合法性审核。

数据变更通知的接口是IRowsetNotify,数据源对象要求的异步通知事件接口是IDBAsynchNotify。

标准COM的回调方式

为了更好的理解OLEDB的回调,先回忆一下标准COM的回调方式。
COM组件除了提供函数供应用程序主动调用这种方式外,还提供了回调这种方式,这种方式由用户实现相应的接口,然后由COM组件来调用,这样我们就可以知道COM组件的运行状态,同时能针对一些情况进行处理,比如处理内存耗尽,获取用户输入等等。
要支持事件回调的COM组件必须提供IConnectionPointContainer接口,调用者调用IConnectionPointContainer接口的FindConnectPoint接口,通过回调事件的IID找到特定的事件挂载点,然后调用接口的Advise方法将挂载点与对应的回调函数关联起来(一个事件可以对应多个回调函数)这样当事件发生时就可以调用对应的回调函数。
这个机制有点类似于QT中的信号和槽函数机制,QT中的事件是实现定义好的,可以直接使用而这里是需要通过事件ID找到具体事件,拥有事件后,与QT步骤类似,都是需要将事件与对应的回调函数绑定。

IRowsetNotify接口

对于OLEDB结果集来说,最重要的事件接口是IRowsetNotify,该接口提供三个重要的通知函数:

通过这些事件函数具体实现时设置不同的返回值可以控制结果集对象对修改做出的响应,比如:返回S_OK表示接受这个修改,返回S_FALSE明确拒绝接受这个修改。这样就给了一个最终反悔的机制。
这些函数有两个重要的参数:

DBREASON 参数的相关值

DBEVENTPHASE

这个参数表示当前执行的状态,一般操作数据结果集有5个状态,分别对应这样的5个值:

下面是数据状态迁移图,这个图很形象的展示了在某个操作执行过程中的各种状态变化


状态迁移图.png

结果集对象事件通知接口的使用方法

  1. 定义一个派生自IRowsetNotify接口的类,并实现其接口中的所有方法
  2. 设置结果集对象属性集DBPROPSET_ROWSET中的DBPROP_IConnectionPointContainer属性为VARIANT_TRUE
  3. 获得结果集对象
  4. 调用IRowset::QueryInterface方法得到IConnectionPointContainer接口指针
  5. 调用IConnectionPointContainer::FindConnectionPoint方法得到IRowsetNotify接口对应的IConnectionPoint接口指针
  6. 实例化一个第一步中创建的类
  7. 调用IConnectionPoint::Advise并传递该对象指针
  8. 对结果集对象进行操作,此时如果事件条件成立,结果集对象会调用该对象的相应方法通知调用者触发了什么事件

详细的内容可以参考MSDN IRowsetNotify

例子

最后来看使用的具体例子

class CCOMRowsetNotify:
    public IRowsetNotify
{
public:
    CCOMRowsetNotify(void);
    virtual ~CCOMRowsetNotify(void);

protected:
    virtual HRESULT FindConnectionPointContainer(IUnknown *pIUnknown, REFIID rrid, IConnectionPoint* &pIcp);
public:
    virtual HRESULT Addvise(IUnknown *pIUnknown, REFIID rrid);
    virtual HRESULT UnAddvise(IUnknown *pIUnknown, REFIID rrid);

public:
    virtual STDMETHODIMP_(ULONG) AddRef(void);
    virtual STDMETHODIMP_(ULONG) Release(void);
    virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);

    virtual STDMETHODIMP OnFieldChange (IRowset *pRowset, HROW hRow, DBORDINAL cColumns, DBORDINAL rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase,BOOL fCantDeny);
    virtual STDMETHODIMP OnRowChange (IRowset *pRowset, DBCOUNTITEM cRows,const HROW rghRows[], DBREASON eReason, DBEVENTPHASE   ePhase, BOOL fCantDeny);
    virtual STDMETHODIMP OnRowsetChange (IRowset *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);

protected:
    ULONG m_uRef;
    DWORD m_dwCookie;
};

使用时首先定义一个派生自IRowsetNotify的类,并实现所有的接口方法

if (!OpenTable(pIOpenRowset, pIRowsetChange))
{
        COM_PRINTF(_T("打开表失败\n"));
        goto __CLEAN_UP;
}

RowsetNotify.Addvise(pIRowsetChange, IID_IRowsetNotify);

HRESULT CCOMRowsetNotify::FindConnectionPointContainer(IUnknown *pIUnknown, REFIID rrid, IConnectionPoint* &pIcp)
{
    IConnectionPointContainer* pICpc = NULL;
    HRESULT hr = pIUnknown->QueryInterface(IID_IConnectionPointContainer,(void**)&pICpc);
    if(FAILED(hr))
    {
        COM_PRINTF(_T("通过IRowset接口获取IConnectionPointContainer接口失败,错误码:0x%08X\n"),hr);
        return hr;
    }
    hr = pICpc->FindConnectionPoint(rrid,&pIcp);
    if(FAILED(hr))
    {
        COM_PRINTF(_T("获取IConnectionPoint接口失败,错误码:0x%08X\n"),hr);
        COM_SAFE_RELEASE(pIcp);
        return hr;
    }

    return hr;
}

HRESULT CCOMRowsetNotify::Addvise(IUnknown *pIUnknown, REFIID rrid)
{
    IConnectionPoint *pIcp = NULL;
    HRESULT hRes = FindConnectionPointContainer(pIUnknown, rrid, pIcp);
    if (S_OK != hRes)
    {
        return hRes;
    }

    hRes = pIcp->Advise(dynamic_cast<IRowsetNotify*>(this), &m_dwCookie);
    COM_SAFE_RELEASE(pIcp);
    return hRes;
}

上述代码先打开数据结果集,然后调用类对象的Addvise方法传入IID_IRowsetNotify接口指针,在方法Addvise中做的主要操作是首先使用传入的接口指针查找到接口IConnectionPointContainer,然后利用IConnectionPointContainer接口的FindConnectionPoint方法找到对应的挂载点,最后调用IConnectionPointContainer的Advise方法将对应的类对象挂载到挂载点上,这样在后面操作结果集时就会调用对应的On函数,完成对应事件的处理

最后放上完整代码的链接
点击这里获取完整代码
<hr />

上一篇 下一篇

猜你喜欢

热点阅读