📄 rowset.cpp
字号:
}
/////////////////////////////////////////////////////////////////
// myDisplayColumnNames
//
// This function takes an IUnknown pointer to a rowset object
// and displays the names of the columns of that rowset
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayColumnNames( IUnknown * pUnkRowset,
ULONG * rgDispSize
)
{
HRESULT hr;
IColumnsInfo * pIColumnsInfo = NULL;
ULONG cColumns;
DBCOLUMNINFO * rgColumnInfo = NULL;
LPOLESTR pStringsBuffer = NULL;
WCHAR wszColumn[MAX_DISPLAY_SIZE + 1];
LPWSTR pwszColName;
ULONG iCol;
ULONG cSpaces;
ULONG iSpace;
// Get the IColumnsInfo interface for the rowset
XCHECK_HR(hr = pUnkRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo));
// Get the columns information
XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
&cColumns, // pcColumns
&rgColumnInfo, // prgColumnInfo
&pStringsBuffer // ppStringBuffer
));
// Display the title of the row index column
wprintf(L" Row | ");
// Display all column names
for( iCol = 0; iCol < cColumns; iCol++ )
{
pwszColName = rgColumnInfo[iCol].pwszName;
// If the column name is NULL, we'll use a default string
if( !pwszColName )
{
// Is this the bookmark column?
if( !rgColumnInfo[iCol].iOrdinal )
pwszColName = L"Bmk";
else
pwszColName = L"(null)";
}
// Ensure that the name is no longer than MAX_DISPLAY_SIZE
wcsncpy(wszColumn, pwszColName, MAX_DISPLAY_SIZE);
wszColumn[min(rgDispSize[iCol], MAX_DISPLAY_SIZE)] = L'\0';
// Figure out how many spaces we need to print after
// this column name
cSpaces = min(rgDispSize[iCol], MAX_DISPLAY_SIZE) - wcslen(wszColumn);
// Print the column name
wprintf(L"%s", wszColumn);
// Now print any spaces necessary to align this column
for(iSpace = 0; iSpace < cSpaces; iSpace++ )
putch(' ');
// Now end the column with a separator marker if necessary
if( iCol < cColumns - 1 )
wprintf(L" | ");
}
// Done with the header, so print a newline
wprintf(L"\n");
CLEANUP:
CoTaskMemFree(rgColumnInfo);
CoTaskMemFree(pStringsBuffer);
if( pIColumnsInfo )
pIColumnsInfo->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// myDisplayRow
//
// This function displays the data for a row
//
/////////////////////////////////////////////////////////////////
HRESULT myDisplayRow(
ULONG iRow,
ULONG cBindings,
DBBINDING * rgBindings,
void * pData,
ULONG * rgDispSize
)
{
HRESULT hr = S_OK;
WCHAR wszColumn[MAX_DISPLAY_SIZE + 1];
DBSTATUS dwStatus;
ULONG ulLength;
void * pvValue;
ULONG iCol;
ULONG cbRead;
ISequentialStream * pISeqStream = NULL;
ULONG cSpaces;
ULONG iSpace;
// Display the row number
wprintf(L" [%d] | ", iRow);
// For each column that we have bound, display the data
for( iCol = 0; iCol < cBindings; iCol++ )
{
// We have bound status, length, and the data value for all
// columns, so we know that these can all be used
dwStatus = *(DBSTATUS *)((BYTE *)pData + rgBindings[iCol].obStatus);
ulLength = *(ULONG *)((BYTE *)pData + rgBindings[iCol].obLength);
pvValue = (BYTE *)pData + rgBindings[iCol].obValue;
// Check the status of this column. This decides
// exactly what will be displayed for the column.
switch( dwStatus )
{
// The data is NULL, so don't try to display it
case DBSTATUS_S_ISNULL:
wcscpy(wszColumn, L"(null)");
break;
// The data was fetched, but may have been truncated.
// Display string data for this column to the user.
case DBSTATUS_S_TRUNCATED:
case DBSTATUS_S_OK:
case DBSTATUS_S_DEFAULT:
{
// We have bound the column either as a Unicode string
// (DBTYPE_WSTR) or as an ISequentialStream object
// (DBTYPE_IUNKNOWN), and we have to do different processing
// for each one of these possibilities
switch( rgBindings[iCol].wType )
{
case DBTYPE_WSTR:
{
// Copy the string data
wcsncpy(wszColumn, (WCHAR *)pvValue, MAX_DISPLAY_SIZE);
wszColumn[MAX_DISPLAY_SIZE - 1] = L'\0';
break;
}
case DBTYPE_IUNKNOWN:
{
// We've bound this as an ISequentialStream object,
// therefore the data in our buffer is a pointer
// to the object's ISequentialStream interface
pISeqStream = *(ISequentialStream**)pvValue;
// We call ISequentialStream::Read to read bytes from
// the stream blindly into our buffer, simply as a
// demonstration of ISequentialStream. To display the
// data properly, the native provider type of this
// column should be accounted for; it could be
// DBTYPE_WSTR, in which case this works, or it could
// be DBTYPE_STR or DBTYPE_BYTES, in which case this
// won't display the data correctly.
CHECK_HR(hr = pISeqStream->Read(
wszColumn, // pBuffer
MAX_DISPLAY_SIZE, // cBytes
&cbRead // pcBytesRead
));
// Since streams don't provide NULL-termination,
// we'll NULL-terminate the resulting string ourselves
wszColumn[cbRead / sizeof(WCHAR)] = L'\0';
// Release the stream object, now that we're done
pISeqStream->Release();
pISeqStream = NULL;
break;
}
}
break;
}
// This is an error status, so don't try to display the data
default:
wcscpy(wszColumn, L"(error status)");
break;
}
// Determine how many spaces we need to add after displaying this
// data to align it with this column in other rows
cSpaces = min(rgDispSize[iCol], MAX_DISPLAY_SIZE) - wcslen(wszColumn);
// Print the column data
wprintf(L"%s", wszColumn);
// Now print any spaces necessary
for(iSpace = 0; iSpace < cSpaces; iSpace++ )
putch(' ');
// Now end the column with a separator marker if necessary
if( iCol < cBindings - 1 )
wprintf(L" | ");
}
CLEANUP:
if( pISeqStream )
pISeqStream->Release();
// Print the row separator
wprintf(L"\n");
return hr;
}
/////////////////////////////////////////////////////////////////
// myFreeBindings
//
//This function frees a bindings array and any allocated
// structures contained in that array
//
/////////////////////////////////////////////////////////////////
void myFreeBindings(
ULONG cBindings,
DBBINDING * rgBindings
)
{
ULONG iBind;
// Free any memory used by DBOBJECT structures in the array
for( iBind = 0; iBind < cBindings; iBind++ )
CoTaskMemFree(rgBindings[iBind].pObject);
// Now free the bindings array itself
CoTaskMemFree(rgBindings);
}
/////////////////////////////////////////////////////////////////
// myAddRowsetProperties
//
// This function sets up the given DBPROPSET and DBPROP
// structures, adding two optional properties that describe
// features that we would like to use on the rowset created
// with these properties applied:
// - DBPROP_CANFETCHBACKWARDS -- the rowset should support
// fetching rows backwards from our current cursor position.
// - DBPROP_IRowsetLocate -- the rowset should support
// the IRowsetLocate interface and its semantics.
//
/////////////////////////////////////////////////////////////////
void myAddRowsetProperties(DBPROPSET* pPropSet, ULONG cProperties, DBPROP* rgProperties)
{
// Initialize the property set array
pPropSet->rgProperties = rgProperties;
pPropSet->cProperties = cProperties;
pPropSet->guidPropertySet = DBPROPSET_ROWSET;
// Add the following two properties (as OPTIONAL) to the property
// array contained in the property set array in order to request
// that they be supported by the rowset we will create. Because
// these are optional, the rowset we obtain may or may not support
// this functionality. We will check for the functionality that
// we need once the rowset is created and will modify our behavior
// appropriately.
myAddProperty(&rgProperties[0], DBPROP_CANFETCHBACKWARDS);
myAddProperty(&rgProperties[1], DBPROP_IRowsetLocate);
}
/////////////////////////////////////////////////////////////////
// myUpdateDisplaySize
//
// 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 myUpdateDisplaySize(
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;
}
/////////////////////////////////////////////////////////////////
// myFindColumn
//
// Find the index of the column described in pwszName, and return
// S_OK or, if not found, S_FALSE
//
/////////////////////////////////////////////////////////////////
HRESULT myFindColumn(
IUnknown * pUnkRowset,
LPCWSTR pwszName,
LONG * plIndex
)
{
HRESULT hr;
IColumnsInfo * pIColumnsInfo = NULL;
ULONG cColumns;
DBCOLUMNINFO * rgColumnInfo = NULL;
OLECHAR * pStringsBuffer = NULL;
ULONG iCol;
// Get the IColumnsInfo interface
XCHECK_HR(hr = pUnkRowset->QueryInterface(
IID_IColumnsInfo, (void**)&pIColumnsInfo));
// Get the columns information
XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
&cColumns, // pcColumns
&rgColumnInfo, // prgColumnInfo
&pStringsBuffer // ppStringBuffer
));
// Assume that we'll find the column
hr = S_OK;
// Search for the column we need
for( iCol = 0; iCol < cColumns; iCol++ )
{
// If the column name matches, we've found the column...
if( rgColumnInfo[iCol].pwszName && !wcscmp(pwszName, rgColumnInfo[iCol].pwszName) )
{
*plIndex = iCol;
goto CLEANUP;
}
}
// If we didn't find the column, we'll return S_FALSE
hr = S_FALSE;
CLEANUP:
CoTaskMemFree(rgColumnInfo);
CoTaskMemFree(pStringsBuffer);
if( pIColumnsInfo )
pIColumnsInfo->Release();
return hr;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -