📄 irowsetchangeimpl.h
字号:
#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 + -