Binding and Accessor Example

OLE DB Programmer's Reference

The code in this example shows how to set up bindings and use them to create an accessor.

///////////////////////////////////////////////////////////////// // 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 whether 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 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 )       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; }

1998-2001 Microsoft Corporation. All rights reserved.



Microsoft Ole Db 2.0 Programmer's Reference and Data Access SDK
Microsoft OLE DB 2.0 Programmers Reference and Data Access SDK (Microsoft Professional Editions)
ISBN: 0735605904
EAN: 2147483647
Year: 1998
Pages: 1083

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net