📄 s15_03.cpp
字号:
//
// 创建一个OLE DB会话对象(从指定的数据源对象).
// 对于数据源对象而言, IDBCreateSession接口是强制的.
//
/////////////////////////////////////////////////////////////////
HRESULT CreateSession(IUnknown *pUnkDataSource,
IUnknown **ppUnkSession)
{
HRESULT hr;
IDBCreateSession * pIDBCreateSession = NULL;
//由数据源对象创建一个会话对象.
XCHECK_HR(hr = pUnkDataSource->QueryInterface(
IID_IDBCreateSession, (void**)&pIDBCreateSession));
XCHECK_HR(hr = pIDBCreateSession->CreateSession(
NULL, // pUnkOuter
IID_IOpenRowset, // riid
ppUnkSession )); // ppSession
CLEANUP:
if( pIDBCreateSession )
pIDBCreateSession->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// 函数: CreateRowset
//
// 这个函数从指定的OLE DB会放对象创建一个行集对象.
// - 这个函数让用户指定一个表名, 调用IOpenRowset::OpenRowset
// 来创建一个行集对象.
//
/////////////////////////////////////////////////////////////////
HRESULT CreateRowset(IUnknown *pUnkSession,
IUnknown **ppUnkRowset)
{
HRESULT hr;
IUnknown * pUnkCommand = NULL;
IOpenRowset * pIOpenRowset = NULL;
WCHAR wszTableName[MAX_NAME_LEN + 1] = {0};
const ULONG cProperties = 2;
DBPROP rgProperties[cProperties];
DBPROPSET rgPropSets[1];
// Obtain a default table name from the user by displaying the
// tables schema rowset if schema rowsets are supported.
// CHECK_HR(hr = CreateSchemaRowset(DBSCHEMA_TABLES, pUnkSession,
// MAX_NAME_LEN, wszTableName));
// Set properties on the rowset, to request additional functionality.
AddRowsetProperties(rgPropSets, cProperties, rgProperties);
DBID TableID;
// 创建表ID.
TableID.eKind = DBKIND_NAME;
TableID.uName.pwszName = wszTableName;
// 由用户指定打开的表名.
GetInputFromUser(wszTableName, L"\nType the name of the table to use: ");
// 获取IOpenRowset接口, 并创建一个指定表的行集.
XCHECK_HR(hr = pUnkSession->QueryInterface(
IID_IOpenRowset, (void**)&pIOpenRowset));
XCHECK_HR(hr = pIOpenRowset->OpenRowset(
NULL, // pUnkOuter
&TableID, // pTableID
NULL, // pIndexID
IID_IRowset, // riid
1, // cPropSets
rgPropSets, // rgPropSets
ppUnkRowset)); // ppRowset
CLEANUP:
if( pIOpenRowset ) pIOpenRowset->Release();
if( pUnkCommand ) pUnkCommand->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// 函数: CreateSchemaRowset
//
// 如果提供程序支持IDBSchemaRowset, 则这个函数将获取表的架构行集.
// 并将这个行集显示给用户, 可以让用户在这个行集中选择一个感兴趣的表.
//
/////////////////////////////////////////////////////////////////
HRESULT CreateSchemaRowset(GUID guidSchema,
IUnknown *pUnkSession,
ULONG cchBuffer,
LPWSTR pwszBuffer)
{
HRESULT hr = S_OK;
IDBSchemaRowset * pIDBSchemaRowset = NULL;
IUnknown * pUnkRowset = NULL;
const ULONG cProperties = 2;
DBPROP rgProperties[cProperties];
DBPROPSET rgPropSets[1];
// 试图获取会话对象的IDBSchemaRowset接口.
// 对会话对象而言, 接口IDBSchemaRowset不是强制的.
CHECK_HR(pUnkSession->QueryInterface(
IID_IDBSchemaRowset, (void**)&pIDBSchemaRowset));
// 设置行集的属性, 以请求更多的功能.
AddRowsetProperties(rgPropSets, cProperties, rgProperties);
// 如果支持IDBSchemaRowset接口, 则获取要求的架构行集.
// 这个架构行集要求支持:
// DBSCHEMA_TABLES, DBSCHEMA_COLUMNS和DBSCHEMA_PROVIDERTYPES.
XCHECK_HR(hr = pIDBSchemaRowset->GetRowset(
NULL, // pUnkOuter
guidSchema, // guidSchema
0, // cRestrictions
NULL, // rgRestrictions
IID_IRowset, // riid
1, // cPropSets
rgPropSets, // rgPropSets
&pUnkRowset // ppRowset
));
// 将这个架构行集的表名(TABLE_NAME字段)显示给用户.
// 请见S15_05中的函数myDisplayRowset.
// CHECK_HR(hr = DisplayRowset(pUnkRowset,
// L"TABLE_NAME", cchBuffer, pwszBuffer));
CLEANUP:
if( pIDBSchemaRowset )
pIDBSchemaRowset->Release();
if( pUnkRowset )
pUnkRowset->Release();
return hr;
}
/////////////////////////////////////////////////////////////////
// 函数: GetInputFromUser
//
// 这个函数提示(通过pwszFmt参数)用户输入一个字符串.
// 如果这个字符串不为空, 则将它拷贝到参数pwszInput中, 并返回TRUE;
// 否则返回FALSE.
//
/////////////////////////////////////////////////////////////////
BOOL GetInputFromUser(LPWSTR pwszInput,
LPCWSTR pwszFmt,
... )
{
va_list vargs;
WCHAR wszBuffer[MAX_NAME_LEN + 1] = {0};
// Create the string with variable arguments...
va_start(vargs, pwszFmt);
_vsnwprintf(wszBuffer, MAX_NAME_LEN, pwszFmt, vargs);
va_end(vargs);
// Output the string...
wprintf(wszBuffer);
// Now get the Input from the user....
_getws(wszBuffer);
if( wszBuffer[0] )
{
wcscpy(pwszInput, wszBuffer);
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////
// 函数: AddRowsetProperties
//
// 这个函数设置DBPROPSET和DBPROP结构, 并添加两个可选属性.
// - DBPROP_CANFETCHBACKWARDS
// - DBPROP_IRowsetLocate
//
/////////////////////////////////////////////////////////////////
void AddRowsetProperties(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.
AddProperty(&rgProperties[0], DBPROP_CANFETCHBACKWARDS);
AddProperty(&rgProperties[1], DBPROP_IRowsetLocate);
}
/////////////////////////////////////////////////////////////////
// 函数: CreateAccessor
//
// 这个函数从一个行集对象的IUnknown接口指针, 创建一个访问器对象;
// 描述我们在获取数据时使用的缓冲区的布局.
// 提供程序将按照这个访问器的描述来填充数据缓冲区.
//
/////////////////////////////////////////////////////////////////
HRESULT CreateAccessor(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 = SetupBindings(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.
// See IAccessor
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;
}
/////////////////////////////////////////////////////////////////
// 函数: SetupBindings
//
// 这个函数从一个行集对象的IUnknown指针创建一个绑定数组.
// 这个绑定数组描述了我们想怎样将从行集获取的数据放置在缓冲区中.
// 同时, 计算了每行的总的大小. 在后面可以使用它来分配存储空间.
//
// For each column in the rowset, there will be a corresponding
// element in the bindings array that describes how the
// provider should transfer the data, including length and
// status, for that column. This element also specifies the data
// type that the provider should return the column as. We will
// bind all columns as DBTYPE_WSTR, with a few exceptions
// detailed below, as providers are required to support the
// conversion of their column data to this type in the vast
// majority of cases. The exception to our binding as
// DBTYPE_WSTR is if the native column data type is
// DBTYPE_IUNKNOWN or if the user has requested that BLOB
// columns be bound as ISequentialStream objects, in which case
// we will bind those columns as ISequentialStream objects.
//
/////////////////////////////////////////////////////////////////
HRESULT SetupBindings(IUnknown * pUnkRowset,
ULONG * pcBindings,
DBBINDING ** prgBindings,
ULONG * pcbRowSize )
{
HRESULT hr;
ULONG cColumns;
DBCOLUMNINFO * rgColumnInfo = NULL;
LPWSTR pStringBuffer = NULL;
IColumnsInfo * pIColumnsInfo = NULL;
ULONG iCol;
ULONG dwOffset = 0;
DBBINDING * rgBindings = NULL;
ULONG cStorageObjs = 0;
BOOL fMultipleObjs = FALSE;
// 获取行集的列信息.
// 由此, 我们可以找到构造绑定数组的信息:
// - the number of columns
// - the ordinal of each column
// - the precision and scale of numeric columns
// - the OLE DB data type of the column
XCHECK_HR(hr = pUnkRowset->QueryInterface(
IID_IColumnsInfo, (void**)&pIColumnsInfo));
XCHECK_HR(hr = pIColumnsInfo->GetColumnInfo(
&cColumns, // pcColumns
&rgColumnInfo, // prgColumnInfo
&pStringBuffer // ppStringBuffer
));
// 分配绑定数组的存储空间.
// 从GetColumnInfo返回的列与绑定之间是一对一的关系.
rgBindings = (DBBINDING*)CoTaskMemAlloc(cColumns * sizeof(DBBINDING));
CHECK_MEMORY(hr, rgBindings);
memset(rgBindings, 0, cColumns * sizeof(DBBINDING));
// 确定行集是否支持多存储对象(multiple storage object)绑定.
// 如果不支持, 我们将只绑定第一个BLOB列, 或作为ISequentialStream对象绑定;
// 后面的列将作为DBTYPE_WSTR来绑定.
GetProperty(pUnkRowset, IID_IRowset, DBPROP_MULTIPLESTORAGEOBJECTS,
DBPROPSET_ROWSET, &fMultipleObjs);
// 为每列构造绑定数组元素.
for( iCol = 0; iCol < cColumns; iCol++ )
{
// 列的顺序.
rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;
// 要求提供程序提供列的值, 长度和状态信息.
rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
// 指定绑定部分的地址偏移量.
rgBindings[iCol].obStatus = dwOffset;
rgBindings[iCol].obLength = dwOffset + sizeof(DBSTATUS);
rgBindings[iCol].obValue = dwOffset + sizeof(DBSTATUS) + sizeof(ULONG);
// 指定缓冲区的释放者. 这里指由我们自己负责.
rgBindings[iCol].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
// 指定不是参数绑定.
rgBindings[iCol].eParamIO = DBPARAMIO_NOTPARAM;
// 我们想使用的列(数值)的精度和有效数字.
rgBindings[iCol].bPrecision = rgColumnInfo[iCol].bPrecision;
rgBindings[iCol].bScale = rgColumnInfo[iCol].bScale;
// 作为DBTYPE_WSTR类型来绑定.
// 这将使提供程序将Unicode字符串拷贝到我们自己的缓冲区中.
// 这是字符转换所需要的.
rgBindings[iCol].wType = DBTYPE_WSTR;
// 先将数据的长度设为0. 正确的长度值在后面再进行计算.
rgBindings[iCol].cbMaxLen = 0;
// 确定在缓冲区中容纳Unicode字符串(提供程序的本地数据类型)
// 所需内存的最大字节数. 包括空结束符.
switch( rgColumnInfo[iCol].wType )
{
case DBTYPE_NULL:
case DBTYPE_EMPTY:
case DBTYPE_I1:
case DBTYPE_I2:
case DBTYPE_I4:
case DBTYPE_UI1:
case DBTYPE_UI2:
case DBTYPE_UI4:
case DBTYPE_R4:
case DBTYPE_BOOL:
case DBTYPE_I8:
case DBTYPE_UI8:
case DBTYPE_R8:
case DBTYPE_CY:
case DBTYPE_ERROR:
// When the above types are converted to a string, they
// will all fit into 25 characters, so use that plus space
// for the NULL terminator.
rgBindings[iCol].cbMaxLen = (25 + 1) * sizeof(WCHAR);
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -