Appendix F: Sample OLE DB Consumer Application

OLE DB Programmer's Reference

This sample demonstrates the rowset programming and object model for an OLE DB consumer. It creates data source, session, and rowset objects; allows the user to display and navigate the rows in the rowset; and handles errors, if any. Command line switches determine whether an enumerator, class ID, user prompt, or connection string is used to create the data source object, whether or not a command is used to create the rowset, and so on.

Sample OLE DB Consumer Application Source

Source files for the Sample OLE DB Consumer Application are located in the PRSample directory where the OLE DB samples are installed, and consist of the following files:

Source and Project Files

command.cpp prsample.h
datasource.cpp prsample.mak
enum.cpp prsample.rc
error.cpp resource.h
main.cpp rowset.cpp
prsample.dsp session.cpp

Summary of Routines

The table below shows the structure of the routines in the sample. The # column is the ordinal number of the routine named in the Routine column; the Routine column gives a brief description of what the routine does. Some routines contain subroutines, which are shown indented within their containing routine. The ordinal number of the called subroutine is listed in the Calls column. Click any routine name in this table to display the actual routine.

Within a routine, click the "Summary of Routines" link in the block comment to display the following table. Click any linked interface name to display the interface topic in the OLE DB Programmer's Reference. Click any subroutine name to display the actual routine.

# Calls Routine
0   Preprocessor statements and prototypes—Necessary preprocessor definitions and declarations.
1   main—Begin the sample consumer application.
  2
myParseCommandLine
  3
myDisplayInstructions
  7
myCreateDataSource
  11
myCreateSession
  13
myCreateRowset
  16
myDisplayRowset
2   myParseCommandLine—Parses command line switches that control whether the sample creates the data source object with the Data Link UI or uses an enumerator and connection string; whether or not a command creates the rowset; whether to fetch a BLOB; and whether to display method call strings when reporting errors.
3   myDisplayInstructions—Displays instructions on how to use the sample based on the command line switches.
  5
myGetChar
4   myGetInputFromUser—Prompts the user for variable input, such as command text.
5   myGetChar—Gets a character from the keyboard.
6   myCreateEnumerator—Enumerates the providers on the user's machine and allows the user to select one.
  8
myDoInitialization
  21
myAddRowsetProperties
  16
myDisplayRowset
  4
myGetInputFromUser
7   myCreateDataSource—Creates a data source object, based on the user's previous specifications.
  6
myCreateEnumerator
  8
myDoInitialization
8   myDoInitialization—Sets the provider initialization properties and then initializes the data source object.
  10
myAddProperty
9   myGetProperty—Gets the value of the specified property.
10   myAddProperty—Adds a property to an array of properties.
11   myCreateSession—Creates a session object from a data source object.
12   myCreateSchemaRowset—Obtains the names of, and allows the user to select, tables supported by a provider from a table schema rowset.
  21
myAddRowsetProperties
  16
myDisplayRowset
13   myCreateRowset—Creates a rowset from a default table name, command, or user-specified table name.
  12
myCreateSchemaRowset
  21
myAddRowsetProperties
  24
myCreateCommand
  4
myGetInputFromUser
  25
myExecuteCommand
14   mySetupBindings—Uses the columns of the obtained rowset to specify an array of bindings. Bindings specify how data in columns should optionally be converted and stored in user variables.
  9
myGetProperty
15   myCreateAccessor—Creates an accessor, which specifies the bindings to use to fetch data from the rowset.
  14
mySetupBindings
16   myDisplayRowset—Displays data from a rowset and performs basic rowset navigation.
  9
myGetProperty
  23
myFindColumn
  15
myCreateAccessor
  22
myUpdateDisplaySize
  18
myDisplayColumnNames
  19
myDisplayRow
  17
myInteractWithRowset
  20
myFreeBindings
17   myInteractWithRowset—Prompts the user for rowset navigation directives (next, previous, and restart); allows the user to select a row; exits the sample program upon request.
  5
myGetChar
18   myDisplayColumnNames—Displays rowset column names.
19   myDisplayRow—Displays the data in a row of the rowset.
20   myFreeBindings—Frees memory allocated for bindings.
21   myAddRowsetProperties—Specifies optional properties that identify features the rowset should support.
  10
myAddProperty
22   myUpdateDisplaySize—Manages the size of the column displayed.
23   myFindColumn—Find the specified index column.
24   myCreateCommand—Creates a command object, if possible.
25   myExecuteCommand—Sets properties on a command object, executes the command, and returns a rowset object.
26   myHandleResult—Handles and displays error information.
  27
myDisplayErrorRecord
  28
myDisplayErrorInfo
27   myDisplayErrorRecord—Displays error information for a single error record.
  29
myGetSqlErrorInfo
28   myDisplayErrorInfo—Displays basic error information for an error object that doesn't support error records.
29   myGetSqlErrorInfo—Displays, if possible, SQL and native error information.

Sample OLE DB Consumer Application Routines

//--------------------------------------------------------------------------- // Microsoft® OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft® Corporation. // // @doc // // @module PRSAMPLE.H // //--------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////
// Includes // Summary of Routines // ///////////////////////////////////////////////////////////////// #ifndef __PRSAMPLE_H__ #define __PRSAMPLE_H__ #include "oledb.h"         // OLE DB Header #include "oledberr.h"      // OLE DB Errors #include "msdasc.h"        // OLE DB Service Component header #include "msdaguid.h"      // OLE DB Root Enumerator #include "msdasql.h"       // MSDASQL - Default provider #include <stdio.h>         // input and output functions #include <conio.h>         // getch, putch #include <locale.h>        // setlocale ///////////////////////////////////////////////////////////////// // Globals // ///////////////////////////////////////////////////////////////// extern DWORD g_dwFlags; ///////////////////////////////////////////////////////////////// // Defines // ///////////////////////////////////////////////////////////////// #define __LONGSTRING(string) L##string #define LONGSTRING(string) __LONGSTRING(string) // Goes to CLEANUP on Failure_ #define CHECK_HR(hr)      \    if(FAILED(hr))         \       goto CLEANUP // Goes to CLEANUP on Failure, and displays any ErrorInfo #define XCHECK_HR(hr)                                               \ {                                                                   \    if( g_dwFlags & DISPLAY_METHODCALLS )                            \       fwprintf(stderr, LONGSTRING(#hr) L"\n");                      \    if(FAILED(myHandleResult(hr, LONGSTRING(__FILE__), __LINE__)))   \       goto CLEANUP;                                                 \ } #define CHECK_MEMORY(hr, pv)   \ {                              \    if(!pv)                     \    {                           \       hr = E_OUTOFMEMORY;      \       CHECK_HR(hr);            \    }                           \ } #define MAX_COL_SIZE         5000 #define MAX_NAME_LEN         256 #define MAX_ROWS              10 #define MAX_DISPLAY_SIZE      20 #define MIN_DISPLAY_SIZE      3 // ROUNDUP on all platforms pointers must be aligned properly #define ROUNDUP_AMOUNT            8 #define ROUNDUP_(size,amount) (((ULONG)(size)+((amount)-1))&~((amount)-1)) #define ROUNDUP(size)             ROUNDUP_(size, ROUNDUP_AMOUNT) enum {    // Connecting    USE_PROMPTDATASOURCE      = 0x0001,    USE_ENUMERATOR            = 0x0002,    // Rowset    USE_COMMAND               = 0x0010,    // Storage objects    USE_ISEQSTREAM            = 0x0100,    // Display options    DISPLAY_METHODCALLS       = 0x1000,    DISPLAY_INSTRUCTIONS      = 0x2000, }; ///////////////////////////////////////////////////////////////////////////// // Function prototypes // ///////////////////////////////////////////////////////////////////////////// // Main BOOL   myParseCommandLine(); void   myDisplayInstructions(); BOOL   myGetInputFromUser(LPWSTR pwszInput, LPCWSTR pwszFmt, ...); CHAR   myGetChar(); // Enumerator HRESULT myCreateEnumerator(REFCLSID clsidEnumerator, CLSID* pCLSID); // Data source HRESULT myCreateDataSource(IUnknown** ppUnkDataSource); HRESULT myDoInitialization(IUnknown* pIUnknown); HRESULT myGetProperty(IUnknown* pIUnknown, REFIID riid, DBPROPID                        dwPropertyID, REFGUID guidPropertySet, BOOL*                        pbValue); void myAddProperty(DBPROP* pProp, DBPROPID dwPropertyID, VARTYPE vtType =                     VT_BOOL, LONG lValue = VARIANT_TRUE, DBPROPOPTIONS                     dwOptions =    DBPROPOPTIONS_OPTIONAL); // Session HRESULT myCreateSession(IUnknown* pUnkDataSource, IUnknown**                          ppUnkSession); HRESULT myCreateSchemaRowset(GUID guidSchema, IUnknown* pUnkSession,                              ULONG cchBuffer, LPWSTR pwszBuffer); // Command HRESULT myCreateCommand(IUnknown* pUnkSession, IUnknown** ppUnkCommand); HRESULT myExecuteCommand(IUnknown* pUnkCommand, WCHAR* pwszCommandText,                           ULONG cPropSets, DBPROPSET* rgPropSets,                           IUnknown** ppUnkRowset); // Rowset HRESULT myCreateRowset(IUnknown* pUnkSession, IUnknown** ppUnkRowset); HRESULT mySetupBindings(IUnknown* pUnkRowset, ULONG* pcBindings,                          DBBINDING** prgBindings, ULONG* pcbRowSize); HRESULT myCreateAccessor(IUnknown* pUnkRowset, HACCESSOR* phAccessor,                           ULONG* pcBindings, DBBINDING** prgBindings,                           ULONG* pcbRowSize); HRESULT myDisplayRowset(IUnknown* pUnkRowset, LPCWSTR pwszColToReturn,                          ULONG cchBuffer, LPWSTR pwszBuffer); HRESULT myDisplayColumnNames(IUnknown* pUnkRowset, ULONG* rgDispSize); HRESULT myDisplayRow(ULONG iRow, ULONG cBindings, DBBINDING* rgBindings,                       void* pData, ULONG * rgDispSize); HRESULT myInteractWithRowset(IRowset* pIRowset, LONG* pcRows, DBCOUNTITEM                               cRowsObtained, BOOL fCanFetchBackwards,                               void* pData, ULONG cbRowSize, DBBINDING*                               pBinding, ULONG cchBuffer, LPWSTR                               pwszBuffer); HRESULT myFindColumn(IUnknown * pUnkRowset, LPCWSTR pwszName, LONG*                       plIndex); HRESULT myUpdateDisplaySize(ULONG cBindings, DBBINDING* rgBindings, void*                              pData, ULONG* rgDispSize); void myFreeBindings(ULONG cBindings, DBBINDING* rgBindings); void myAddRowsetProperties(DBPROPSET* pPropSet, ULONG cProperties,                             DBPROP* rgProperties); // Error HRESULT myHandleResult(HRESULT hrReturned, LPCWSTR pwszFile, ULONG                         ulLine); HRESULT myDisplayErrorRecord(HRESULT hrReturned, ULONG iRecord,                               IErrorRecords* pIErrorRecords, LPCWSTR                               pwszFile, ULONG ulLine); HRESULT myDisplayErrorInfo(HRESULT hrReturned, IErrorInfo* pIErrorInfo,                             LPCWSTR pwszFile, ULONG ulLine); HRESULT myGetSqlErrorInfo(ULONG iRecord, IErrorRecords* pIErrorRecords,                            BSTR* pBstr, LONG* plNativeError); #endif   // __PRSAMPLE_H__ //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module MAIN.CPP // //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #define DBINITCONSTANTS   // Store all OLE DB consts inside                           // this .obj file. #include "prsample.h"     // Programmer's Reference Sample includes ///////////////////////////////////////////////////////////////// // Globals // ///////////////////////////////////////////////////////////////// DWORD g_dwFlags = USE_PROMPTDATASOURCE | DISPLAY_METHODCALLS; /////////////////////////////////////////////////////////////////
// main // Summary of Routines // // This is a simple OLE DB application that will display a // rowset and will allow basic navigation of that rowset by the // user. // // In the sample, functions that begin with 'my' are implemented // in the sample code; all other functions are either OLE DB // methods or standard system methods. In addition, two // macros are used repeatedly throughout the sample: //  - CHECK_HR(hr) - this macro goes to the CLEANUP label if //     FAILED(hr), where hr is usually a method call. //  - XCHECK_HR(hr) - this macro prints the string //     representation of hr to stderr and if FAILED(hr), attempts //     to obtain and display any extended error information //     posted by the last method call and then jumps to the CLEANUP //     label. // // This is the entry point for the sample. This function will: //  - parse command line arguments passed to the sample. //  - display appropriate instructions based on these arguments. //  - create an OLE DB data source object for a user-chosen //     provider. //  - create an OLE DB session object from the provider's //     data source object. //  - create an OLE DB rowset object, over a table specified by //     the user, from the provider's session object. //  - display the rowset data and will allow the user to //     navigate over the rowset. // ///////////////////////////////////////////////////////////////// int main() {    HRESULT      hr;    IUnknown *   pUnkDataSource   = NULL;    IUnknown *   pUnkSession      = NULL;    IUnknown *   pUnkRowset       = NULL;    // Parse command line arguments, if any; this will update    // the value of g_dwFlags as appropriate for the arguments.    if( !myParseCommandLine() )       return EXIT_FAILURE;    // Display instructions for the given command line arguments.    myDisplayInstructions();    // Initialize OLE    hr = CoInitialize(NULL);    if( FAILED(hr) )       return EXIT_FAILURE;    // Create the data source object using the OLE DB service components.    CHECK_HR(hr = myCreateDataSource(&pUnkDataSource));    // Create a session object from the data source object.    CHECK_HR(hr = myCreateSession(pUnkDataSource, &pUnkSession));    // Create a rowset object from the session object, either directly    // from the session or through a command object.    CHECK_HR(hr = myCreateRowset(pUnkSession, &pUnkRowset));    // Display the rowset object data to the user.    CHECK_HR(hr = myDisplayRowset(pUnkRowset, NULL, 0, NULL)); CLEANUP:    if( pUnkRowset )       pUnkRowset->Release();    if( pUnkSession )       pUnkSession->Release();    if( pUnkDataSource )       pUnkDataSource->Release();    CoUninitialize();    if( FAILED(hr) )       return EXIT_FAILURE;    return EXIT_SUCCESS; } /////////////////////////////////////////////////////////////////
// myParseCommandLine // Summary of Routines // // This function parses the application's command line arguments // and sets the appropriate bits in g_dwFlags. If an invalid // argument is encountered, a usage message is displayed and // the function returns FALSE; otherwise, TRUE is returned. // ///////////////////////////////////////////////////////////////// BOOL myParseCommandLine    (    void    ) {    int      iArg;    CHAR *   psz;    // Set the locale for all C run-time functions.    setlocale(LC_ALL, ".ACP");    // Go through each command line argument and set the appropriate    // bits in g_dwFlags, depending on the chosen options.    for( iArg = 1; iArg < __argc; iArg++ )    {       // Inspect the current argument string.       psz = __argv[iArg];       // Valid options begin with '-' or '/'.       if( psz[0] == '-' || psz[0] == '/' )       {          // The next character is the option.          switch( tolower(psz[1]) )          {          case 'u':             // Use the service components UI to prompt for and create             // the data source object; the enumerator is not used.             g_dwFlags |= USE_PROMPTDATASOURCE;             g_dwFlags &= ~USE_ENUMERATOR;             continue;          case 'e':             // Use the enumerator to select the provider, and then use             // IDataInitialize to create the data source object.             // Don't use the UI to prompt for the data source.             g_dwFlags |= USE_ENUMERATOR;             g_dwFlags &= ~USE_PROMPTDATASOURCE;             continue;          case 'c':             // Use ICommand instead of IOpenRowset.             g_dwFlags |= USE_COMMAND;             continue;          case 'b':             // Use ISequentialStream to fetch BLOB column data.             g_dwFlags |= USE_ISEQSTREAM;             continue;          case 'n':             // Don't display method call strings as part of             // the extended error checking macro.             g_dwFlags &= ~DISPLAY_METHODCALLS;             continue;          }       }       // Invalid argument; show the usage flags to the user.       fprintf(stderr, "Usage: %s [-u] [-e] [-c] [-b] [-n]\n\nWhere:\n\t" \          "u = Use the Microsoft Data Links UI " \             "to create the DataSource\n\t" \          "e = Use the Enumerator and IDataInitialize " \             "to create the DataSource\n\t" \          "c = Use ICommand instead of IOpenRowset to create the Rowset\n\t" \          "b = Use ISequentialStream for BLOB columns\n\t" \          "n = Don't display method call strings\n",          __argv[0]);       return FALSE;    }    return TRUE; } /////////////////////////////////////////////////////////////////
// myDisplayInstructions // Summary of Routines // // This function asks the user whether they would like // instructions displayed for the application. If so, it // displays the instructions appropriate to the flags set // in g_dwFlags. // ///////////////////////////////////////////////////////////////// void myDisplayInstructions    (    void    ) {    CHAR ch;    // Display header and ask the user if they want instructions.    printf("\nOLE DB Programmer's Reference Sample\n" \           "====================================\n\n");    printf("Display instructions [Y or N]? ");    do    {       ch = myGetChar();    }    while( ch != 'y' && ch != 'n' );    printf("%c\n\n", ch);    // No instructions, so we're done.    if( ch == 'n' )       return;    // Display basic instructions.    printf("This application is a simple OLE DB sample that will display\n" \          "a rowset and will allow basic navigation of that rowset by\n" \          "the user. The application will perform the following\n" \          "steps:\n\n");    // Display data source creation instructions.    if( g_dwFlags & USE_PROMPTDATASOURCE )    {       printf(" - Creates a data source object through the Microsoft              Data\n" \ "   Links UI. This allows the user to select              the OLE DB\n" \ "   provider to use and to set connection              properties.\n");    }    else    {       printf(" - Creates a data source object through              IDataInitialize::\n" \              "   CreateDBInstance, which allows the OLE DB service\n" \              "   component manager to add additional functionality to\n"\              "   the provider as requested. The user will select the\n" \              "   provider to use from a rowset obtained from the OLE              DB\n" \ "   enumerator.\n");    }    // Display session creation and table-selection instructions.    printf(" - Creates a session object from the data source object.\n");    printf(" - If the provider supports the schema rowset interface,\n" \           "   creates a TABLES schema rowset and allows the user to\n" \           "   select a table name from this rowset.\n");    // Display rowset creation instructions    if( g_dwFlags & USE_COMMAND )    {       printf(" - Creates a cxommand object from the session object and\n" \             "   allows the user to specify command text for this Command,\n" \             "   then executes the command to create the final rowset.\n");    }    else    {       printf(" - Creates the final rowset over the table specified by the\n" \             "   user.\n");    }    printf(" - Displays this rowset and allows the user to perform basic\n" \          "   navigation of that rowset.\n\n");    // Wait for the user to press a key before continuing.    printf("Press a key to continue...");    myGetChar();    printf("\n\n"); } /////////////////////////////////////////////////////////////////
// myGetInputFromUser // Summary of Routines // // This function prompts the user with the contents of pwszFmt // and any accompanying variable arguments and then gets a string // as input from the user. If the string is non-empty, it is // copied into pwszInput and the function returns TRUE; // otherwise, this function returns FALSE. // ///////////////////////////////////////////////////////////////// BOOL myGetInputFromUser    (    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; } /////////////////////////////////////////////////////////////////
// myGetChar // Summary of Routines // // This function gets a character from the keyboard and // converts it to lowercase before returning it. // ///////////////////////////////////////////////////////////////// CHAR myGetChar    (    void    ) {    CHAR ch;    // Get a character from the keyboard.    ch = _getch();    // Re-read for the actual key value if necessary.    if( !ch || ch == 0xE0 )       ch = _getch();    return tolower(ch); } //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module ENUM.CPP // //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #include "prsample.h"      // Programmer's Reference Sample includes /////////////////////////////////////////////////////////////////
// myCreateEnumerator // Summary of Routines // // This function creates an enumerator, obtains a sources rowset // from it, displays the rowset to the user, and allows the user // to specify the ProgID of a provider. The CLSID that matches // this ProgID is retuned to the caller in *pCLSID. // ///////////////////////////////////////////////////////////////// HRESULT myCreateEnumerator    (    REFCLSID           clsidEnumerator,    CLSID *            pCLSID    ) {    HRESULT            hr;    IUnknown *         pIUnkEnumerator               = NULL;    ISourcesRowset *   pISourcesRowset               = NULL;    IRowset *          pIRowset                      = NULL;    IDBInitialize *    pIDBInitialize                = NULL;    WCHAR              wszProgID[MAX_NAME_LEN + 1]   = {0};        const ULONG        cProperties                   = 2;    DBPROP             rgProperties[cProperties];    DBPROPSET          rgPropSets[1];    // Create the enumerator object. We ask for IUnknown when creating    // the enumerator because some enumerators may require initialization    // before we can obtain a sources rowset from the enumerator. This is    // indicated by whether the enumerator object exposes IDBInitialize    // or not. (We don't want to ask for IDBInitialize, since enumerators    // that don't require initialization will cause the CoCreateInstance    // to fail.)    XCHECK_HR(hr = CoCreateInstance(             clsidEnumerator,                    // clsid -- enumerator             NULL,                               // pUnkOuter             CLSCTX_INPROC_SERVER,               // dwClsContext             IID_IUnknown,                       // riid             (void**)&pIUnkEnumerator            // ppvObj             ));    // If the enumerator exposes IDBInitialize, we need to initialize it.    // See IDBInitialize    if( SUCCEEDED(hr = pIUnkEnumerator->QueryInterface(IID_IDBInitialize,             (void**)&pIDBInitialize)) )    {       CHECK_HR(hr = myDoInitialization(pIUnkEnumerator));    }    // Set properties on the rowset, to request additional functionality.    myAddRowsetProperties(rgPropSets, cProperties, rgProperties);    // Obtain a sources rowset from the enumerator. This rowset contains    // all of the OLE DB providers that this enumerator is able to list.    // See ISourcesRowset and IRowset    XCHECK_HR(hr = pIUnkEnumerator->QueryInterface(IID_ISourcesRowset,             (void**)&pISourcesRowset));    XCHECK_HR(hr = pISourcesRowset->GetSourcesRowset(             NULL,                               // pUnkOuter             IID_IRowset,                        // riid             1,                                  // cPropSets             rgPropSets,                         // rgPropSets             (IUnknown**)&pIRowset               // ppRowset             ));    // Display the rowset to the user. This will allow the user to    // perform basic navigation of the rowset and will allow the user    // to select a row containing a desired provider.    CHECK_HR(hr = myDisplayRowset(pIRowset,             L"SOURCES_NAME", MAX_NAME_LEN, wszProgID));    // Obtain the ProgID for the provider to use from the user.    // The default value for this is the value of the SOURCES_NAME    // column in the row selected by the user previously.    myGetInputFromUser(wszProgID, L"\nType the ProgID of a provider"             L" to use [Enter = `%s`]: ", wszProgID);    XCHECK_HR(hr = CLSIDFromProgID(wszProgID, pCLSID)); CLEANUP:    if( pIUnkEnumerator )       pIUnkEnumerator->Release();    if( pISourcesRowset )       pISourcesRowset->Release();    if( pIRowset )       pIRowset->Release();    if( pIDBInitialize )       pIDBInitialize->Release();    return hr; } //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module DATASOURCE.CPP // //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #include "prsample.h"      // Programmer's Reference Sample includes /////////////////////////////////////////////////////////////////
// myCreateDataSource // Summary of Routines // // This function creates an OLE DB data source object for a // provider selected by the user, sets initialization properties // for the data source, and initializes the data source. The // function returns a pointer to the data source object's // IUnknown in *ppUnkDataSource. // ///////////////////////////////////////////////////////////////// HRESULT myCreateDataSource    (    IUnknown **             ppUnkDataSource    ) {    HRESULT                 hr;    IDataInitialize *       pIDataInitialize       = NULL;    IDBPromptInitialize *   pIDBPromptInitialize   = NULL;    IDBInitialize *         pIDBInitialize         = NULL;    CLSID                   clsid                  = CLSID_MSDASQL;    // Use the Microsoft Data Links UI to create the data source    // object. This will allow the user to select the provider    // to connect to and to set the initialization properties    // for the data source object, which will be created by the    // Data Links UI.    if( g_dwFlags & USE_PROMPTDATASOURCE )    {       // Create the Data Links UI object, and obtain the       // IDBPromptInitialize interface from it.       // See IDBPromptInitialize        XCHECK_HR(hr = CoCreateInstance(                CLSID_DataLinks,                 // clsid -- Data Links UI                NULL,                            // pUnkOuter                CLSCTX_INPROC_SERVER,            // dwClsContext                IID_IDBPromptInitialize,         // riid                (void**)&pIDBPromptInitialize    // ppvObj                ));       // Invoke the Data Links UI to allow the user to select       // the provider and set initialization properties for       // the data source object that this will create.       // See IDBPromptInitialize and IDBInitialize       XCHECK_HR(hr = pIDBPromptInitialize->PromptDataSource(                NULL,                             // pUnkOuter                GetDesktopWindow(),               // hWndParent                DBPROMPTOPTIONS_PROPERTYSHEET,    // dwPromptOptions                0,                                // cSourceTypeFilter                NULL,                             // rgSourceTypeFilter                NULL,                             // pwszszzProviderFilter                IID_IDBInitialize,                // riid                (IUnknown**)&pIDBInitialize       // ppDataSource                ));       // We've obtained a data source object from the Data Links UI. This       // object has had its initialization properties set, so all we       // need to do is Initialize it.       XCHECK_HR(hr = pIDBInitialize->Initialize());    }    // We are not using the Data Links UI to create the data source    // object. Instead, we will enumerate the providers installed on this    // system through the OLE DB enumerator and will allow the user to    // select the ProgID of the provider for which we will create a    // data source object.    else    {       // Use the OLE DB enumerator to obtain a rowset of installed       // providers, and then allow the user to select a provider from       // this rowset.       CHECK_HR(hr = myCreateEnumerator(CLSID_OLEDB_ENUMERATOR, &clsid));       // We will create the data source object through the OLE DB service       // component IDataInitialize interface, so we need to create an       // instance of the data initialization object.       // See IDataInitialize       XCHECK_HR(hr = CoCreateInstance(                CLSID_MSDAINITIALIZE,          // clsid -- data initialize                NULL,                          // pUnkOuter                CLSCTX_INPROC_SERVER,          // dwClsContext                IID_IDataInitialize,           // riid                (void**)&pIDataInitialize      // ppvObj                ));       // Use IDataInitialize::CreateDBInstance to create an uninitialized       // data source object for the chosen provider. By using this       // service component method, the service component manager can       // provide additional functionality beyond what is natively       // supported by the provider if the consumer requests that       // functionality.       // See IDBInitialize       XCHECK_HR(hr = pIDataInitialize->CreateDBInstance(                clsid,                                // clsid -- provider                NULL,                                 // pUnkOuter                CLSCTX_INPROC_SERVER,                 // dwClsContext                NULL,                                 // pwszReserved                IID_IDBInitialize,                    // riid                (IUnknown**)&pIDBInitialize           // ppDataSource                ));       // Initialize the data source object by setting any required       // initialization properties and calling IDBInitialize::Initialize.       CHECK_HR(hr = myDoInitialization(pIDBInitialize));    } CLEANUP:    *ppUnkDataSource = pIDBInitialize;    if( pIDataInitialize )       pIDataInitialize->Release();    if( pIDBPromptInitialize )       pIDBPromptInitialize->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myDoInitialization // Summary of Routines // // This function sets initialization properties that tell the // provider to prompt the user for any information required to // initialize the provider and then calls the provider's // initialization function. // ///////////////////////////////////////////////////////////////// HRESULT myDoInitialization    (    IUnknown *        pIUnknown    ) {    HRESULT           hr;    IDBInitialize *   pIDBInitialize   = NULL;    IDBProperties *   pIDBProperties   = NULL;    HWND              hWnd             = GetDesktopWindow();        const ULONG       cProperties      = 2;    DBPROP            rgProperties[cProperties];    DBPROPSET         rgPropSets[1];    // To initialize the data source object, most providers require    // some initialization properties to be set by the consumer. For    // example, these might include the data source to connect to and the    // user ID and password to use to establish identity. We will ask the    // provider to prompt the user for this required information by    // setting the following properties:    myAddProperty(&rgProperties[0],DBPROP_INIT_PROMPT,VT_I2,DBPROMPT_COMPLETE);    myAddProperty(&rgProperties[1],DBPROP_INIT_HWND,  VT_I4,(LONG)hWnd);    rgPropSets[0].rgProperties      = rgProperties;    rgPropSets[0].cProperties       = cProperties;    rgPropSets[0].guidPropertySet   = DBPROPSET_DBINIT;    // Obtain the needed interfaces.    // See IDBProperties and IDBInitialize    XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,             (void**)&pIDBProperties));    XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBInitialize,             (void**)&pIDBInitialize));    // If a provider requires initialization properties, it must support    // the properties that we are setting (_PROMPT and _HWND). However,    // some providers do not need initialization properties and may    // therefore not support the _PROMPT and _HWND properties. Because of    // this, we will not check the return value from SetProperties.    hr = pIDBProperties->SetProperties(1, rgPropSets);    // Now that we've set our properties, initialize the provider.    XCHECK_HR(hr = pIDBInitialize->Initialize()); CLEANUP:    if( pIDBProperties )       pIDBProperties->Release();    if( pIDBInitialize )       pIDBInitialize->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myGetProperty // Summary of Routines // // This function gets the BOOL value for the specified property // and returns the result in *pbValue. // ///////////////////////////////////////////////////////////////// HRESULT myGetProperty    (    IUnknown *             pIUnknown,    REFIID                 riid,    DBPROPID               dwPropertyID,    REFGUID                guidPropertySet,    BOOL *                 pbValue    ) {    HRESULT                hr;    DBPROPID               rgPropertyIDs[1];    DBPROPIDSET            rgPropertyIDSets[1];        ULONG                  cPropSets        = 0;    DBPROPSET *            rgPropSets       = NULL;    IDBProperties *        pIDBProperties   = NULL;    ISessionProperties *   pISesProps       = NULL;    ICommandProperties *   pICmdProps       = NULL;    IRowsetInfo *          pIRowsetInfo     = NULL;    // Initialize the output value    *pbValue = FALSE;    // Set up the property ID array    rgPropertyIDs[0] = dwPropertyID;        // Set up the Property ID Set    rgPropertyIDSets[0].rgPropertyIDs     = rgPropertyIDs;    rgPropertyIDSets[0].cPropertyIDs      = 1;    rgPropertyIDSets[0].guidPropertySet   = guidPropertySet;    // Get the property value for this property from the provider, but    // don't try to display extended error information, since this may    // not be a supported property. A failure is, in fact, expected if    // the property is not supported.    // See IDBProperties, ISessionProperties, ICommandProperties,    // and IRowsetInfo    if( riid == IID_IDBProperties )    {       XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IDBProperties,                (void**)&pIDBProperties));       CHECK_HR(hr = pIDBProperties->GetProperties(                1,                                 // cPropertyIDSets                rgPropertyIDSets,                  // rgPropertyIDSets                &cPropSets,                        // pcPropSets                &rgPropSets                        // prgPropSets                ));    }    else if( riid == IID_ISessionProperties )    {       XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ISessionProperties,                (void**)&pISesProps));       CHECK_HR(hr = pISesProps->GetProperties(                1,                                 // cPropertyIDSets                rgPropertyIDSets,                  // rgPropertyIDSets                &cPropSets,                        // pcPropSets                &rgPropSets                        // prgPropSets                ));    }    else if( riid == IID_ICommandProperties )    {       XCHECK_HR(hr = pIUnknown->QueryInterface(IID_ICommandProperties,                (void**)&pICmdProps));       CHECK_HR(hr = pICmdProps->GetProperties(                1,                                 // cPropertyIDSets                rgPropertyIDSets,                  // rgPropertyIDSets                &cPropSets,                        // pcPropSets                &rgPropSets                        // prgPropSets                ));    }    else    {       XCHECK_HR(hr = pIUnknown->QueryInterface(IID_IRowsetInfo,                (void**)&pIRowsetInfo));       CHECK_HR(hr = pIRowsetInfo->GetProperties(                1,                                 // cPropertyIDSets                rgPropertyIDSets,                  // rgPropertyIDSets                &cPropSets,                        // pcPropSets                &rgPropSets                        // prgPropSets                ));    }    // Return the value for this property to the caller if    // it's a VT_BOOL type value, as expected.    if( V_VT(&rgPropSets[0].rgProperties[0].vValue) == VT_BOOL )       *pbValue = V_BOOL(&rgPropSets[0].rgProperties[0].vValue); CLEANUP:    // Notice that in addition to calling IMalloc::Free    // for the rgProperties element within each element of    // rgPropSets, cleanup code also calls VariantClear for    // the vValue property of each DBPROPSET structure in    // order to prevent a memory leak in cases where the    // variant contains a reference type (such as a BSTR.)     ULONG iPropSet;    ULONG iProp;    Assert((rgPropSets != NULL) || (cPropSets == 0));    if( rgPropSets )    {       for(iPropSet = 0; iPropSet < cPropSets; iPropSet++)       {          Assert((rgPropSets[iPropSet].cProperties == 0) ||                (rgPropSets[iPropSet].rgProperties != NULL));          if(rgPropSets[iPropSet].rgProperties)          {             for(iProp = 0;                iProp < rgPropSets[iPropSet].cProperties;                iProp++)             {          VariantClear(&(rgPropSets[iPropSet].rgProperties[iProp].vValue));             }             Free(rgPropSets[iPropSet].rgProperties);          }       }       Free(rgPropSets);    }    if( pIDBProperties )       pIDBProperties->Release();    if( pISesProps )       pISesProps->Release();    if( pICmdProps )       pICmdProps->Release();    if( pIRowsetInfo )       pIRowsetInfo->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myAddProperty // Summary of Routines // // This function initializes the property structure pProp. // ///////////////////////////////////////////////////////////////// void myAddProperty    (    DBPROP *           pProp,    DBPROPID           dwPropertyID,    VARTYPE            vtType,    LONG               lValue,    DBPROPOPTIONS      dwOptions    ) {    // Set up the property structure.    pProp->dwPropertyID    = dwPropertyID;    pProp->dwOptions       = dwOptions;    pProp->dwStatus        = DBPROPSTATUS_OK;    pProp->colid           = DB_NULLID;    V_VT(&pProp->vValue)   = vtType;    // Since VARIANT data is a union, we can place the value in any    // member (except for VT_DECIMAL, which is a union with the whole    // VARIANT structure -- but we know we're not passing VT_DECIMAL).    V_I4(&pProp->vValue)   = lValue; } //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module SESSION.CPP // //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #include "prsample.h"      // Programmer's Reference Sample includes /////////////////////////////////////////////////////////////////
// myCreateSession // Summary of Routines // // Create an OLE DB session object from the given data source // object. The IDBCreateSession interface is mandatory, so this // is a simple operation. // ///////////////////////////////////////////////////////////////// HRESULT   myCreateSession    (    IUnknown *           pUnkDataSource,    IUnknown **          ppUnkSession    ) {    HRESULT              hr;    IDBCreateSession *   pIDBCreateSession      = NULL;        //Create a session object from a data source object    // See IDBCreateSession and IOpenRowset    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; } /////////////////////////////////////////////////////////////////
// myCreateSchemaRowset // Summary of Routines // // If the provider supports IDBSchemaRowset, this function will // obtain the tables schema rowset, will display this rowset to // the user, and will allow the user to select a row in the // rowset containing the name of a table of interest. // ///////////////////////////////////////////////////////////////// HRESULT   myCreateSchemaRowset    (    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];    // Attempt to obtain the IDBSchemaRowset interface on the session    // object. This is not a mandatory interface; if it is not supported,    // we are done.    // See IDBSchemaRowset    CHECK_HR(pUnkSession->QueryInterface(                IID_IDBSchemaRowset, (void**)&pIDBSchemaRowset));        // Set properties on the rowset, to request additional functionality.    myAddRowsetProperties(rgPropSets, cProperties, rgProperties);    // Get the requested schema rowset. If IDBSchemaRowset is supported,    // the following schema rowsets are required to be supported:    // DBSCHEMA_TABLES, DBSCHEMA_COLUMNS, and DBSCHEMA_PROVIDERTYPES    // We know that we will be asking for one of these, so it is not    // necessary to call IDBSchemaRowset::GetSchemas in this case.    // See IRowset    XCHECK_HR(hr = pIDBSchemaRowset->GetRowset(             NULL,                              // pUnkOuter             guidSchema,                        // guidSchema             0,                                 // cRestrictions             NULL,                              // rgRestrictions             IID_IRowset,                       // riid             1,                                 // cPropSets             rgPropSets,                        // rgPropSets             &pUnkRowset                        // ppRowset             ));    // Display the rowset to the user. This will allow the user to    // perform basic navigation of the rowset and will allow the user    // to select a row containing a desired table name (taken from the    // TABLE_NAME column).    CHECK_HR(hr = myDisplayRowset(pUnkRowset,       L"TABLE_NAME", cchBuffer, pwszBuffer)); CLEANUP:    if( pIDBSchemaRowset )       pIDBSchemaRowset->Release();    if( pUnkRowset )       pUnkRowset->Release();    return hr; } _ //--------------------------------------------------------------------------- // 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 // Summary of Routines // // 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. // ///////////////////////////////////////////////////////////////// HRESULT   myCreateRowset    (    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.       // See 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; } /////////////////////////////////////////////////////////////////
// mySetupBindings // Summary of Routines // // 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    // See IColumnsInfo    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.    // See ISequentialStream    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 // Summary of Routines // // 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.    // 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; } /////////////////////////////////////////////////////////////////
// myDisplayRowset // Summary of Routines // // This function will display data from a rowset object and will // allow the user to perform basic navigation of the rowset. // // The function takes a pointer to a rowset object's IUnknown // and, optionally, the name of a column and a buffer that will // receive the value of that column when the user selects a row. // ///////////////////////////////////////////////////////////////// HRESULT myDisplayRowset    (    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    myGetProperty(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 = myFindColumn(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 = myCreateAccessor(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);    // 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 = myUpdateDisplaySize(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                   ));                    // Display the names of the rowset columns.          CHECK_HR(hr = myDisplayColumnNames(pIRowset, rgDispSize));       }              // 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 = myDisplayRow(iRow, cBindings, rgBindings,             pCurData, rgDispSize));       }       // Allow the user to navigate the rowset. This displays the       // appropriate prompts, gets the user's input, may call       // IRowset::RestartPosition, and may copy data from a selected row       // to the selection buffer, if so directed. This will return S_OK       // if the user asked for more rows, S_FALSE if the user selected a       // row, or E_FAIL if the user quits.       hr = myInteractWithRowset(          pIRowset,                // IRowset pointer, for RestartPosition          &cRows,                  // updated with fetch direction value          cRowsObtained,           // to indicate selection range          fCanFetchBackwards,      // whether [P]revious is supported          pData,                   // data pointer for copying selection          cbRowSize,               // size of rows for copying selection          iRetCol >= 0 ?           // bindings for the selection column,             &rgBindings[iRetCol] :   // or NULL if no selection column             NULL,          cchBuffer,                // size of the selection buffer          pwszBuffer);             // pointer to the selection buffer       // 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:    myFreeBindings(cBindings, rgBindings);    CoTaskMemFree(rgDispSize);    CoTaskMemFree(pData);    if( pIRowset )       pIRowset->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myInteractWithRowset // Summary of Routines // // This function allows the user to interact with the rowset. It // prompts the user appropriately, gets the user's input, may // call IRowset::RestartPosition if the user requests a restart, // and will copy data from a selected row to the selection // buffer. // ///////////////////////////////////////////////////////////////// HRESULT myInteractWithRowset    (    IRowset *     pIRowset,    LONG *        pcRows,    DBCOUNTITEM   cRowsObtained,    BOOL          fCanFetchBackwards,    void *        pData,    ULONG         cbRowSize,    DBBINDING *   pBinding,    ULONG         cchBuffer,    LPWSTR        pwszBuffer    ) {    HRESULT       hr = S_OK;    CHAR          ch;    // Let the user know if no rows were fetched.    if( !cRowsObtained )       printf("\n*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n" \              "*                                 *\n" \              "* No rows obtained on this fetch! *\n" \              "*                                 *\n" \              "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n");    // Print navigation options.    if( fCanFetchBackwards )       printf("\n[P]revious; [N]ext; [R]estart; ");    else       printf("\n[N]ext; [R]estart; ");        // Print selection options.    if( cRowsObtained && pwszBuffer && pBinding )       printf("[0]-[%d] for a row; ", cRowsObtained - 1);    // User can always quit the program.    printf("[Q]uit? ");    // Get the user's input.    while( TRUE )    {       // Get a character from the console.       ch = myGetChar();       // Look for one of the allowed options; if not found, go       // back around and wait for another input from the user.       // If we're looking for a row selection, allow the user to select       // a row that we fetched, and then copy the data from the requested       // column into the selection buffer we were passed.       if( pwszBuffer && pBinding &&          ch >= '0' && ch < (int)('0' + cRowsObtained) )       {          // Save the data for the selected row.          ULONG nSelection = ch - '0';          _snwprintf(pwszBuffer, cchBuffer, L"%s",                     (WCHAR *)((BYTE *)pData + cbRowSize * nSelection +                     pBinding->obValue));          pwszBuffer[cchBuffer] = L'\0';          hr = S_FALSE;       }       // If the provider supports fetching backwards, set *pcRows       // to -MAX_ROWS. When GetNextRows is called with this value,       // it will fetch rows backwards from the current position       // until it fetches MAX_ROWS rows or hits the end of the rowset.       else if( fCanFetchBackwards && ch == 'p' )       {          // Fetch backwards.          *pcRows = -MAX_ROWS;       }       // Set *pcRows so that the next call to GetNextRows fetches       // MAX_ROWS rows forward from the current position.       else if( ch == 'n' )       {          // Fetch forward          *pcRows = MAX_ROWS;       }       // Call IRowset::RestartPosition, and fetch the first MAX_ROWS       // rows of the rowset forward from there.       else if( ch == 'r' )       {          // RestartPosition          *pcRows = MAX_ROWS;          XCHECK_HR(hr = pIRowset->RestartPosition(DB_NULL_HCHAPTER));                    // Restarting a command may return the DB_S_COMMANDREEXECUTED          // warning. If this happens, we still want the caller to          // continue to display data, so we will reset the result code.          // to S_OK.          hr = S_OK;       }       // Quit the program.       else if( ch == 'q' )       {          hr = E_FAIL;       }       // Invalid option; go back up and get another character from the       // user.       else       {          continue;       }       // Echo the character and stop waiting for input.       printf("%c\n", ch);       break;    } CLEANUP:    return hr; }        /////////////////////////////////////////////////////////////////
// myDisplayColumnNames // Summary of Routines // // 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.    // See IColumnsInfo    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 new line.    wprintf(L"\n"); CLEANUP:    CoTaskMemFree(rgColumnInfo);    CoTaskMemFree(pStringsBuffer);    if( pIColumnsInfo )       pIColumnsInfo->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myDisplayRow // Summary of Routines // // 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 // Summary of Routines // //   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 // Summary of Routines // // 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 // 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 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 // Summary of Routines // // 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.    // See IColumnsInfo    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; } //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module COMMAND.CPP // //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #include "prsample.h"         // Programmer's Reference Sample includes /////////////////////////////////////////////////////////////////
// myCreateCommand // Summary of Routines // // This function takes an IUnknown pointer on a session object // and attempts to create a command object using the session's // IDBCreateCommand interface. Since this interface is optional, // this may fail. // ///////////////////////////////////////////////////////////////// HRESULT myCreateCommand    (    IUnknown *           pUnkSession,    IUnknown **          ppUnkCommand    ) {    HRESULT              hr;    IDBCreateCommand *   pIDBCreateCommand = NULL;        // Attempt to create a command object from the session object    // See IDBCreateCommand    XCHECK_HR(hr = pUnkSession->QueryInterface(             IID_IDBCreateCommand, (void**)&pIDBCreateCommand));    XCHECK_HR(hr = pIDBCreateCommand->CreateCommand(             NULL,                                       // pUnkOuter             IID_ICommand,                               // riid             ppUnkCommand                                // ppCommand             )); CLEANUP:    if( pIDBCreateCommand )       pIDBCreateCommand->Release();    return hr; } /////////////////////////////////////////////////////////////////
// myExecuteCommand // Summary of Routines // // This function takes an IUnknown pointer on a command object // and performs the following steps to create a new rowset // object: // - sets the given properties on the command object; these //    properties will be applied by the provider to any rowset //    created by this command. // - sets the given command text for the command. // - executes the command to create a new rowset object. // ///////////////////////////////////////////////////////////////// HRESULT myExecuteCommand    (    IUnknown *             pUnkCommand,    WCHAR *                pwszCommandText,    ULONG                  cPropSets,    DBPROPSET*             rgPropSets,    IUnknown **            ppUnkRowset    ) {    HRESULT                hr;    ICommandText *           pICommandText         = NULL;    ICommandProperties *   pICommandProperties   = NULL;    // Set the properties on the command object.    // See ICommandProperties    XCHECK_HR(hr = pUnkCommand->QueryInterface(             IID_ICommandProperties, (void**)&pICommandProperties));    XCHECK_HR(hr = pICommandProperties->SetProperties(cPropSets, rgPropSets));    // Set the text for this command, using the default command text    // dialect. All providers that support commands must support this    // dialect, and providers that support SQL must be able to recognize    // an SQL command as SQL when this dialect is specified.    // See ICommandText    XCHECK_HR(hr = pUnkCommand->QueryInterface(             IID_ICommandText, (void**)&pICommandText));    XCHECK_HR(hr = pICommandText->SetCommandText(             DBGUID_DEFAULT,                        // guidDialect             pwszCommandText                        // pwszCommandText             ));    // And execute the command. Note that the user could have    // entered a non-row returning command, so we will check for    // that and return failure to prevent the display of the    // nonexistent rowset by the caller.    XCHECK_HR(hr = pICommandText->Execute(                NULL,                              // pUnkOuter             IID_IRowset,                       // riid             NULL,                              // pParams             NULL,                              // pcRowsAffected             ppUnkRowset                        // ppRowset             ));           if( !*ppUnkRowset )    {       printf("\nThe command executed successfully, but did not " \              "return a rowset.\nNo rowset will be displayed.\n");       hr = E_FAIL;    } CLEANUP:    if( pICommandText )       pICommandText->Release();    if( pICommandProperties )       pICommandProperties->Release();    return hr; } //--------------------------------------------------------------------------- // Microsoft OLE DB Programmer's Reference Sample. // Copyright (C) 1998 By Microsoft Corporation. // // @doc // // @module ERROR.CPP // //--------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////// // Includes // //////////////////////////////////////////////////////////////////////// #include "prsample.h"         // Programmer's Reference Sample includes ////////////////////////////////////////////////////////////////////////
// myHandleResult // Summary of Routines // // This function is called as part of the XCHECK_HR macro; it takes an // HRESULT, which is returned by the method called in the XCHECK_HR // macro, and the file and line number where the method call was made. // If the method call failed, this function attempts to get and display // the extended error information for the call from the IErrorInfo, // IErrorRecords, and ISQLErrorInfo interfaces. // //////////////////////////////////////////////////////////////////////// HRESULT myHandleResult    (    HRESULT           hrReturned,    LPCWSTR           pwszFile,    ULONG             ulLine    ) {    HRESULT           hr;    IErrorInfo *      pIErrorInfo      = NULL;    IErrorRecords *   pIErrorRecords   = NULL;    ULONG             cRecords;    ULONG             iErr;    // If the method called as part of the XCHECK_HR macro failed,    // we will attempt to get extended error information for the call.    if( FAILED(hrReturned) )    {       // Obtain the current error object, if any, by using the       // Automation GetErrorInfo function, which will give       // us back an IErrorInfo interface pointer if successful.       hr = GetErrorInfo(0, &pIErrorInfo);       // We've got the IErrorInfo interface pointer on the error object.       if( SUCCEEDED(hr) && pIErrorInfo )       {          // OLE DB extends the Automation error model by allowing          // error objects to support the IErrorRecords interface. This          // interface can expose information on multiple errors.          // See IErrorRecords          hr = pIErrorInfo->QueryInterface(IID_IErrorRecords,                   (void**)&pIErrorRecords);          if( SUCCEEDED(hr) )          {             // Get the count of error records from the object.             CHECK_HR(hr = pIErrorRecords->GetRecordCount(&cRecords));                          // Loop through the set of error records, and             // display the error information for each one.             for( iErr = 0; iErr < cRecords; iErr++ )             {                myDisplayErrorRecord(hrReturned, iErr, pIErrorRecords,                   pwszFile, ulLine);             }          }          // The object didn't support IErrorRecords; display          // the error information for this single error.          else          {             myDisplayErrorInfo(hrReturned, pIErrorInfo, pwszFile, ulLine);          }       }       // There was no error object, so just display the HRESULT       // to the user.       else       {          wprintf(L"\nNo Error Info posted; HResult: 0x%08x\n"             L"File: %s, Line: %d\n", hrReturned, pwszFile, ulLine);       }    } CLEANUP:    if( pIErrorInfo )       pIErrorInfo->Release();    if( pIErrorRecords )       pIErrorRecords->Release();    return hrReturned; } ////////////////////////////////////////////////////////////////////////
// myDisplayErrorRecord // Summary of Routines // // This function displays the error information for a single error // record, including information from ISQLErrorInfo, if supported. // //////////////////////////////////////////////////////////////////////// HRESULT myDisplayErrorRecord    (    HRESULT           hrReturned,    ULONG             iRecord,    IErrorRecords *   pIErrorRecords,    LPCWSTR           pwszFile,    ULONG             ulLine    ) {    HRESULT           hr;    IErrorInfo *      pIErrorInfo       = NULL;    BSTR              bstrDescription   = NULL;    BSTR              bstrSource        = NULL;    BSTR              bstrSQLInfo       = NULL;    static LCID       lcid              = GetUserDefaultLCID();    LONG              lNativeError      = 0;    ERRORINFO         ErrorInfo;    // Get the IErrorInfo interface pointer for this error record.    CHECK_HR(hr = pIErrorRecords->GetErrorInfo(iRecord, lcid, &pIErrorInfo));        // Get the description of this error.    CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));           // Get the source of this error.    CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));    // Get the basic error information for this record.    CHECK_HR(hr = pIErrorRecords->GetBasicErrorInfo(iRecord, &ErrorInfo));    // If the error object supports ISQLErrorInfo, get this information.    myGetSqlErrorInfo(iRecord, pIErrorRecords, &bstrSQLInfo, &lNativeError);    // Display the error information to the user.    if( bstrSQLInfo )    {       wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"          L"SQLErrorInfo: %s\nSource: %s\nFile: %s, Line: %d\n",          ErrorInfo.hrError,          bstrDescription,          bstrSQLInfo,          bstrSource,          pwszFile,          ulLine);    }    else    {       wprintf(L"\nErrorRecord:  HResult: 0x%08x\nDescription: %s\n"          L"Source: %s\nFile: %s, Line: %d\n",          ErrorInfo.hrError,          bstrDescription,          bstrSource,          pwszFile,          ulLine);    } CLEANUP:    if( pIErrorInfo )       pIErrorInfo->Release();    SysFreeString(bstrDescription);    SysFreeString(bstrSource);    SysFreeString(bstrSQLInfo);    return hr; } ////////////////////////////////////////////////////////////////////////
// myDisplayErrorInfo // Summary of Routines // // This function displays basic error information for an error object // that doesn't support the IErrorRecords interface. // //////////////////////////////////////////////////////////////////////// HRESULT myDisplayErrorInfo    (    HRESULT        hrReturned,    IErrorInfo *   pIErrorInfo,    LPCWSTR        pwszFile,    ULONG          ulLine    ) {    HRESULT        hr;    BSTR           bstrDescription   = NULL;    BSTR           bstrSource        = NULL;    // Get the description of the error.    CHECK_HR(hr = pIErrorInfo->GetDescription(&bstrDescription));           // Get the source of the error -- this will be the window title.    CHECK_HR(hr = pIErrorInfo->GetSource(&bstrSource));    // Display this error information.    wprintf(L"\nErrorInfo:  HResult: 0x%08x, Description: %s\nSource:             %s\n" L"File: %s, Line: %d\n",             hrReturned,             bstrDescription,             bstrSource,             pwszFile,             ulLine); CLEANUP:    SysFreeString(bstrDescription);    SysFreeString(bstrSource);    return hr; } ////////////////////////////////////////////////////////////////////////
// myGetSqlErrorInfo // Summary of Routines // // If the error object supports ISQLErrorInfo, get the SQL error // string and native error code for this error. // //////////////////////////////////////////////////////////////////////// HRESULT myGetSqlErrorInfo    (    ULONG             iRecord,    IErrorRecords *   pIErrorRecords,    BSTR *            pBstr,    LONG *            plNativeError    ) {    HRESULT           hr;    ISQLErrorInfo *   pISQLErrorInfo   = NULL;    LONG              lNativeError     = 0;    // Attempt to get the ISQLErrorInfo interface for this error    // record through GetCustomErrorObject. Note that ISQLErrorInfo    // is not mandatory, so failure is acceptable here.    // See ISQLErrorInfo    CHECK_HR(hr = pIErrorRecords->GetCustomErrorObject(             iRecord,                               // iRecord             IID_ISQLErrorInfo,                     // riid             (IUnknown**)&pISQLErrorInfo            // ppISQLErrorInfo             ));    // If we obtained the ISQLErrorInfo interface, get the SQL    // error string and native error code for this error.    if( pISQLErrorInfo )       hr = pISQLErrorInfo->GetSQLInfo(pBstr, &lNativeError); CLEANUP:    if( plNativeError )       *plNativeError = lNativeError;    if( pISQLErrorInfo )       pISQLErrorInfo->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