📄 rowset.cpp
字号:
pIColumnsInfo->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// myCreateAccessor
//
// This function takes an IUnknown pointer for a rowset object
// and creates an accessor that describes the layout of the
// buffer we will use when we fetch data. The provider will fill
// this buffer according to the description contained in the
// accessor that we will create here.
//
/////////////////////////////////////////////////////////////////
HRESULT myCreateAccessor( IUnknown * pUnkRowset,
HACCESSOR * phAccessor,
ULONG * pcBindings,
DBBINDING ** prgBindings,
ULONG * pcbRowSize
)
{
HRESULT hr;
IAccessor * pIAccessor = NULL;
// An accessor is basically a handle to a collection of bindings.
// To create the accessor, we need to first create an array of
// bindings for the columns in the rowset.
CHECK_HR(hr = mySetupBindings(pUnkRowset, pcBindings, prgBindings, pcbRowSize));
// Now that we have an array of bindings, tell the provider to
// create the accessor for those bindings. We get back a handle
// to this accessor, which we will use when fetching data.
XCHECK_HR(hr = pUnkRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor));
XCHECK_HR(hr = pIAccessor->CreateAccessor(
DBACCESSOR_ROWDATA, // dwAccessorFlags
*pcBindings, // cBindings
*prgBindings, // rgBindings
0, // cbRowSize
phAccessor, // phAccessor
NULL // rgStatus
));
CLEANUP:
if( pIAccessor )
pIAccessor->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// myDisplayRowset
//
// This function will display data from a rowset object and will
// allow the user to perform basic navigation of the rowset.
//
// The function takes a pointer to a rowset object's IUnknown
// and, optionally, the name of a column and a buffer that will
// receive the value of that column when the user selects a row.
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayRowset(IUnknown * pUnkRowset,
LPCWSTR pwszColToReturn,
ULONG cchBuffer,
LPWSTR pwszBuffer
)
{
HRESULT hr;
IRowset * pIRowset = NULL;
ULONG cBindings;
DBBINDING * rgBindings = NULL;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
ULONG cbRowSize;
void * pData = NULL;
ULONG * rgDispSize = NULL;
ULONG cRowsObtained;
HROW * rghRows = NULL;
ULONG iRow;
LONG cRows = MAX_ROWS;
LONG iRetCol = -1;
BOOL fCanFetchBackwards;
ULONG iIndex;
void * pCurData;
// Obtain the IRowset interface for use in fetching rows and data
XCHECK_HR(hr = pUnkRowset->QueryInterface(IID_IRowset, (void**)&pIRowset));
// Determine whether this rowset supports fetching data backwards;
// we use this to determine whether the rowset can support moving
// to the previous set of rows, described in more detail below
myGetProperty(pUnkRowset, IID_IRowset, DBPROP_CANFETCHBACKWARDS, DBPROPSET_ROWSET, &fCanFetchBackwards);
// If the caller wants us to return the data for a particular column
// from a user-selected row, we need to turn the column name into a
// column ordinal
if( pwszColToReturn )
CHECK_HR(hr = myFindColumn(pUnkRowset, pwszColToReturn, &iRetCol));
// Create an accessor. An accessor is basically a handle to a
// collection of bindings that describes to the provider how to
// copy (and convert, if necessary) column data into our buffer.
// The accessor that this creates will bind all columns either as
// DBTYPE_WSTR (a Unicode string) or as an ISequentialStream object
// (used for BLOB data). This will also give us the size of the
// row buffer that the accessor describes to the provider.
CHECK_HR(hr = myCreateAccessor(pUnkRowset, &hAccessor, &cBindings, &rgBindings, &cbRowSize));
// Allocate enough memory to hold cRows rows of data; this is
// where the actual row data from the provider will be placed
pData = CoTaskMemAlloc(cbRowSize * MAX_ROWS);
CHECK_MEMORY(hr, pData);
// Allocate memory for an array that we will use to calculate the
// maximum display size used by each column in the current set of
// rows
rgDispSize = (ULONG *)CoTaskMemAlloc(cBindings * sizeof(ULONG));
CHECK_MEMORY(hr, rgDispSize);
// In this loop, we perform the following process:
// - reset the maximum display size array
// - try to get cRows row handles from the provider
// - these handles are then used to actually get the row data from the
// provider copied into our allocated buffer
// - calculate the maximum display size for each column
// - release the row handles to the rows we obtained
// - display the column names for the rowset
// - display the row data for the rows that we fetched
// - get user input
// - free the provider-allocated row handle array
// - repeat unless the user has chosen to quit or has selected a row
while( hr == S_OK )
{
// Clear the maximum display size array
memset(rgDispSize, 0, cBindings * sizeof(ULONG));
// Attempt to get cRows row handles from the provider
XCHECK_HR(hr = pIRowset->GetNextRows(
DB_NULL_HCHAPTER, // hChapter
0, // lOffset
cRows, // cRows
&cRowsObtained, // pcRowsObtained
&rghRows // prghRows
));
// Loop over the row handles obtained from GetNextRows,
// actually fetching the data for these rows into our buffer
for( iRow = 0; iRow < cRowsObtained; iRow++ )
{
// Find the location in our buffer where we want to place
// the data for this row. Note that if we fetched rows
// backwards (cRows < 0), the row handles obtained from the
// provider are reversed from the order in which we want to
// actually display the data on the screen, so we will
// account for this. This ensures that the resulting order
// of row data in the pData buffer matches the order we
// wish to use to display the data.
iIndex = cRows > 0 ? iRow : cRowsObtained - iRow - 1;
pCurData = (BYTE*)pData + (cbRowSize * iIndex);
// Get the data for this row handle. The provider will copy
// (and convert, if necessary) the data for each of the
// columns that are described in our Aaccessor into the given
// buffer (pCurData).
XCHECK_HR(hr = pIRowset->GetData(
rghRows[iRow], // hRow
hAccessor, // hAccessor
pCurData // pData
));
// Update the maximum display size array, accounting for
// this row
CHECK_HR(hr = myUpdateDisplaySize(cBindings, rgBindings, pCurData, rgDispSize));
}
// If we obtained rows, release the row handles for the retrieved
// rows and display the names of the rowset columns before we
// display the data
if( cRowsObtained )
{
// Release the row handles that we obtained
XCHECK_HR(hr = pIRowset->ReleaseRows(
cRowsObtained, // cRows
rghRows, // rghRows
NULL, // rgRowOptions
NULL, // rgRefCounts
NULL // rgRowStatus
));
// Display the names of the rowset columns
CHECK_HR(hr = myDisplayColumnNames(pIRowset, rgDispSize));
}
// For each row that we obtained the data for, display this data
for( iRow = 0; iRow < cRowsObtained; iRow++ )
{
// Get a pointer to the data for this row
pCurData = (BYTE*)pData + (cbRowSize* iRow);
// And display the row data
CHECK_HR(hr = myDisplayRow(iRow, cBindings, rgBindings, pCurData, rgDispSize));
}
// Allow the user to navigate the rowset. This displays the
// appropriate prompts, gets the user's input, may call
// IRowset::RestartPosition, and may copy data from a selected row
// to the selection buffer, if so directed. This will return S_OK
// if the user asked for more rows, S_FALSE if the user selected a
// row, or E_FAIL if the user quits.
hr = myInteractWithRowset(
pIRowset, // IRowset pointer, for RestartPosition
&cRows, // updated with fetch direction value
cRowsObtained, // to indicate selection range
fCanFetchBackwards, // whether [P]revious is supported
pData, // data pointer for copying selection
cbRowSize, // size of rows for copying selection
iRetCol >= 0 ? // bindings for the selection column,
&rgBindings[iRetCol] : // or NULL if no selection column
NULL,
cchBuffer, // size of the selection buffer
pwszBuffer); // pointer to the selection buffer
// Since we are allowing the provider to allocate the memory for
// the row handle array, we will free this memory and reset the
// pointer to NULL. If this is not NULL on the next call to
// GetNextRows, the provider will assume that it points to an
// allocated array of the required size (which may not be the case
// if we obtained less than cRows rows from this last call to
// GetNextRows).
CoTaskMemFree(rghRows);
rghRows = NULL;
}
CLEANUP:
myFreeBindings(cBindings, rgBindings);
CoTaskMemFree(rgDispSize);
CoTaskMemFree(pData);
if( pIRowset )
pIRowset->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// myInteractWithRowset
//
// This function allows the user to interact with the rowset. It
// prompts the user appropriately, gets the user's input, may
// call IRowset::RestartPosition if the user requests a restart,
// and will copy data from a selected row to the selection
// buffer.
//
/////////////////////////////////////////////////////////////////
HRESULT myInteractWithRowset( IRowset * pIRowset,
LONG * pcRows,
ULONG cRowsObtained,
BOOL fCanFetchBackwards,
void * pData,
ULONG cbRowSize,
DBBINDING * pBinding,
ULONG cchBuffer,
LPWSTR pwszBuffer
)
{
HRESULT hr = S_OK;
CHAR ch;
// Let the user know if no rows were fetched
if( !cRowsObtained )
printf( "\n*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n" \
"* *\n" \
"* No rows obtained on this fetch! *\n" \
"* *\n" \
"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n");
// Print navigation options
if( fCanFetchBackwards )
printf("\n[P]revious; [N]ext; [R]estart; ");
else
printf("\n[N]ext; [R]estart; ");
// Print selection options
if( cRowsObtained && pwszBuffer && pBinding )
printf("[0]-[%d] for a row; ", cRowsObtained - 1);
// User can always quit the program
printf("[Q]uit? ");
// Get the user's input
while( TRUE )
{
// Get a character from the console
ch = myGetChar();
// Look for one of the allowed options; if not found, go
// back around and wait for another input from the user
// If we're looking for a row selection, allow the user to select
// a row that we fetched, and then copy the data from the requested
// column into the selection buffer we were passed
if( pwszBuffer && pBinding && ch >= '0' && ch < (int)('0' + cRowsObtained) )
{
// Save the data for the selected row
ULONG nSelection = ch - '0';
_snwprintf(pwszBuffer, cchBuffer, L"%s",
(WCHAR *)((BYTE *)pData + cbRowSize * nSelection +
pBinding->obValue));
pwszBuffer[cchBuffer] = L'\0';
hr = S_FALSE;
}
// If the provider supports fetching backwards, set *pcRows
// to -MAX_ROWS. When GetNextRows is called with this value,
// it will fetch rows backwards from the current position
// until it fetches MAX_ROWS rows or hits the end of the rowset.
else if( fCanFetchBackwards && ch == 'p' )
{
// Fetch backwards
*pcRows = -MAX_ROWS;
}
// Set *pcRows so that the next call to GetNextRows fetches
// MAX_ROWS rows forward from the current position
else if( ch == 'n' )
{
// Fetch forward
*pcRows = MAX_ROWS;
}
// Call IRowset::RestartPosition, and fetch the first MAX_ROWS
// rows of the rowset forward from there
else if( ch == 'r' )
{
// RestartPosition
*pcRows = MAX_ROWS;
XCHECK_HR(hr = pIRowset->RestartPosition(DB_NULL_HCHAPTER));
// Restarting a command may return the DB_S_COMMANDREEXECUTED
// warning. If this happens, we still want the caller to
// continue to display data, so we will reset the result code
// to S_OK.
hr = S_OK;
}
// Quit the program
else if( ch == 'q' )
{
hr = E_FAIL;
}
// Invalid option; go back up and get another character from the
// user
else
{
continue;
}
// Echo the character and stop waiting for input
printf("%c\n", ch);
break;
}
CLEANUP:
return hr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -