Figure 2 Object Types
Data Sources
cotype TDataSource {
[mandatory] interface IDBInfo;
[mandatory] interface IDBInitialize;
[mandatory] interface IDBCreateSession;
[optional] interface IPersistFile; //for persistence, see OLE2
[optional] interface IProperties; //for persistence, see OLE2
}
DB Sessions
cotype TDBSession {
[mandatory] interface IOpenRowset;
[optional] interface IDBCreateCommand; //mandatory if Commands are supported
[optional] interface ITransactionLocal; //mandatory if transactions are
//supported
[optional] interface ITransactionObject;//optional if transactions are
//supported
[optional] interface ITransactionJoin; //mandatory if coordinated
//transactions
[optional] interface ITableDefinition; //mandatory if table creation is
//supported
[optional] interface IIndexDefinition; //mandatory if index creation is
//supported
[optional] interface IDBSchemaRowset; //mandatory if Schema are supported
}
Commands
cotype TCommand {
[mandatory] interface ICommand;
[mandatory] interface IAccessor;
[mandatory] interface IColumnsInfo;
[mandatory] interface ICommandProperties;
[mandatory] interface ICommandText;
[optional] interface ICommandWithParameters; //mandatory if parameters
//supported
[optional] interface ICommandPrepare; // mandatory
[optional] interface IColumnsRowset;
}
Rowsets
cotype TRowset {
[mandatory] interface IAccessor;
[mandatory] interface IRowsetInfo;
[mandatory] interface IColumnsInfo;
[mandatory] interface IRowset; //IRowset or a derivative is mandatory
[optional] interface IColumnsRowset;
[optional] interface IRowsetIdentity;
[optional] interface IRowsetLocate;
[optional] interface IRowsetScroll;
[optional] interface IRowsetExactScroll;
[optional] interface IRowsetChange;
[optional] interface IRowsetDelete;
[optional] interface IRowsetNewRow;
[optional] interface IRowsetUpdate;
[optional] interface IRowsetResynch;
[optional] interface IRowsetLockRows;
[optional] interface IRowsetWithParameters;
[optional] interface IRowsetCopyRows;
[optional] interface IConnectionPointContainer; //mandatory if notifications supported
[optional] interface IProvideMoniker;
}
Indexes
cotype TIndex {
[mandatory] interface IRowset;
[mandatory] interface IRowsetIndex;
[mandatory] interface IAccessor;
[mandatory] interface IColumnsInfo;
[optional] interface IRowsetDelete;
[optional] interface IRowsetNewRow;
};
Errors
cotype TErrorObject {
[mandatory] interface IErrorInfo;
[mandatory] interface IErrorRecords;
};
Transactions
cotype TTransaction {
[mandatory] interface ITransaction;
[mandatory] interface ITransactionOptions;
[mandatory] interface IConnectionPointContainer;
};
Figure 4 DBBINDING Structure
typedef struct tagDBBINDING
{
DBCOLUMNPART dwPart; // Value | Length | Status
DBPARAMIO eParamIO; // How Accessor is used (Input/Output
// parameter)
ULONG iColumn; // Row Column Ordinal
ULONG dwType; // DBTYPE of Consumers Structure
ITypeInfo __RPC_FAR *pTypeInfo; // COM abstract data type
DBNUMERIC __RPC_FAR *pNum; // Contains values for properties
ULONG obValue; // Offset of value consumer's struct
ULONG cbMaxLen; // Size of consumers field
DBOBJECT *pObject; // Information for binding to a OLE object
ULONG obLength; // Offset of length consumers struct
ULONG obStatus; // Offset of status consumers struct
} DBBINDING;
Figure 5 Binding Creation
//**********************************************************************
// SetupBindings
//
// Purpose:
//
// Creates bindings that map the data in the rowset's columns to
// locations in the data consumer's data buffer.
// Parameters:
//
// ULONG ulCol - number of columns in rowset to bind
// DBCOLUMNINFO* pColumnInfo - pointer to column metadata
// DBBINDING* rgBind_out - out pointer through which to return
// an array of binding structures, one
// structure per column bound
// ULONG* pcBind_out - out pointer through which to return
// the number of columns bound (number
// of valid elements in rgBind_out)
// ULONG* pcMaxRowSize_out - out pointer through which to return
// the buffer size necessary to hold
// the largest row data
//**********************************************************************
HRESULT SetupBindings (ULONG cCol, DBCOLUMNINFO* pColumnInfo,
DBBINDING* rgBind_out, ULONG* pcBind_out,
ULONG* pcMaxRowSize_out)
{
ULONG dwOffset = 0;
for (UNLONG ulCol = 0; ulCol < cCol; ulCol++)
{
rgBind_out[ulCol].dwPart = DBCOLUMNPART_VALUE|DBCOLUMNPART_LENGTH|
DBCOLUMNPART_STATUS;
rgBind_out[ulCol].eParamIO = DBPARAMIO_NOTPARAM;
rgBind_out[ulCol].iColumn = pColumnInfo[ulCol].iNumber;
rgBind_out[ulCol].dwType = DBTYPE_STR;
rgBind_out[ulCol].pTypeInfo = NULL;
rgBind_out[ulCol].pNum = NULL;
rgBind_out[ulCol].obValue = dwOffset + offsetof(COLUMNDATA,bData);
rgBind_out[ulCol].obLength = dwOffset + offsetof(COLUMNDATA,
dwLength);
rgBind_out[ulCol].obStatus = dwOffset + offsetof(COLUMNDATA,
dwStatus);
rgBind_out[ulCol].cbMaxLen = pColumnInfo[ulCol].dwType ==
DBTYPE_STR ?
pColumnInfo[ulCol].cbMaxLength +
sizeof(char) : DEFAULT_CBMAXLENGTH;
rgBind_out[ulCol].pObject = NULL;
dwOffset += rgBind_out[ulCol].cbMaxLen + offsetof( COLUMNDATA,
bData );
dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL );
}
*pcBind_out = ulCol;
*pcMaxRowSize_out = dwOffset;
return NOERROR;
}
Figure 6 CreateAccessor Parameters
DBACCESSORFLAGS dwAccessorFlags [in] | A bitmask that describes the properties of the Accessor and how to use it (DBACCESSOR_READ, DBACCESSOR_READWRITE, and so on). |
ULONG cBindings [in] | The number of bindings in the Accessor. |
const DBBINDING rgBindings[] [in] | An array of DBBINDING structures. |
ULONG cbRowSize [in] | The number of bytes applicable for a single row of data in a data consumer's buffer. |
ULONG* pulErrorBinding [out] | If an error occurs in the binding, this index will call out which binding caused the problem. It begins with zero. If no error occurs this value is not touched. If the caller passes a null pointer, this parameter is ignored. |
HACCESSOR *phAccessor [out] | A pointer to memory in which to return the handle, HACCESSOR, of the created Accessor. |
Figure 7 Getting Data from a Rowset
// Read the rows; 160 rows at a time
// Approximately 16KB of data assuming 100 Byte records
while (SUCCEEDED(hr = pIRowset -> GetNextRows (0, NULL, 0, 160,
&cRowsObtained,
&rghRows)) && cRowsObtained)
{
for (ULONG ulrow = 0; ulrow < cRowsObtained; ulrow++)
{
pIRowset -> GetData (rghRows(ulrow), hAccessor, rgData);
// code to print the data
PrintData (rgData);
}
pIRowset -> ReleaseRows (cRowsObtained, rghRows, NULL, NULL);
CoTaskMemoryFree(rghRows);
}
// release the Accessor and the Rowset
pIAccessor -> ReleaseAccessor(hAccessor);
pIRowset -> Release();
GetNextRows does not give you the data yet. Calling IRowset::GetData will actually get the data from rows that were returned to you from the prior call to IRowset::GetNextRows.
IRowset::GetData is prototyped as follows:
HRESULT GetData (HROW hRow, HACCESSOR hAccessor,
void *pData);
Figure 10 Execute()Parameters
Parameter | Description |
rgpUnkOuters [in] | An array of pointers to the controlling IUnknown interfaces if the Rowsets are created as parts of aggregates. If NULL, then no rowsets are parts of aggregates. |
riid [in] | The requested interface's ID. |
pParams [in/out] | The DBPARAMS structure specifies values for one or more parameters. |
phChapter [out] | A pointer to memory in which to return a pointer to the results chapter. If none of the Rowsets returned supports IRowsetWithParameters, *phChapter is set to DB_INVALID_HCHAPTER. |
fResults [in] | TRUE indicates resume a suspended execution. FALSE indicates execute the command. |
pcRowsets [in/out] | A pointer to memory containing the count of Rowsets. |
prgpRowsets [in/out] | An output array of Rowsets. |
ppRowsetNames [out] | An output array of the string names of all hierarchy levels. Array index 0 will contain the pseudo-name DBROWSET_ROOT, a constant that is always associated with the root of the hierarchy. The caller MUST NOT free the name strings; they are owned by the command object. |
Figure 14 CheckBook's CDocumentClass Rewritten to Use OLE DB
/////////////////////////////////////////////////////////////////////////////
//
// CHECKBOOKDOC.CPP
//
//////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#define DBINITCONSTANTS
#include "oledb.h"
#include "viewhints.h"
#include "row.h"
#include "Checkbook.h"
#include "CheckbookDoc.h"
#include "checkview.h"
#include "ledgerview.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CCheckbookDoc
IMPLEMENT_DYNCREATE(CCheckbookDoc, CDocument)
BEGIN_MESSAGE_MAP(CCheckbookDoc, CDocument)
//{{AFX_MSG_MAP(CCheckbookDoc)
ON_COMMAND(ID_EDIT_NEW_CHECK, OnNewCheck)
ON_COMMAND(ID_NEXT_CHECK, OnNextCheck)
ON_UPDATE_COMMAND_UI(ID_NEXT_CHECK, OnUpdateNextCheck)
ON_COMMAND(ID_PREV_CHECK, OnPrevCheck)
ON_UPDATE_COMMAND_UI(ID_PREV_CHECK, OnUpdatePrevCheck)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(CCheckbookDoc, CDocument)
//{{AFX_DISPATCH_MAP(CCheckbookDoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////////////////
// CCheckbookDoc construction/destruction
CCheckbookDoc::CCheckbookDoc()
{
// Initialize variables
m_pIDBInitialize = NULL;
m_pIRowset = NULL;
m_pIAccessor = NULL;
m_hAccessor = NULL;
m_nActiveRecord = 0;
m_cColumns = 0;
m_pColumnInfo = NULL;
m_ulMaxRowSize = 0;
}
CCheckbookDoc::~CCheckbookDoc()
{
// Cleanup all of the interfaces and Rows
CleanUp();
}
// Release all interfaces that may have been retrieved from the data
// provider and frees up memory that may have been allocated
// to hold Row data.
void CCheckbookDoc::CleanUp()
{
if (m_pColumnInfo != NULL)
{
CoTaskMemFree (m_pColumnInfo);
m_pColumnInfo = NULL;
}
// Release the accessor
if (m_pIAccessor != NULL)
{
if (m_hAccessor != NULL)
{
m_pIAccessor->ReleaseAccessor(m_hAccessor);
m_hAccessor = NULL;
}
m_pIAccessor->Release();
m_pIAccessor = NULL;
}
// Take care of the ulIndexset interface and any rows that may
// have been retrieved from the data provider.
if (m_pIRowset != NULL)
{
CRow *pRow = NULL;
POSITION pos = NULL;
// Release all rows held by the data provider
ReleaseRows();
// Cleaup the memory that was used to hold the check
// information in the CObList m_oblRows.
while ((pos = m_oblRows.GetHeadPosition()) != NULL)
{
pRow = (CRow *)m_oblRows.GetAt(pos);
m_oblRows.RemoveAt(pos);
if (pRow != NULL)
delete pRow;
}
// Release the rowset interface
m_pIRowset->Release();
m_pIRowset = NULL;
}
// Un-Initialize the DSO
if (m_pIDBInitialize != NULL)
{
m_pIDBInitialize -> Uninitialize();
m_pIDBInitialize->Release();
m_pIDBInitialize = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
// CCheckbookDoc diagnostics
#ifdef _DEBUG
void CCheckbookDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CCheckbookDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CCheckbookDoc commands
BOOL CCheckbookDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
HRESULT hr = S_OK;
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
// We have the file name, lets get the CLSID for the Check Book Data
// Provider so that an instance can be created.
CLSID CLSID_OLEDBObject;
if (CLSIDFromProgID(OLESTR("CHKBOOKDP.DSO.1"), &CLSID_OLEDBObject) != S_OK)
{
// Can't get CLSID for OLEDB object ProgID
AfxMessageBox (IDS_FAILED_CLSIDProgID);
return FALSE;
}
// Create an instance of the DSO, obtaining the IDBInitialize interface.
if (CoCreateInstance(CLSID_OLEDBObject, NULL, CLSCTX_SERVER,
IID_IDBInitialize, (LPVOID *)&m_pIDBInitialize) !=S_OK)
{
// Could not instantiate the CheckBook DSO
AfxMessageBox (IDS_FAILED_DSO_INSTANTIATE);
return FALSE;
}
// The next step is to initialize the data provider. During
// initialization, the data provider will open or create
// the checkbook file as necessary.
if ((hr = InitializeDSO(lpszPathName)) == S_OK)
{
// Get a Rowset Interface from the data provider
if ((hr = GetRowsetInterface()) == S_OK)
{
// Get column information used for the bindings
if ((hr = GetColumnInfo()) == S_OK)
{
// Now that the data consumer has the column information,
// set up the DBBINDING structure.
if ((hr = SetupBindings()) == S_OK)
{
// Create an Accessor for getting the check book
// data from the data provider.
if ((hr = CreateAccessor()) == S_OK)
{
// Fetch all of the data from the data provider
if ((hr = GetData ()) == S_OK)
{
// Update both the Check and Ledger views.
UpdateAllViews (NULL, VIEWHINT_GET_ALL_DATA);
}
}
}
}
}
}
if (hr != S_OK)
{
// There was a failure, cleanup all interfaces and do not
// create the document
CleanUp();
return FALSE;
}
return TRUE;
}
// initializes the Check Book data provider by
// providing the file name of the check book .chb file.
HRESULT CCheckbookDoc::InitializeDSO (LPCTSTR lpszPathName)
{
HRESULT hr = S_OK;
GUID rgOptionIDs[1];
VARIANT rgOptionVals[1];
VariantInit(&rgOptionVals[0]);
rgOptionIDs[0] = DBINIT_OPT_NAME;
rgOptionVals[0].vt = VT_BSTR;
CString strPathName = lpszPathName;
rgOptionVals[0].bstrVal = strPathName.AllocSysString();
hr = m_pIDBInitialize->Initialize (1, rgOptionIDs, rgOptionVals);
if (hr != S_OK)
AfxMessageBox(IDS_FAILED_DSO_INITIALIZE);
return hr;
}
// Obtain a Rowset interface from the check book data provider.
// The ulIndexset interface is saved for future uses such as
// retrieving,updating and adding new checks.
HRESULT CCheckbookDoc::GetRowsetInterface ()
{
HRESULT hr = S_OK;
// Create a DSSession object
IDBCreateSession *pIDBCreateSession = NULL;
hr = m_pIDBInitialize->QueryInterface(IID_IDBCreateSession,
(void **)&pIDBCreateSession);
if (hr == S_OK)
{
// From the DBSession object, get the IOpenRowset interface
IOpenRowset *pIOpenRowset = NULL;
hr = pIDBCreateSession -> CreateSession(NULL, IID_IOpenRowset,
(IUnknown **)&pIOpenRowset);
pIDBCreateSession -> Release();
if (hr == S_OK)
{
hr = pIOpenRowset -> OpenRowset(NULL, NULL, 0, NULL, IID_IRowset,
NULL, (IUnknown **)&m_pIRowset);
pIOpenRowset -> Release();
}
}
return hr;
}
// Retrieves an IAccessor interface and create an accessor that
// is used for reading and writing checks.
HRESULT CCheckbookDoc::CreateAccessor ()
{
HRESULT hr = S_OK;
hr = m_pIRowset -> QueryInterface(IID_IAccessor, (void **)&m_pIAccessor);
if (hr == S_OK)
{
ULONG ulErrorBinding = (ULONG)-1L;
hr = m_pIAccessor->CreateAccessor(DBACCESSOR_READWRITE |
DBACCESSOR_ROWDATA,
m_ulBindings, m_prgBindings, 0,
&ulErrorBinding, &m_hAccessor);
}
return hr;
}
// Releases all of the HROWS from within the data provider.
HRESULT CCheckbookDoc::ReleaseRows()
{
ASSERT (m_pIRowset);
ULONG ulRows;
HRESULT hr = S_OK;
HROW *prghRows = NULL;
ulRows = m_oblRows.GetCount();
prghRows = (HROW *) CoTaskMemAlloc((sizeof (HROW *)) * ulRows);
if (prghRows != NULL)
{
HROW *pTempRow;
pTempRow = prghRows;
POSITION pos = NULL;
CRow *pRow = NULL;
for (ULONG ulIndex = 0; ulIndex < ulRows; ulIndex++)
{
pos = m_oblRows.FindIndex (ulIndex);
pRow = (CRow *)m_oblRows.GetAt(pos);
*pTempRow++ = pRow -> m_hRow;
}
hr = m_pIRowset -> ReleaseRows(ulRows, prghRows, NULL, NULL);
CoTaskMemFree(prghRows);
}
else
hr = E_OUTOFMEMORY;
return hr;
}
// Retrieves a specific check from the CObList and returns the check
// in a CRow.
BOOL CCheckbookDoc::GetRow (ULONG ulIndex, CRow **pRow)
{
POSITION pos = NULL;
pos = m_oblRows.FindIndex (ulIndex);
if (pos != NULL)
{
*pRow = (CRow *)m_oblRows.GetAt(pos);
return TRUE;
}
return FALSE;
}
// Retrieves check information based on the index into the "checkbook".
BOOL CCheckbookDoc::GetCheck (ULONG ulIndex, UINT& nCheckNo, DWORD& dwCents,
CString& strPayTo, CString& strDate,
CString& strMemo)
{
CRow *pRow;
if (GetRow(ulIndex, &pRow))
{
COLUMNDATA *pColumn;
ASSERT (offsetof(COLUMNDATA, dwLength) == 0);
for (ULONG ulIndex=0; ulIndex < m_ulBindings; ulIndex++)
{
pColumn = (COLUMNDATA *) (pRow -> m_pData +
m_prgBindings[ulIndex].obLength);
switch (m_prgBindings[ulIndex].iColumn)
{
case CB_CHECKNO_ORDINAL:
nCheckNo = *(unsigned int *) pColumn->bData;
break;
case CB_AMOUNT_ORDINAL:
dwCents = *(unsigned long *) pColumn->bData;
break;
case CB_PAYTO_ORDINAL:
strPayTo = (const TCHAR *) &pColumn->bData;
break;
case CB_DATE_ORDINAL:
strDate = (const TCHAR *) &pColumn->bData;
break;
case CB_MEMO_ORDINAL:
strMemo = (const TCHAR *) &pColumn->bData;
break;
}
}
return TRUE;
}
return FALSE;
}
// Updates the active check with the values passed into the member function
// by using the data providers ulIndexsetChange interface.
HRESULT CCheckbookDoc::UpdateData(CView* pSourceView, UINT nCheckNo,
DWORD dwCents, LPCTSTR lpszPayTo,
LPCTSTR lpszDate, LPCTSTR lpszMemo)
{
ASSERT (m_pIRowset);
HRESULT hr = S_OK;
IRowsetChange *pIRowsetChange = NULL;
hr = m_pIRowset->QueryInterface(IID_IRowsetChange,
(void **)&pIRowsetChange);
if (hr == S_OK)
{
CRow *pRow;
if (GetRow(m_nActiveRecord, &pRow))
{
BYTE *pRowData = NULL;
pRowData = (BYTE *) CoTaskMemAlloc (m_ulMaxRowSize);
if (!pRowData)
return E_OUTOFMEMORY;
memcpy(pRowData, pRow -> m_pData, sizeof(*pRowData));
if (S_OK == (CopyCheckDataIntoBuffer (pRowData, nCheckNo,
dwCents, lpszPayTo,
lpszDate, lpszMemo)))
{
if ((hr = pIRowsetChange -> SetData (pRow -> m_hRow,
m_hAccessor, (const void *)pRowData)) == S_OK)
{
memcpy((BYTE *)pRow -> m_pData, (BYTE *)pRowData,
m_ulMaxRowSize);
UpdateAllViews(pSourceView, VIEWHINT_UPDATE_CHECK, NULL);
}
}
CoTaskMemFree((void *) pRowData);
}
}
pIRowsetChange -> Release();
return hr;
}
// Function creates a new record by using the ulIndexsetNewRow interface
// supported by the data provider. The new row is added to the CObList
// and the views are updated accordingly.
HRESULT CCheckbookDoc::AddNewCheck()
{
BYTE *pRowData = NULL;
HROW *prghRows = NULL;
HRESULT hr = S_OK;
ASSERT (m_pIRowset);
ASSERT (m_hAccessor);
ASSERT (m_pColumnInfo);
ASSERT (m_ulMaxRowSize != 0);
pRowData = (BYTE *) CoTaskMemAlloc (m_ulMaxRowSize);
if (!pRowData)
return E_OUTOFMEMORY;
IRowsetNewRow *pIRowsetNewRow = NULL;
hr = m_pIRowset->QueryInterface(IID_IRowsetNewRow,
(void **)&pIRowsetNewRow);
if (hr == S_OK)
{
HROW phRow = NULL;
if ((hr = pIRowsetNewRow -> SetNewData (NULL, m_hAccessor, pRowData,
&phRow)) == S_OK)
{
StoreRowData (phRow, pRowData);
// Make this the active check and update the ledger view.
m_nActiveRecord = m_oblRows.GetCount() - 1;
UpdateAllViews(NULL, VIEWHINT_ADD_CHECK);
}
else
CoTaskMemFree (pRowData);
pIRowsetNewRow -> Release();
}
return S_OK;
}
BOOL CCheckbookDoc::MaybeCommitDirtyCheck()
{
CView *pView;
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
pView = GetNextView(pos);
CCheckView *pCheckView = DYNAMIC_DOWNCAST(CCheckView, pView);
if (pCheckView != NULL)
return pCheckView->MaybeCommitDirtyCheck();
}
return TRUE;
}
// When the user changes the check selection in the ledger view, this member
// function is called to update the check view.
void CCheckbookDoc::ChangeSelectionToCheck(CLedgerView *pLedgerView,
ULONG ulIndex)
{
if (!MaybeCommitDirtyCheck())
return;
m_nActiveRecord = ulIndex;
UpdateAllViews (pLedgerView, NULL);
}
// When the user uses the toolbar or menu items to move through the checks,
// this member function is called to update the view appropriately.
void CCheckbookDoc::ChangeSelectionNextCheckNo(BOOL bNext)
{
if (bNext)
{
if (m_nActiveRecord < (GetNumberOfRows() - 1))
{
if (!MaybeCommitDirtyCheck())
return;
++m_nActiveRecord;
UpdateAllViews(NULL, VIEWHINT_NEXT_CHECK, NULL);
}
}
else
{
if (m_nActiveRecord > 0)
{
if (!MaybeCommitDirtyCheck())
return;
--m_nActiveRecord;
UpdateAllViews(NULL, VIEWHINT_NEXT_CHECK, NULL);
}
}
}
////////////////////////////////////////////////////////
// Commands
void CCheckbookDoc::OnNewCheck()
{
// Before creating a new record, which will become the new selection,
// ask the user whether he or she wants to commit data entered in the
// check view for the previously selected check.
if (!MaybeCommitDirtyCheck())
return;
AddNewCheck();
}
void CCheckbookDoc::OnNextCheck()
{
ChangeSelectionNextCheckNo(TRUE);
}
void CCheckbookDoc::OnUpdateNextCheck(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_nActiveRecord < (GetNumberOfRows() - 1));
}
void CCheckbookDoc::OnPrevCheck()
{
ChangeSelectionNextCheckNo(FALSE);
}
void CCheckbookDoc::OnUpdatePrevCheck(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_nActiveRecord > 0);
}
// Function retieves column information from the data provider.
HRESULT CCheckbookDoc::GetColumnInfo ()
{
ASSERT (m_pIRowset);
ULONG pcColumns;
HRESULT hr = S_OK;
DBCOLUMNINFO *prgInfo = NULL;
WCHAR *ppStringsBuffer = NULL;
// Get an interface pointer to IColumnsInfo
IColumnsInfo *pIColumnsInfo = NULL;
hr = m_pIRowset -> QueryInterface(IID_IColumnsInfo,
(void **)&pIColumnsInfo);
if (hr == S_OK)
{
// Get the Column Information
hr = pIColumnsInfo -> GetColumnInfo(&pcColumns,
(DBCOLUMNINFO **)&prgInfo,
(WCHAR **)&ppStringsBuffer);
pIColumnsInfo -> Release();
if (hr == S_OK)
{
m_cColumns = pcColumns;
m_pColumnInfo = prgInfo;
CoTaskMemFree (ppStringsBuffer);
ppStringsBuffer = NULL;
}
}
return hr;
}
// Function creates bindings that map the data in the rowset's columns
// to the check book data consumers buffer.
HRESULT CCheckbookDoc::SetupBindings ()
{
ASSERT (m_pColumnInfo);
UINT cBinding = 0;
DWORD dwOffset = 0;
// Since the check book data consumer displays all information
// about a check, and that's all that the data provider provides,
// get all of the data.
for (ULONG ulIndex=0; ulIndex < m_cColumns; ulIndex++)
{
m_prgBindings[cBinding].dwPart = DBCOLUMNPART_VALUE |
DBCOLUMNPART_LENGTH | DBCOLUMNPART_STATUS;
m_prgBindings[cBinding].eParamIO = DBPARAMIO_NOTPARAM;
m_prgBindings[cBinding].iColumn = m_pColumnInfo[ulIndex].iNumber;
m_prgBindings[cBinding].dwType = m_pColumnInfo[ulIndex].dwType;
m_prgBindings[cBinding].pTypeInfo = m_pColumnInfo[ulIndex].pTypeInfo;
m_prgBindings[cBinding].pNum = NULL;
m_prgBindings[cBinding].obValue = dwOffset + offsetof(COLUMNDATA,bData);
m_prgBindings[cBinding].obLength = dwOffset + offsetof(COLUMNDATA,
dwLength);
m_prgBindings[cBinding].obStatus = dwOffset + offsetof(COLUMNDATA,
dwStatus);
m_prgBindings[cBinding].cbMaxLen =
m_pColumnInfo[ulIndex].dwType ==
DBTYPE_STR ? m_pColumnInfo[ulIndex].cbMaxLength + sizeof(char) : m_pColumnInfo[ulIndex].cbMaxLength;
m_prgBindings[cBinding].pObject.pUnkOuter = NULL;
m_prgBindings[cBinding].pObject.iid = IID_NULL;
m_prgBindings[cBinding].pObject.pbc = NULL;
dwOffset += m_prgBindings[cBinding].cbMaxLen + offsetof (COLUMNDATA,
bData);
cBinding++;
}
m_ulBindings = cBinding;
m_ulMaxRowSize = dwOffset;
return S_OK;
}
HRESULT CCheckbookDoc::GetData ()
{
HRESULT hr = S_OK;
HROW *prghRows = NULL;
BYTE *pRowData = NULL;
ULONG ulRowsObtained;
ASSERT (m_pIRowset);
ASSERT (m_hAccessor);
ASSERT (m_pColumnInfo);
while (TRUE)
{
hr = m_pIRowset->GetNextRows(0, 0, 20, &ulRowsObtained, &prghRows);
if (FAILED(hr))
return hr;
// check to see the data provider returned any rows
if (ulRowsObtained == 0)
break;
// For each row, get the checkbook data from the ata provider
for (ULONG ulIndex = 0; ulIndex < ulRowsObtained; ulIndex++ )
{
pRowData = (BYTE *) CoTaskMemAlloc(m_ulMaxRowSize);
if (!pRowData)
return E_OUTOFMEMORY;
hr = m_pIRowset->GetData(prghRows[ulIndex], m_hAccessor, pRowData );
if (FAILED(hr))
{
// Free the task memory allocated by the data provider
CoTaskMemFree(prghRows);
if (pRowData)
CoTaskMemFree(pRowData);
return hr;
}
StoreRowData (prghRows[ulIndex], pRowData);
m_nActiveRecord = m_oblRows.GetCount() - 1;
}
// Free the task memory allocated by the data provider
CoTaskMemFree(prghRows);
prghRows = NULL;
}
return S_OK;
}
HRESULT CCheckbookDoc::StoreRowData (HROW phRow, BYTE *pData)
{
CRow *pRow = NULL;
HRESULT hr = S_OK;
COLUMNDATA* pColumn;
DWORD dwStatus, dwLength;
ASSERT (offsetof(COLUMNDATA, dwLength) == 0);
pRow = new CRow;
if (pRow == NULL)
return E_OUTOFMEMORY;
// Do some coersion error checking first before storing the data
for (ULONG ulIndex=0; ulIndex < m_ulBindings; ulIndex++)
{
pColumn = (COLUMNDATA *) (pData + m_prgBindings[ulIndex].obLength);
dwStatus = pColumn->dwStatus;
dwLength = pColumn->dwLength;
if (dwStatus == DBCOLUMNSTATUS_CANTCOERCE)
// Have a problem, therefore return an error
hr = E_UNEXPECTED;
else
{
switch (m_prgBindings[ulIndex].dwType)
{
case DBTYPE_UI8:
// Make sure this is the Amount Column
if (!m_prgBindings[ulIndex].iColumn == CB_AMOUNT_ORDINAL)
hr = E_UNEXPECTED;
break;
case DBTYPE_UI4:
// Make sure this is the Check Number Column
if (!m_prgBindings[ulIndex].iColumn == CB_CHECKNO_ORDINAL)
hr = E_UNEXPECTED;
break;
case DBTYPE_STR:
// Process the string related columns
if (!(m_prgBindings[ulIndex].iColumn == CB_PAYTO_ORDINAL ||
m_prgBindings[ulIndex].iColumn == CB_DATE_ORDINAL ||
m_prgBindings[ulIndex].iColumn == CB_MEMO_ORDINAL))
hr = E_UNEXPECTED;
break;
default:
hr = E_UNEXPECTED;
break;
}
if (hr != S_OK)
break;
}
}
if (hr != S_OK)
delete pRow;
else
{
// Save the check information in a CObList
pRow -> m_hRow = phRow;
pRow -> m_pData = pData;
m_oblRows.AddTail (pRow);
}
return hr;
}
HRESULT CCheckbookDoc::CopyCheckDataIntoBuffer (BYTE *pData, UINT nCheckNo,
DWORD dwCents, LPCTSTR lpszPayTo,
LPCTSTR lpszDate,
LPCTSTR lpszMemo)
{
HRESULT hr = S_OK;
COLUMNDATA* pColumn;
DWORD dwStatus, dwLength;
ASSERT (offsetof(COLUMNDATA, dwLength) == 0);
for (ULONG ulIndex=0; ulIndex < m_ulBindings; ulIndex++)
{
pColumn = (COLUMNDATA *) (pData + m_prgBindings[ulIndex].obLength);
dwStatus = pColumn->dwStatus;
dwLength = pColumn->dwLength;
switch (m_prgBindings[ulIndex].dwType)
{
case DBTYPE_UI8:
if (m_prgBindings[ulIndex].iColumn == CB_AMOUNT_ORDINAL)
*(unsigned long *)pColumn->bData = dwCents;
else
hr = E_UNEXPECTED;
break;
case DBTYPE_UI4:
if (m_prgBindings[ulIndex].iColumn == CB_CHECKNO_ORDINAL)
*(unsigned int *)pColumn->bData = nCheckNo;
else
hr = E_UNEXPECTED;
break;
case DBTYPE_STR:
if (m_prgBindings[ulIndex].iColumn == CB_PAYTO_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData, lpszPayTo);
else if (m_prgBindings[ulIndex].iColumn == CB_DATE_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData, lpszDate);
else if(m_prgBindings[ulIndex].iColumn == CB_MEMO_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData, lpszMemo);
else
hr = E_UNEXPECTED;
break;
default:
hr = E_UNEXPECTED;
break;
}
if (hr != S_OK)
break;
}
return hr;
}