⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 irowsetchangeimpl.h

📁 The ATL OLE DB Provider templates only seem to support read-only rowsets and making them support upd
💻 H
📖 第 1 页 / 共 2 页
字号:
#ifndef __I_ROWSET_CHANGE_IMPL__INCLUDED__
#define __I_ROWSET_CHANGE_IMPL__INCLUDED__

// IRowsetChangeImpl provides an implementation of IRowsetChange which sends
// appropriate notifications using IRowsetNotify when inserts, updates or 
// deletes occur.
//
// Your derived class must support IConnectionPointContainerImpl and have a
// connection point map that includes IRowsetNotify
//
// This code is half ready to support IRowsetUpdate. When IRowsetUpdate is
// available changes made through this interface would be cached until 
// IRowsetUpdate::Update is called. This allows for undo, etc. 
// There's a matching IRowsetUpdateImpl that derives from this and implements
// DelayedUpdateMode() so that it returns the setting of the IRowsetUpdate 
// rowset property.... More work is needed to make this work properly.
// 
//
// By inheriting from this implementation you can set the following rowset 
// properties:
//
//       PROPERTY_INFO_ENTRY_VALUE(IRowsetChange, VARIANT_TRUE)
//
//       PROPERTY_INFO_ENTRY_EX(
//          UPDATABILITY, 
//          VT_I4, 
//          DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, 
//          DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT, 
//          0)
//
//       PROPERTY_INFO_ENTRY_EX(
//          OWNINSERT, 
//          VT_BOOL, 
//          DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, 
//          VARIANT_TRUE, 
//          0)
//
//       PROPERTY_INFO_ENTRY_EX(
//          OWNUPDATEDELETE, 
//          VT_BOOL, 
//          DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, 
//          VARIANT_TRUE, 
//          0)
// 
//       PROPERTY_INFO_ENTRY_EX(
//          REMOVEDELETED, 
//          VT_BOOL, 
//          DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, 
//          VARIANT_TRUE, 
//          0)
// 
//       PROPERTY_INFO_ENTRY_EX(
//          IConnectionPointContainer, 
//          VT_BOOL, 
//          DBPROPFLAGS_ROWSET | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, 
//          VARIANT_TRUE, 
//          0)
//

#include <atlcom.h>
#include <atldb.h>

#include "IRowsetNotifySourceImpl.h"

template <class T, class Storage, class Base>
class ATL_NO_VTABLE CRowsetChangeImpl;

///////////////////////////////////////////////////////////////////////////
// class IRowsetChangeImpl

template <class T, class Storage>
class ATL_NO_VTABLE IRowsetChangeImpl : public CRowsetChangeImpl<T, Storage, IRowsetChange>
{
   public :

      virtual bool DelayedUpdateMode() { return false; }
};
///////////////////////////////////////////////////////////////////////////
// class CRowsetChangeImpl

template <class T, class Storage, class Base>
class ATL_NO_VTABLE CRowsetChangeImpl : 
   public Base,
   public IRowsetNotifySourceImpl<T>
{
public:

   // Implement IRowsetChange

   STDMETHOD(DeleteRows)(
      HCHAPTER hChapter, 
      ULONG cRows, 
      const HROW rghRows[], 
      DBROWSTATUS rgRowStatus[]);

   STDMETHOD(InsertRow)(
      HCHAPTER hChapter, 
      HACCESSOR hAccessor, 
      void *pData, 
      HROW *phRow);

   STDMETHOD(SetData)(
      HROW hRow, 
      HACCESSOR hAccessor, 
      void *pSrcData);

   protected :

      // Internal checks to see what functionality to expose

      virtual bool DelayedUpdateMode() = 0;

      bool Supports(UINT updateMask);

      // Notification helpers

      bool SendPreRowChangeNotifications(
         DBREASON eReason,
         ULONG cRows,            
         const ULONG rgRows[]);

      void SendPostRowChangeNotifications(
         DBREASON eReason,
         ULONG cRows,            
         const ULONG rgRows[]);

      void SendFailedRowChangeNotifications(
         DBREASON eReason,
         ULONG cRows,            
         const ULONG rgRows[]);

      bool SendPreSetDataNotifications(
         HROW hRow,
         ULONG cColumns,            
         ULONG rgColumns[]);

      void SendPostSetDataNotifications(
         HROW hRow,
         ULONG cColumns,            
         ULONG rgColumns[]);

      void SendFailedSetDataNotifications(
         HROW hRow,
         ULONG cColumns,            
         ULONG rgColumns[]);

   private :

      // This is where the real work of SetData() is done. Since we reuse the
      // code for the InsertRow we need a way to prevent the sending of
      // the normal SetData() notifications. If you need to replace the SetData()
      // functionality then you should override THIS function and not SetData() 
      // itself.

      virtual HRESULT SetDataHelper(
         HROW hRow, 
         HACCESSOR hAccessor, 
         void *pSrcData,
         bool bSendNotifications);

      // private binding to column info mapping function

      ATLCOLUMNINFO *FindColByOrdinal(
         ATLCOLUMNINFO *pColInfo,
         ULONG cCols,
         UINT iOrdinal);
};

///////////////////////////////////////////////////////////////////////////////
// Implement IRowsetChange
///////////////////////////////////////////////////////////////////////////////

template <class T, class Storage, class Base>
STDMETHODIMP CRowsetChangeImpl<T, Storage, Base>::DeleteRows(
   HCHAPTER hChapter, 
   ULONG cRows, 
   const HROW rghRows[], 
   DBROWSTATUS rgRowStatus[])
{
   ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows()\n");

   if (!Supports(DBPROPVAL_UP_DELETE))
   {
      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows - Error - DBPROPVAL_UP_DELETE not supported\n");
      
      return DB_E_NOTSUPPORTED;
   }

   if (hChapter != DB_NULL_HCHAPTER)
   {
      // I think we should really just ignore the chapter handle if we dont support
      // chapters. If we start getting problems then just take this check out.

      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows - Error - (hChapter != DB_NULL_HCHAPTER)\n");

      return DB_E_BADCHAPTER;
   }

   if (!rghRows && cRows != 0)
   {
      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows - Error - (!rghRows && cRows != 0)\n");

      return E_INVALIDARG;
   }

   if (cRows == 0)
   {
      // Nothing to do...
      return S_OK;
   }

   if (DelayedUpdateMode())
   {
      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows - Error - delayed updates not supported\n");

      return E_NOTIMPL;
   }

   bool bErrorsOccurred = false;
   bool bAtLeastOneRowDeleted = false;

   HRESULT hr = E_FAIL;

   // At present we send delete notifications once per call, I think this is how it should be done.
   // It doesn't allow a client to veto single row deletes though. Perhaps we should call once
   // per row that we're deleting?
   // Per rowset deletion and per row deletion code is included. I think only one set should be 
   // active at a time...
   
   // per rowset notification
   bool bDeleteCancelled = !SendPreRowChangeNotifications(DBREASON_ROW_DELETE, cRows, rghRows);
   
   for (size_t i = 0; i < cRows; i++)
   {
      T *pT = (T*)this;
   
      T::_HRowClass* pRow = 0; 

      if (rghRows[i] == 0 || (pRow = pT->m_rgRowHandles.Lookup((T::_HRowClass::KeyType)rghRows[i])) == NULL)
      {
         ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::DeleteRows - Error - Bad row handle\n");
      
         if (rgRowStatus)
         {
            rgRowStatus[i] = DBROWSTATUS_E_INVALID;
            bErrorsOccurred = true;
         }
      }
      else
      {
         // row is OK, lets go...

         // per row notification code
         //if (!SendPreRowChangeNotifications(DBREASON_ROW_DELETE, 1, &rghRows[i]))
         //{
         //   hr = DBROWSTATUS_E_CANCELED;
         //}
         //else
         //{
            if (!bDeleteCancelled)
            {
               hr = pT->m_rgRowData.RemoveAt(rghRows[i]);
            }
            else
            {
               hr = DBROWSTATUS_E_CANCELED;
            }
         //}

         if (rgRowStatus)
         {
            rgRowStatus[i] = hr;
         }

         if (DBROWSTATUS_S_OK == hr)
         {
            bAtLeastOneRowDeleted = true;
             
            // per row notification...
            //SendPostRowChangeNotifications(DBREASON_ROW_DELETE, 1, &rghRows[i]);
         }
         else
         {
            bErrorsOccurred = true;

            // per row notification...
            //SendFailedRowChangeNotifications(DBREASON_ROW_DELETE, 1, &rghRows[i]);
         }
      }
   }

   if (bErrorsOccurred)
   {
      if (bAtLeastOneRowDeleted)
      {
         hr = DB_S_ERRORSOCCURRED;
      }
      else
      {
         hr = DB_E_ERRORSOCCURRED;
      }
   }
   else
   {
      hr = S_OK;
   }

   // per rowset notification...
   if (SUCCEEDED(hr))
   {
      SendPostRowChangeNotifications(DBREASON_ROW_DELETE, cRows, rghRows);
   }
   else
   {
      SendFailedRowChangeNotifications(DBREASON_ROW_DELETE, cRows, rghRows);
   }

   return hr;
}

template <class T, class Storage, class Base>
STDMETHODIMP CRowsetChangeImpl<T, Storage, Base>::InsertRow(
   HCHAPTER hChapter, 
   HACCESSOR hAccessor, 
   void *pData, 
   HROW *phRow)
{
   ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::InsertRow()\n");

   if (!Supports(DBPROPVAL_UP_INSERT))
   {
      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::InsertRow - Error - DBPROPVAL_UP_INSERT not supported\n");
      return DB_E_NOTSUPPORTED;
   }
   
   if (phRow)
   {
      *phRow = 0;
   }

   if (hChapter != DB_NULL_HCHAPTER)
   {
      // I think we should really just ignore the chapter handle if we dont support
      // chapters. If we start getting problems then just take this check out.

      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::InsertRow - Error - (hChapter != DB_NULL_HCHAPTER)\n");
      return DB_E_BADCHAPTER;
   }

   if (DelayedUpdateMode())
   {
      ATLTRACE2(atlTraceDBProvider, 0, "IRowsetChange::InsertRow - Error - delayed updates not supported\n");

      return E_NOTIMPL;
   }

   // What we can do here is add a new row, then call setData on it...

   T *pT = (T*)this;

   LONG newRowIndex = pT->m_rgRowData.GetSize();
   ULONG cRowsObtained = 0;
   HROW newRow = newRowIndex;    // hmm

   // create a space on the end of the data array for the new row...

   // Pity the type stored in the simple array isn't a typedef so we could
   // access it here as something like...
   //
   // typename pT->m_rgRowData::Storage storage

   if (!SendPreRowChangeNotifications(DBREASON_ROW_INSERT, 1, &newRow))
   {  
      return DB_E_CANCELED;
   }

   Storage storage;
   
   HRESULT hr = pT->m_rgRowData.Add(storage);
   
   if (SUCCEEDED(hr))
   {
      hr = pT->CreateRow(newRowIndex, cRowsObtained, &newRow);
      
      if (SUCCEEDED(hr))
      {
         hr = SetDataHelper(newRow, hAccessor, pData, false);
         
         if (SUCCEEDED(hr))
         {
            if (phRow)
            {
               *phRow = newRow;
            }
         }
         else
         {
            pT->ReleaseRows(1, &newRow, 0, 0, 0);
            pT->m_rgRowData.RemoveAt(newRowIndex);
         }
      }
      else 
      {
         pT->m_rgRowData.RemoveAt(newRowIndex);
      }
   }

   if (SUCCEEDED(hr))
   {
      SendPostRowChangeNotifications(DBREASON_ROW_INSERT, 1, &newRow);
   }
   else
   {
      SendFailedRowChangeNotifications(DBREASON_ROW_INSERT, 1, &newRow);
   }

   return hr;
}

template <class T, class Storage, class Base>
STDMETHODIMP CRowsetChangeImpl<T, Storage, Base>::SetData(
   HROW hRow, 
   HACCESSOR hAccessor, 
   void *pSrcData)
{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -