⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rowset.cpp

📁 oledb的例子
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//---------------------------------------------------------------------------
// Microsoft OLE DB Programmer's Reference Sample
// Copyright (C) 1998 By Microsoft Corporation.
//
// @doc
//
// @module ROWSET.CPP
//
//---------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////
// Includes
//
/////////////////////////////////////////////////////////////////
#include "prsample.h"         // Programmer's Reference Sample includes
/////////////////////////////////////////////////////////////////
// myCreateRowset
//
// This function creates an OLE DB rowset object from the given
// provider's session object. It first obtains a default table
// name from the user through the tables schema rowset, if
// supported, and then creates a rowset object by one of two methods:
//
// - If the user requested that the rowset object be created
//   from a command object, it creates a command object and then
//   obtains command text from the user, sets properties and
//   the command text, and finally executes the command to
//   create the rowset object.
// - Otherwise, the function obtains a table name from the user
//   and calls IOpenRowset::OpenRowset to create a rowset object
//   over that table that supports the requested properties.
//
/////////////////////////////////////////////////////////////////
HRESULTmyCreateRowset(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 = myCreateSchemaRowset(DBSCHEMA_TABLES, pUnkSession, MAX_NAME_LEN, wszTableName));

	// Set properties on the rowset, to request additional functionality
	myAddRowsetProperties(rgPropSets, cProperties, rgProperties);

	// If the user requested that the rowset be created from a
	// Ccommand object, create a command, set its properties and
	// text, and execute it to create the rowset object
	if( g_dwFlags & USE_COMMAND )
	{
		WCHAR	wszCommandText[MAX_NAME_LEN + 1];

		// Attempt to create the command object from the provider's
		// session object. Note that commands are not supported by
		// all providers, and this will fail in that case.
		CHECK_HR(hr = myCreateCommand(pUnkSession, &pUnkCommand));

		// From the user, get the command text that we will execute
		if( !myGetInputFromUser(wszCommandText, L"\nType the command "
						                L"to execute [Enter = `select * from %s`]: ", wszTableName) )
		{
			swprintf(wszCommandText, L"select * from %s", wszTableName);
		}

		// And execute the command the user entered
		CHECK_HR(hr = myExecuteCommand(pUnkCommand, wszCommandText, 1, rgPropSets, ppUnkRowset));
	}

	// Otherwise, the user gets the default behavior, which is to use
	// IOpenRowset to create the rowset object from the session object.
	// IOpenRowset is supported by all providers; it takes a TableID
	// and creates a rowset containing all rows in that table. It is
	// similar to using SQL command text of "SELECT * FROM TableID".
	else
	{
		DBID TableID;
		// Create the TableID
		TableID.eKind            = DBKIND_NAME;
		TableID.uName.pwszName   = wszTableName;

		// Obtain the table name from the user
		myGetInputFromUser(wszTableName, L"\nType the name of the table "
			                 L"to use [Enter = `%s`]: ", wszTableName);

		// Get the IOpenRowset interface, and create a rowset object
		// over the requested table through OpenRowset
		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;

}

/////////////////////////////////////////////////////////////////
// mySetupBindings
//
// This function takes an IUnknown pointer from a rowset object
// and creates a bindings array that describes how we want the
// data we fetch from the rowset to be laid out in memory. It
// also calculates the total size of a row so that we can use
// this to allocate memory for the rows that we will fetch
// later.
//
// 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 mySetupBindings(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;

	// Obtain the column information for the rowset; from this, we can 
	// find out the following information that we need to construct the 
	// bindings array:
	// - 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
									));

	// Allocate memory for the bindings array; there is a one-to-one
	// mapping between the columns returned from GetColumnInfo and our
	// bindings
	rgBindings = (DBBINDING*)CoTaskMemAlloc(cColumns * sizeof(DBBINDING));
	CHECK_MEMORY(hr, rgBindings);
	memset(rgBindings, 0, cColumns * sizeof(DBBINDING));

	// Determine if the rowset supports multiple storage object bindings.
	// If it does not, we will bind only the first BLOB column or IUnknown
	// column as an ISequentialStream object, and we will bind the rest as
	// DBTYPE_WSTR.

	myGetProperty(pUnkRowset, IID_IRowset, DBPROP_MULTIPLESTORAGEOBJECTS, DBPROPSET_ROWSET, &fMultipleObjs);

	// Construct the binding array element for each column
	for( iCol = 0; iCol < cColumns; iCol++ )
	{
		// This binding applies to the ordinal of this column
		rgBindings[iCol].iOrdinal = rgColumnInfo[iCol].iOrdinal;
		
		// We are asking the provider to give us the data for this column
		// (DBPART_VALUE), the length of that data (DBPART_LENGTH), and
		// the status of the column (DBPART_STATUS)
		rgBindings[iCol].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;

		// The following values are the offsets to the status, length, and
		// data value that the provider will fill with the appropriate 
		// values when we fetch data later. When we fetch data, we will 
		// pass a pointer to a buffer that the provider will copy column 
		// data to, in accordance with the binding we have provided for 
		// that column; these are offsets into that future buffer.
		rgBindings[iCol].obStatus   = dwOffset;
		rgBindings[iCol].obLength   = dwOffset + sizeof(DBSTATUS);
		rgBindings[iCol].obValue    = dwOffset + sizeof(DBSTATUS) + sizeof(ULONG);

		// Any memory allocated for the data value will be owned by us, the
		// client. Note that no data will be allocated in this case, as the
		// DBTYPE_WSTR bindings we are using will tell the provider to 
		// simply copy data directly into our provided buffer.
		rgBindings[iCol].dwMemOwner= DBMEMOWNER_CLIENTOWNED;

		// This is not a parameter binding
		rgBindings[iCol].eParamIO= DBPARAMIO_NOTPARAM;

		// We want to use the precision and scale of the column
		rgBindings[iCol].bPrecision   = rgColumnInfo[iCol].bPrecision;
		rgBindings[iCol].bScale       = rgColumnInfo[iCol].bScale;

		// Bind this column as DBTYPE_WSTR, which tells the provider to
		// copy a Unicode string representation of the data into our 
		// buffer, converting from the native type if necessary
		rgBindings[iCol].wType = DBTYPE_WSTR;

		// Initially, we set the length for this data in our buffer to 0;
		// the correct value for this will be calculated directly below
		rgBindings[iCol].cbMaxLen = 0;

		// Determine the maximum number of bytes required in our buffer to
		// contain the Unicode string representation of the provider's 
		// native data type, including room for the NULL-termination 
		// character
		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;
			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 )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -