📄 s15_03.cpp
字号:
case DBTYPE_DECIMAL:
case DBTYPE_NUMERIC:
case DBTYPE_DATE:
case DBTYPE_DBDATE:
case DBTYPE_DBTIMESTAMP:
case DBTYPE_GUID:
// Converted to a string, the above types will all fit into
// 50 characters, so use that plus space for the terminator.
rgBindings[iCol].cbMaxLen = (50 + 1) * sizeof(WCHAR);
break;
case DBTYPE_BYTES:
// In converting DBTYPE_BYTES to a string, each byte
// becomes two characters (e.g. 0xFF -> "FF"), so we
// will use double the maximum size of the column plus
// include space for the NULL terminator.
rgBindings[iCol].cbMaxLen =
(rgColumnInfo[iCol].ulColumnSize * 2 + 1) * sizeof(WCHAR);
break;
case DBTYPE_STR:
case DBTYPE_WSTR:
case DBTYPE_BSTR:
// Going from a string to our string representation,
// we can just take the maximum size of the column,
// a count of characters, and include space for the
// terminator, which is not included in the column size.
rgBindings[iCol].cbMaxLen =
(rgColumnInfo[iCol].ulColumnSize + 1) * sizeof(WCHAR);
break;
default:
// For any other type, we will simply use our maximum
// column buffer size, since the display size of these
// columns may be variable (e.g. DBTYPE_VARIANT) or
// unknown (e.g. provider-specific types).
rgBindings[iCol].cbMaxLen = MAX_COL_SIZE;
break;
};
// If the provider's native data type for this column is
// DBTYPE_IUNKNOWN or this is a BLOB column and the user
// has requested that we bind BLOB columns as ISequentialStream
// objects, bind this column as an ISequentialStream object if
// the provider supports our creating another ISequentialStream
// binding.
if( (rgColumnInfo[iCol].wType == DBTYPE_IUNKNOWN ||
((rgColumnInfo[iCol].dwFlags & DBCOLUMNFLAGS_ISLONG) ))//&&
//(g_dwFlags & USE_ISEQSTREAM))) &&
&& (fMultipleObjs || !cStorageObjs) )
{
// To create an ISequentialStream object, we will
// bind this column as DBTYPE_IUNKNOWN to indicate
// that we are requesting this column as an object.
rgBindings[iCol].wType = DBTYPE_IUNKNOWN;
// We want to allocate enough space in our buffer for
// the ISequentialStream pointer we will obtain from
// the provider.
rgBindings[iCol].cbMaxLen = sizeof(ISequentialStream *);
// To specify the type of object that we want from the
// provider, we need to create a DBOBJECT structure and
// place it in our binding for this column.
rgBindings[iCol].pObject =
(DBOBJECT *)CoTaskMemAlloc(sizeof(DBOBJECT));
CHECK_MEMORY(hr, rgBindings[iCol].pObject);
// Direct the provider to create an ISequentialStream
// object over the data for this column.
rgBindings[iCol].pObject->iid = IID_ISequentialStream;
// We want read access on the ISequentialStream
// object that the provider will create for us.
rgBindings[iCol].pObject->dwFlags = STGM_READ;
// Keep track of the number of storage objects
// (ISequentialStream is a storage interface) that we have
// requested, so that we can avoid requesting multiple storage
// objects from a provider that supports only a single storage
// object in our bindings.
cStorageObjs++;
}
// Ensure that the bound maximum length is no more than the
// maximum column size in bytes that we've defined.
rgBindings[iCol].cbMaxLen
= min(rgBindings[iCol].cbMaxLen, MAX_COL_SIZE);
// Update the offset past the end of this column's data so
// that the next column will begin in the correct place in
// the buffer.
dwOffset = rgBindings[iCol].cbMaxLen + rgBindings[iCol].obValue;
// Ensure that the data for the next column will be correctly
// aligned for all platforms, or if we're done with columns,
// that if we allocate space for multiple rows that the data
// for every row is correctly aligned.
dwOffset = ROUNDUP(dwOffset);
}
// Return the row size (the current dwOffset is the size of the row),
// the count of bindings, and the bindings array to the caller.
*pcbRowSize = dwOffset;
*pcBindings = cColumns;
*prgBindings = rgBindings;
CLEANUP:
CoTaskMemFree(rgColumnInfo);
CoTaskMemFree(pStringBuffer);
if( pIColumnsInfo )
pIColumnsInfo->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// 函数: DisplayRowset
//
// 显示行集对象数.
//
/////////////////////////////////////////////////////////////////
HRESULT DisplayRowset(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;
DBCOUNTITEM 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
// See IRowset
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
GetProperty(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 = FindColumn(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 = CreateAccessor(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);
// Display the names of the rowset columns.
CHECK_HR(hr = DisplayColumnNames(pIRowset, 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 = UpdateDisplaySize(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
));
}
// 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 = DisplayRow(iRow, cBindings, rgBindings,
pCurData, rgDispSize));
}
// 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:
FreeBindings(cBindings, rgBindings);
CoTaskMemFree(rgDispSize);
CoTaskMemFree(pData);
if( pIRowset )
pIRowset->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// myUpdateDisplaySize
// Summary of Routines
//
// This function updates the rgDispSize array, keeping the
// maximum of the display size needed for the given data and
// the previous maximum size already in the array.
//
/////////////////////////////////////////////////////////////////
HRESULT UpdateDisplaySize(ULONG cBindings,
DBBINDING * rgBindings,
void * pData,
ULONG * rgDispSize )
{
DBSTATUS dwStatus;
ULONG cchLength;
ULONG iCol;
// Loop through the bindings, comparing the size of each column
// against the previously found maximum size for that column.
for( iCol = 0; iCol < cBindings; iCol++ )
{
dwStatus = *(DBSTATUS *)((BYTE *)pData + rgBindings[iCol].obStatus);
cchLength = ((*(ULONG *)((BYTE *)pData + rgBindings[iCol].obLength))
/ sizeof(WCHAR));
// The length that we need to display depends on the status
// of this column and generally on the data in the column.
switch( dwStatus )
{
case DBSTATUS_S_ISNULL:
cchLength = 6; // "(null)"
break;
case DBSTATUS_S_TRUNCATED:
case DBSTATUS_S_OK:
case DBSTATUS_S_DEFAULT:
if( rgBindings[iCol].wType == DBTYPE_IUNKNOWN )
cchLength = 2 + 8; // "0x%08lx"
// Ensure that the length is at least the minimum
// display size.
cchLength = max(cchLength, MIN_DISPLAY_SIZE);
break;
default:
cchLength = 14; // "(error status)"
break;
}
if( rgDispSize[iCol] < cchLength )
rgDispSize[iCol] = cchLength;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////
// 函数: FindColumn
//
// 查找指定(pwszName)列的索引.
//
/////////////////////////////////////////////////////////////////
HRESULT FindColumn(IUnknown * pUnkRowset,
LPCWSTR pwszName,
LONG * plIndex )
{
HRESULT hr;
IColumnsInfo * pIColumnsInfo = NULL;
ULONG cColumns;
DBCOLUMNINFO * rgColumnInfo = NULL;
OLECHAR * pStringsBuffer = NULL;
ULONG iCol;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -