当前所在位置:珠峰网资料 >> 计算机 >> 计算机等级考试 >> 正文
计算机二级辅导:VisualC++ADO数据库编程入门(3)
发布时间:2009/12/6 10:13:08 来源:城市学习网 编辑:admin

  1、响应ADO的通知事件
  通知事件就是当某个特定事件发生时,由Provider通知客户程序,换句话说,就是由Provider调用客户程序中的一个特定的方法(即事件的处理函数)。所以为了响应一个事件,最关键的就是要实现事件的处理函数。
  (1). 从ConnectionEventsVt接口派生出一个类
  为了响应_Connection的通知事件,应该从ConnectionEventsVt接口派生出一个类:
  class CConnEvent : public ConnectionEventsVt
  {
  private:
  ULONG m_cRef;
  public:
  CConnEvent() { m_cRef = 0; };
  ~CConnEvent() {};
  STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
  STDMETHODIMP_(ULONG) AddRef(void);
  STDMETHODIMP_(ULONG) Release(void);
  STDMETHODIMP raw_InfoMessage(
  struct Error *pError, EventStatusEnum *adStatus,
  struct _Connection *pConnection);
  STDMETHODIMP raw_BeginTransComplete(
  LONG TransactionLevel, struct Error *pError, EventStatusEnum *adStatus,
  struct _Connection *pConnection);
  ......
  };
  (2). 实现每一个事件的处理函数(凡是带raw_前缀的方法都把它实现了):
  STDMETHODIMP CConnEvent::raw_InfoMessage(
  struct Error *pError, EventStatusEnum *adStatus,
  struct _Connection *pConnection)
  {
  *adStatus = adStatusUnwantedEvent;
  return S_OK;
  };
  有些方法虽然你并不需要,但也必须实现它,只需简单地返回一个S_OK即可。但如果要避免经常被调用,还应在其中将adStatus参数设置为adStatusUnwantedEvent,则在本次调用后,以后就不会被调用了。
  另外还必须实现QueryInterface, AddRef, 和Release三个方法:
  STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)
  {
  *ppv = NULL;
  if (riid == __uuidof(IUnknown)││
  riid == __uuidof(ConnectionEventsVt)) *ppv = this;
  if (*ppv == NULL)
  return ResultFromScode(E_NOINTERFACE);
  AddRef();
  return NOERROR;
  }
  STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
  STDMETHODIMP_(ULONG) CConnEvent::Release()
  {
  if (0 != --m_cRef) return m_cRef;
  delete this;
  return 0;
  }

  (3). 开始响应通知事件
  // Start using the Connection events
  IConnectionPointContainer *pCPC = NULL;
  IConnectionPoint *pCP = NULL;
  hr = pConn.CreateInstance(__uuidof(Connection));
  if (FAILED(hr)) return;
  hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
  (void **)&pCPC);
  if (FAILED(hr)) return;
  hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
  pCPC->Release();
  if (FAILED(hr)) return;
  pConnEvent = new CConnEvent();
  hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
  if (FAILED(hr)) return rc;
  hr = pCP->Advise(pUnk, &dwConnEvt);
  pCP->Release();
  if (FAILED(hr)) return;
  pConn->Open("dsn=Pubs;", "sa", "", adConnectUnspecified);
  也就是说在连接(Open)之前就做这些事。
  (4). 停止响应通知事件
  pConn->Close();
  // Stop using the Connection events
  hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
  (void **) &pCPC);
  if (FAILED(hr)) return;
  hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
  pCPC->Release();
  if (FAILED(hr)) return rc;
  hr = pCP->Unadvise( dwConnEvt );
  pCP->Release();
  if (FAILED(hr)) return;
  在连接关闭之后做这件事。
  2、邦定数据
  定义一个绑定类,将其成员变量绑定到一个指定的记录集,以方便于访问记录集的字段值。
  (1). 从CADORecordBinding派生出一个类:
  class CCustomRs : public CADORecordBinding
  {
  BEGIN_ADO_BINDING(CCustomRs)
  ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname, sizeof(m_szau_fname), lau_fnameStatus, false) ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname, sizeof(m_szau_lname), lau_lnameStatus, false) ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone, sizeof(m_szphone), lphoneStatus, true)
  END_ADO_BINDING()
  public:
  CHAR m_szau_fname[22];
  ULONG lau_fnameStatus;
  CHAR m_szau_lname[42];
  ULONG lau_lnameStatus;
  CHAR m_szphone[14];
  ULONG lphoneStatus;
  };

  其中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量,一个存放字段的值,另一个存放字段的状态。字段用从1开始的序号表示,如1,2,3等等。
  特别要注意的是:如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段 au_fname的长度实际是20),不这样绑定就会失败。我分析多出的2可能是为了存放字符串结尾的空字符null和BSTR字符串开头的一个字(表示 BSTR的长度)。这个问题对于初学者来说可能是一个意想不到的问题。
  CADORecordBinding类的定义在icrsint.h文件里,内容是:
  class CADORecordBinding
  {
  public:
  STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;
  };
  BEGIN_ADO_BINDING宏的定义也在icrsint.h文件里,内容是:
  #define BEGIN_ADO_BINDING(cls) public: \
  typedef cls ADORowClass; \
  const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { \
  static const ADO_BINDING_ENTRY rgADOBindingEntries[] = {
  ADO_VARIABLE_LENGTH_ENTRY2宏的定义也在icrsint.h文件里:#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)\ {Ordinal, \ DataType, \ 0, \ 0, \ Size, \ offsetof(ADORowClass, Buffer), \ offsetof(ADORowClass, Status), \ 0, \ classoffset(CADORecordBinding, ADORowClass), \ Modify},
  #define END_ADO_BINDING宏的定义也在icrsint.h文件里:#define END_ADO_BINDING() {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}};\
  return rgADOBindingEntries;}
  (2). 绑定
  _RecordsetPtr Rs1;
  IADORecordBinding *picRs=NULL;
  CCustomRs rs;
  ...... Rs1->QueryInterface(__uuidof(IADORecordBinding),
  (LPVOID*)&picRs));
  picRs->BindToRecordset(&rs);
  派生出的类必须通过IADORecordBinding接口才能绑定,调用它的BindToRecordset方法就行了。
  (3). rs中的变量即是当前记录字段的值
  //Set sort and filter condition:
  // Step 4: Manipulate the data
  Rs1->Fields->GetItem("au_lname")->Properties->GetItem("Optimize")->Value = true;
  Rs1->Sort = "au_lname ASC";
  Rs1->Filter = "phone LIKE '415 5*'";
  Rs1->MoveFirst();
  while (VARIANT_FALSE == Rs1->EndOfFile)
  {
  printf("Name: %s\t %s\tPhone: %s\n", (rs.lau_fnameStatus == adFldOK ? rs.m_szau_fname : ""), (rs.lau_lnameStatus == adFldOK ? rs.m_szau_lname : ""), (rs.lphoneStatus == adFldOK ? rs.m_szphone : "")); if (rs.lphoneStatus == adFldOK) strcpy(rs.m_szphone, "777");
  TESTHR(picRs->Update(&rs)); // Add change to the batch
  Rs1->MoveNext();
  }
  Rs1->Filter = (long) adFilterNone;
  ......
  if (picRs) picRs->Release();
  Rs1->Close();
  pConn->Close();
  只要字段的状态是adFldOK,就可以访问。如果修改了字段,不要忘了先调用picRs的Update(注意不是Recordset的Update),然后才关闭,也不要忘了释放picRs(即picRs->Release();)。
  (4). 此时还可以用IADORecordBinding接口添加新纪录
  if(FAILED(picRs->AddNew(&rs)))
  ......

广告合作:400-664-0084 全国热线:400-664-0084
Copyright 2010 - 2017 www.my8848.com 珠峰网 粤ICP备15066211号
珠峰网 版权所有 All Rights Reserved