SAMPCLNT.CPP

//-------------------------------------------------------------------- 
// Microsoft OLE DB Sample Consumer
// (C) Copyright 1995 - 1996 Microsoft Corporation. All Rights Reserved.
//
// File name: SAMPCLNT.CPP
//
// Implementation file for a simple OLE DB consumer.
// Dump\output routines for SAMPCLNT are in DUMP.CPP
// SAMPCLNT.H is the header file.
//
// See OLE DB SDK Guide for information on building and running
//this sample, as well as notes concerning the implementation of
//a simple OLE DB consumer.
//
// Functions:
//
// See SAMPCLNT.H for function prototypes
//
//
// SampClnt is structured to match the steps required for simple OLE DB data access:
//
// DoTests
// GetSampprovDataSource
// GetDBSessionFromDataSource
// GetRowsetFromDBSession
// GetDataFromRowset
// GetColumnsInfo
// SetupBindings
// CreateAccessor
// GetData
// CleanupRowset
//




#define INITGUID
#define DBINITCONSTANTS
#include "sampclnt.h"


IMalloc* g_pIMalloc = NULL;
FILE* g_fpLogFile = NULL; // our log file




//**********************************************************************
//
// main
//
// Purpose:
//
// Entry point for this program.
//
// Parameters:
//
// none
//
// Return Value:
//
// none
//
// Comments:
//
// Main does some initializing, then calls DoTests, then cleans up.
// All of the interesting action in this program happens in DoTests.
//
//**********************************************************************


void main()
{
DWORD dwVersion;
HRESULT hr;
time_tttime;
BOOL fOleInitialized = FALSE;
charch;

g_fpLogFile = fopen( "sampclnt.out", "at");
if (!g_fpLogFile)
{
DumpErrorMsg( "Warning: cannot open log file sampclnt.out\n" );
}

time(&ttime);

DumpStatusMsg( "\n-------------------------\n\n");
DumpStatusMsg( "running sampclnt.exe\n%s\n\n", ctime(&ttime) );

dwVersion = OleBuildVersion();
if (HIWORD(dwVersion) != rmm)
{
DumpErrorMsg( "Error: OLE version mismatch. Build version %ld, current version %ld\n",
rmm, HIWORD(dwVersion) );
goto error;
}

hr = OleInitialize( NULL );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorMsg("Error: OleInitialize failed\n");
goto error;
}
fOleInitialized = TRUE;

hr = CoGetMalloc( MEMCTX_TASK, &g_pIMalloc );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorMsg("Error: CoGetMalloc failed\n");
goto error;
}

hr = DoTests();
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "DoTests");
goto error;
}

g_pIMalloc->Release();
OleUninitialize();

if (g_fpLogFile)
fclose(g_fpLogFile);

/* Pause before we quit, in case user ran from an icon, so they can see our messages. */
printf("\n\n>>> Output has gone into 'sampclnt.out'.");
printf("\n>>> You may wish to use a wide-column editor to view this file.\n\n");
printf("<press any key to continue>");
ch = _getch();
return;

error:
if (g_pIMalloc)
g_pIMalloc->Release();
if (fOleInitialized)
OleUninitialize();
if (g_fpLogFile)
fclose(g_fpLogFile);

/* Pause before we quit, in case user ran from an icon, so they can see our messages. */
printf("\n\n>>> Output has gone into 'sampclnt.out'.");
printf("\n>>> You may wish to use a wide-column editor to view this file.\n\n");
printf("<press any key to continue>");
ch = _getch();

return;
}



//**********************************************************************
//
// DoTests
//
// Purpose:
//
// Hooks up to the SAMPPROV OLE DB provider application, asks the provider
// for all data in the CSV file CUSTOMER.CSV, and logs the resulting data
// to sampclnt.out
//
// Parameters:
//
// none
//
// Return Value:
//
// S_OK- Success
// E_*- Failure
//
//
// Comments:
//
// At a high level, an OLE DB data consumer obtains data by
//
// 1. Getting hooked up to a data provider's Data Source object,
// and initializing that object
// 2. Getting a DBSession object from the Data Source object
// 3. Getting the data from the Rowset object.
//
// DoTests follows these steps by making calls to GetSampprovDataSource,
// GetDBSessionDataSource, and GetDataFromRowset
//
//**********************************************************************


HRESULT DoTests
(
)
{
IDBInitialize* pIDBInitialize = NULL;
IOpenRowset* pIOpenRowset = NULL;
IRowset* pIRowset= NULL;
LPWSTR pwszTableName = L"customer.csv";
HRESULT hr;


hr = GetSampprovDataSource( &pIDBInitialize );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetSampprovDataSource" );
goto error;
}

hr = GetDBSessionFromDataSource( pIDBInitialize, &pIOpenRowset );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetDBSessionFromDataSource" );
goto error;
}

pIDBInitialize->Release();
pIDBInitialize = NULL;

hr = GetRowsetFromDBSession( pIOpenRowset, pwszTableName, &pIRowset );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetRowsetFromDBCreateSession" );
goto error;
}

pIOpenRowset->Release();
pIOpenRowset = NULL;

hr = GetDataFromRowset( pIRowset );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetDataFromRowset" );
goto error;
}
pIRowset->Release();
pIRowset = NULL;
CoFreeUnusedLibraries();

DumpStatusMsg( "\nDone! ");
printf("\n\nFor more data from this run, see the log file sampclnt.out\n" );
return ResultFromScode( S_OK );

error:
if (pIRowset)
pIRowset->Release();
if (pIOpenRowset)
pIOpenRowset->Release();
if (pIDBInitialize)
pIDBInitialize->Release();

return ResultFromScode( hr );
}





//**********************************************************************
//
// GetSampprovDataSource
//
// Purpose:
//
// Calls OLE to find and load the SAMPPROV data provider.
// Returns an IDBInitialize interface pointer on SAMPPROV's
// Data Source object.
//
// Parameters:
//
// IDBInitialize** ppIDBInitialize_out - out pointer through which to return
// IDBInitialize pointer on data
// provider's Data Source object
//
// Return Value:
//
// S_OK- Success
// E_* - Failure
//
//
// Comments:
//
// The call to CoCreateInstance is hard-coded with SAMPPROV's CLSID.
// The pointer returned through ppIDBInitialize_out has been AddRef'ed,
// it must be Release'd later by the caller.
//
//**********************************************************************

HRESULT GetSampprovDataSource
(
IDBInitialize**ppIDBInitialize_out
)
{
IDBInitialize*pIDBInit = NULL;
IDBProperties*pIDBProperties = NULL;
DBPROPSETdbPropSet[1];
DBPROPdbProp[1];

HRESULThr;


DumpStatusMsg( "Connecting to the SampProv sample data provider...\n" );

assert(ppIDBInitialize_out != NULL);

VariantInit(&(dbProp[0].vValue));

// Create an instance of the SampProv sample data provider
hr = CoCreateInstance( CLSID_SampProv, NULL, CLSCTX_INPROC_SERVER,
IID_IDBInitialize, (void **)&pIDBInit );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "CoCreateInstance" );
goto error;
}

// Initialize this provider with the path to the customer.csv file
dbPropSet[0].rgProperties= &dbProp[0];
dbPropSet[0].cProperties= 1;
dbPropSet[0].guidPropertySet= DBPROPSET_DBINIT;

dbProp[0].dwPropertyID= DBPROP_INIT_DATASOURCE;
dbProp[0].dwOptions= DBPROPOPTIONS_REQUIRED;
dbProp[0].colid= DB_NULLID;
V_VT(&(dbProp[0].vValue))= VT_BSTR;
V_BSTR(&(dbProp[0].vValue))= SysAllocString( L"." );
if ( NULL == V_BSTR(&(dbProp[0].vValue)) )
{
DUMP_ERROR_LINENUMBER();
DumpErrorMsg( "SysAllocString failed\n" );
goto error;
}

hr = pIDBInit->QueryInterface( IID_IDBProperties, (void**)&pIDBProperties);
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IDBInitialize::QI for IDBProperties");
goto error;
}

hr = pIDBProperties->SetProperties( 1, &dbPropSet[0]);
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IDBProperties::SetProperties" );
goto error;
}

hr = pIDBInit->Initialize();
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IDBInitialize::Initialize" );
goto error;
}

*ppIDBInitialize_out = pIDBInit;

hr = ResultFromScode( S_OK );

error:
VariantClear( &(dbProp[0].vValue) );

if( pIDBProperties )
pIDBProperties->Release();

if( FAILED(hr) )
{
if (pIDBInit)
pIDBInit->Release();
*ppIDBInitialize_out = NULL;
}

return hr;
}




// **********************************************************************
//
// GetDBSessionFromDataSource
//
// Purpose:
// Calls the provider's Data Source object to get an IOpenRowset interface
// pointer on a DBSession object.
//
// Parameters:
// pIDBInitialize - pointer to Data Source object
// ppIOpenRowset_out - out pointer through which to return
// IOpenRowset pointer on DBSession object
//
// Return Value:
//
// S_OK- Success
// E_* - Failure
//
//
// Comments:
//
// The interface pointer returned through ppIOpenRowset_out has been
// AddRef'ed, the caller must Release it later.
//
//**********************************************************************

HRESULT GetDBSessionFromDataSource
(
IDBInitialize* pIDBInitialize, // [in]
IOpenRowset** ppIOpenRowset_out // [out]
)
{
IDBCreateSession* pIDBCreateSession;
IOpenRowset* pIOpenRowset;
HRESULT hr;


DumpStatusMsg( "Getting a DBSession object from the data source object...\n" );

assert(pIDBInitialize != NULL);
assert(ppIOpenRowset_out != NULL );

hr = pIDBInitialize->QueryInterface( IID_IDBCreateSession, (void**)&pIDBCreateSession);
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IDBInitialize::QI for IDBCreateSession");
goto error;
}

hr = pIDBCreateSession->CreateSession( NULL, IID_IOpenRowset, (IUnknown**)&pIOpenRowset );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IDBCreateSession::CreateSession");
goto error;
}
pIDBCreateSession->Release();
pIDBCreateSession = NULL;

// all went well
*ppIOpenRowset_out = pIOpenRowset;
return ResultFromScode( S_OK );

error:
if (pIDBCreateSession)
pIDBCreateSession->Release();

*ppIOpenRowset_out = NULL;
return ResultFromScode( hr );
}



//**********************************************************************
//
// GetRowsetFromDBSession
//
// Purpose:
// Calls the provider's DBSession object to get an IRowset interface
// pointer on a Rowset object.
//
// Parameters:
// pIOpenRowset - interface pointer on DBSession object
// pwszTableName - name of "table" (in this case text file)
// ppIRowset_out - out pointer through which to return
// IRowset pointer on Rowset object
//
// Return Value:
//
// S_OK- Success
// E_* - Failure
//
// Comments:
//
// The interface pointer returned through ppIRowset_out has been
// AddRef'ed, the caller must Release it later.
//
///**********************************************************************

HRESULT GetRowsetFromDBSession
(
IOpenRowset* pIOpenRowset, // [in]
LPWSTR pwszTableName, // [in]
IRowset** ppIRowset_out // [out]
)
{
DBID dbcolid;
IRowset* pIRowset = NULL;
HRESULT hr;

DumpStatusMsg( "Getting a rowset object from the DBSession object...\n" );

assert(pIOpenRowset != NULL);
assert(ppIRowset_out != NULL );

// tell the provider which table to open
dbcolid.eKind = DBKIND_NAME;
dbcolid.uName.pwszName = pwszTableName;

hr = pIOpenRowset->OpenRowset
(
NULL, // pUnkOuter - we are not aggregating
&dbcolid, // pTableID - the table we want
NULL,// pIndexID - the index we want
IID_IRowset, // riid - interface we want on the rowset object
0, // cProperties - we are niave about props for now
NULL, // prgProperties[]
(IUnknown**)&pIRowset // ppRowset
);
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IOpenRowset::OpenRowset" );
goto error;
}

// all went well
*ppIRowset_out = pIRowset;
return ResultFromScode( S_OK );

error:
if (pIRowset)
pIRowset->Release();
*ppIRowset_out = NULL;

return ResultFromScode( hr );
}


//**********************************************************************
//
// GetDataFromRowset
//
// Purpose:
//
// Pulls the data from a Rowset object.
//
// Parameters:
//
// IRowset*pIRowset - interface pointer on data provider's
// Rowset object
//
// Return Value:
//
// S_OK - Success
// E_* - Failure
//
// Comments:
//
// At a high level, a consumer pulls the data from a Rowset object by:
//
// 1. getting metadata for the Rowset's columns
// 2. using that metadata, along with the consumer's own knowledge of
// how it wants to recieve the data, to create bindings. Bindings
// represent how the actual data in the Rowset's columns is
// actually transferred to the consumer's buffer.
// 3. pass the bindings to the Rowset, and get in return an accessor
// handle that represents that particulr set of bindings
// 4. get the actual data
// 5. clean up the rowset (at a minumum, release the accessor)
//
// GetDataFromRowset performs these steps by calling GetColumnsInfo,
// SetupBindings, CreateAccessor, GetData, and CleanupRowset
//
//**********************************************************************

HRESULT GetDataFromRowset
(
IRowset*pIRowset
)
{
ULONG cCol;
ULONG cbMaxRowSize;// buffer size for 1 row's data
ULONG cBind;
DBBINDINGrgBind[MAX_BINDINGS];
HACCESSORhAccessor= NULL;
DBCOLUMNINFO*pColumnInfo = NULL;
WCHAR*pStringsBuffer = NULL;
HRESULT hr;



DumpStatusMsg( "Reading all the data in the rowset...\n" );

assert(pIRowset != NULL);
assert(g_pIMalloc != NULL);

hr = GetColumnsInfo( pIRowset, &cCol, &pColumnInfo, &pStringsBuffer );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetColumnsInfo");
goto error;
}

hr = SetupBindings( cCol, pColumnInfo, rgBind, &cBind, &cbMaxRowSize );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "SetupBindings");
goto error;
}

hr = CreateAccessor( pIRowset, rgBind, cBind, &hAccessor );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "CreateAccessor" );
goto error;
}

hr = GetData( pIRowset, cbMaxRowSize, hAccessor, rgBind, cBind, pColumnInfo, cCol );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "GetData" );
goto error;
}

g_pIMalloc->Free( pColumnInfo );
pColumnInfo = NULL;
g_pIMalloc->Free( pStringsBuffer );
pStringsBuffer = NULL;

hr = CleanupRowset( pIRowset, hAccessor );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "CleanupRowset" );
goto error;
}

return ResultFromScode( S_OK );

error:
if (pColumnInfo)
g_pIMalloc->Free( pColumnInfo );
if (pStringsBuffer)
g_pIMalloc->Free( pStringsBuffer );

return ResultFromScode( hr );
}






//**********************************************************************
//
// GetColumnsInfo
//
// Purpose:
//
// Obtains information (metadata) about the columns in the rowset - the types
// of the data and so on.
//
//
// Parameters:
// IRowset*pIRowset - interface pointer on data provider's
// Rowset object
// ULONG*pcCol_out - out pointer through which to return
// number of columns in the rowset
// DBCOLUMNINFO**ppColumnInfo_out - out pointer through which to return
// pointer to structure containing
// metadata for the columns in the rowset
// WCHAR**ppStringsBuffer_out - out pointer through which to return
// pointer to table of strings. see comments.
//
// Return Value:
// S_OK - Success
// E_* - Failure
//
//
// Comments:
//
// ppColumnInfo_out and ppStringsBuffer_out are used to return pointers
// to two buffers. These buffers are allocated by the data provider
// (when GetColumnsInfo calls IColumnsInfo::GetColumnInfo). The data
// provider uses IMalloc to allocate the buffers; therefore, the caller
// of this routine must at a later point use IMalloc::Free to free
// both of these buffers. The StringsBuffer contains strings pointed
// to by pointers in the ColumnInfo buffer, therefore the StringsBuffer
// should be freed *after* the ColumnInfo pointer.
//
// GetColumnsInfo calls DumpColumnsInfo to dump the column metadata to
// the log file.
//
//**********************************************************************

HRESULT GetColumnsInfo
(
IRowset*pIRowset,
ULONG*pcCol_out,
DBCOLUMNINFO**ppColumnInfo_out,
WCHAR**ppStringsBuffer_out
)
{
IColumnsInfo* pIColumnsInfo = NULL;
ULONGcCol;
DBCOLUMNINFO*pColumnInfo;
WCHAR*pStringsBuffer;
HRESULT hr;


assert(pIRowset != NULL);
assert(pcCol_out != NULL);
assert(ppColumnInfo_out != NULL);
assert(ppStringsBuffer_out != NULL);

// get column information from the command object via IColumnsInfo
hr = pIRowset->QueryInterface( IID_IColumnsInfo, (void **) &pIColumnsInfo );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "IRowset::QI for IID_IColumnsInfo" );
goto error;
}
hr = pIColumnsInfo->GetColumnInfo(
&cCol,
&pColumnInfo,
&pStringsBuffer );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIColumnsInfo->GetColumnInfo" );
goto error;
}
pIColumnsInfo->Release();
pIColumnsInfo = NULL;

DumpColumnsInfo( pColumnInfo, cCol );

// fill out-params
*pcCol_out = cCol;
*ppColumnInfo_out = pColumnInfo;
*ppStringsBuffer_out = pStringsBuffer;

return ResultFromScode( S_OK );

error:
if (pIColumnsInfo)
pIColumnsInfo->Release();

*pcCol_out = 0;
*ppColumnInfo_out = NULL;
*ppStringsBuffer_out = NULL;

return ResultFromScode( hr );
}


//**********************************************************************
//
// SetupBindings
//
// Purpose:
//
// Creates bindings that map the data in the rowset's columns to
// slots in the consumer's data buffer.
//
// Parameters:
//
// ULONG cCol - number of columns in rowset to bind
// DBCOLUMNINFO*pColumnInfo - pointer to column metadata
// DBBINDING*rgBind_out - out pointer through which to return
// an array of binding structures, one
// structure per column bound
// ULONG*pcBind_out - out pointer through which to return
// the number of columns bound (number
// of valid elements in rgBind_out)
// ULONG*pcMaxRowSize_out - out pointer through which to return
// the buffer size necessary to hold
// the largest row data
//
// Return Value:
// S_OK - Success
// E_* - Failure
//
//
// Comments:
//
//
//**********************************************************************

HRESULT SetupBindings
(
ULONG cCol,
DBCOLUMNINFO*pColumnInfo,
DBBINDING*rgBind_out,
ULONG*pcBind_out,
ULONG*pcMaxRowSize_out
)
{
ULONG dwOffset;
ULONG iCol;
ULONG iBind;


assert(pColumnInfo != NULL);
assert(rgBind_out != NULL);
assert(pcBind_out != NULL);
assert(pcMaxRowSize_out != NULL);

// Create bindings.
// Bind everything as a string just to keep things simple.
dwOffset = 0;
iBind=0;
for (iCol=0; iCol < cCol; iCol++)
{
// Skip columns of type _VECTOR. Probably binary data.
if (pColumnInfo[iCol].wType & DBTYPE_VECTOR)
continue;

rgBind_out[iBind].dwPart= DBPART_VALUE | DBPART_LENGTH |
DBPART_STATUS;
rgBind_out[iBind].eParamIO = DBPARAMIO_NOTPARAM;
rgBind_out[iBind].iOrdinal = pColumnInfo[iCol].iOrdinal;
rgBind_out[iBind].wType= DBTYPE_STR;
rgBind_out[iBind].pTypeInfo = NULL;
rgBind_out[iBind].obValue = dwOffset + offsetof(COLUMNDATA,bData);
rgBind_out[iBind].obLength = dwOffset + offsetof(COLUMNDATA,dwLength);
rgBind_out[iBind].obStatus = dwOffset + offsetof(COLUMNDATA,dwStatus);
rgBind_out[iBind].cbMaxLen = pColumnInfo[iCol].wType == DBTYPE_STR ?
pColumnInfo[iCol].ulColumnSize + sizeof(char) : DEFAULT_CBMAXLENGTH;
rgBind_out[iBind].pObject= NULL;
dwOffset += rgBind_out[iBind].cbMaxLen + offsetof( COLUMNDATA, bData );
dwOffset = ROUND_UP( dwOffset, COLUMN_ALIGNVAL );
iBind++;
}

*pcBind_out = iBind;
*pcMaxRowSize_out = dwOffset;

return ResultFromScode( S_OK );
}



//**********************************************************************
//
// CreateAccessor
//
// Purpose:
//
// Passes a set of bindings to the data provider and recieves in return
// an accessor handle that represents those bindings.
//
// Parameters:
// IRowset*pIRowset - interface pointer on data provider's Rowset
// object
// DBBINDING*rgBind - array of binding structures
// ULONGcBind - number of binding structures in rgBind
// HACCESSOR*phAccessor_out - out pointer through which to return an
// accessor handle that represents all the bindings
// in rgBind
//
// Return Value:
// S_OK - Success
// E_* - Failure
//
//
// Comments:
//
//
//**********************************************************************

HRESULT CreateAccessor
(
IRowset*pIRowset,
DBBINDING*rgBind,
ULONGcBind,
HACCESSOR*phAccessor_out
)
{
IAccessor*pIAccessor = NULL;
HACCESSOR hAccessor;
HRESULT hr;


assert(pIRowset != NULL);
assert(rgBind != NULL);
assert(phAccessor_out != NULL);

// Get an accessor for our bindings from the rowset, via IAccessor
hr = pIRowset->QueryInterface( IID_IAccessor, (void**)&pIAccessor );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIRowset->QI for IID_IAccessor" );
goto error;
}
hr = pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, cBind, rgBind, 0,
&hAccessor, NULL );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIAccessor->CreateAccessor" );
goto error;
}
pIAccessor->Release();
pIAccessor = NULL;

*phAccessor_out = hAccessor;

return ResultFromScode( S_OK );

error:
if (pIAccessor)
pIAccessor->Release();
*phAccessor_out = NULL;

return ResultFromScode( hr );
}


//**********************************************************************
//
// GetData
//
// Purpose:
//
// Reads the data from a rowset.
//
// Parameters:
//
// IRowset* pIRowset - interface pointer on data provider's
// Rowset object
// ULONG cMaxRowSize - size of buffer needed to hold the data
// for the largest row
// HACCESSOR hAccessor - accessor handle representing the set
// of desired bindings
// DBBINDING*rgBind - needed only for pretty printing
// ULONGcBind - for pretty printing
// DBCOLUMNINFO*pColumnInfo - for pretty printing
// ULONGcCol- for pretty printing
//
//
// Return Value:
// S_OK - Success
// E_* - Failure
//
//
//
// Comments:
//
// GetData reads all the rows in the rowset, sequentially.
//
// GetData calls CalcPrettyPrintMaxColWidth, DumpColumnHeadings, and
// DumpRow to dump the row data to a log file.
//
//
//**********************************************************************

HRESULT GetData
(
IRowset*pIRowset,
ULONG cMaxRowSize,
HACCESSOR hAccessor,
DBBINDING*rgBind,
ULONGcBind,
DBCOLUMNINFO*pColumnInfo,
ULONGcCol
)
{
ULONG cRowsObtained;
ULONGiRow;
BYTE*pRowData = NULL;
HROW rghRows[NUMROWS_CHUNK];
HROW*pRows = &rghRows[0];
ULONGcMaxColWidth; // needed for pretty printing
HRESULT hr;


assert(pIRowset != NULL);
assert(rgBind != NULL);
assert(pColumnInfo != NULL);

// create a buffer for row data, big enough to hold the biggest row
pRowData = (BYTE *) malloc( cMaxRowSize );
if (!pRowData)
{
DUMP_ERROR_LINENUMBER();
DumpErrorMsg("GetData: malloc failed\n");
goto error;
}

// pretty print
cMaxColWidth = CalcPrettyPrintMaxColWidth( rgBind, cBind );

// pretty print
DumpColumnHeadings( rgBind, cBind, pColumnInfo, cCol, cMaxColWidth );

// process all the rows, NUMROWS_CHUNK rows at a time
while (1)
{
hr = pIRowset->GetNextRows(
NULL,// hChapter
0,// cRowsToSkip
NUMROWS_CHUNK,// cRowsDesired
&cRowsObtained, // pcRowsObtained
&pRows );// filled in w/ row handles
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIRowset->GetNextRows" );
goto error;
}

if ( cRowsObtained == 0 )// all done, no more rows left to get
break;

// loop over rows obtained, getting data for each
for ( iRow=0; iRow < cRowsObtained; iRow++ )
{
hr = pIRowset->GetData(
rghRows[iRow],
hAccessor,
pRowData );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIRowset->GetData" );
goto error;
}

// pretty print
DumpRow( rgBind, cBind, cMaxColWidth, pRowData );

} 
// release row handles
hr = pIRowset->ReleaseRows( cRowsObtained, rghRows, NULL, NULL, NULL );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIRowset->ReleaseRows" );
goto error;
}
}// end while

// free row data buffer
free( pRowData );
return ResultFromScode( S_OK );

error:
if (pRowData)
free( pRowData );

return ResultFromScode( hr );
}



//**********************************************************************
//
// CleanupRowset
//
// Purpose:
//
// Allows the rowset to perform any necessary cleanup.
//
// Parameters:
//
// IRowset*pIRowset - interface pointer on data provider's Rowset
// object
// HACCESSOR hAccessor - accessor handle to release
//
// Return Value:
//
// S_OK - Success
// E_* - Failure
//
//
// Comments:
//
// In this sample, the only cleanup that the rowset needs to do is
// release the accessor handle.
//
//**********************************************************************

HRESULT CleanupRowset
(
IRowset*pIRowset,
HACCESSOR hAccessor
)
{
IAccessor*pIAccessor = NULL;
HRESULThr;

assert(pIRowset != NULL);

// tell the rowset object it can release the accessor, via IAccessor
hr = pIRowset->QueryInterface( IID_IAccessor, (void**)&pIAccessor );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIRowset->QI for IID_IAccessor" );
goto error;
}
hr = pIAccessor->ReleaseAccessor( hAccessor, NULL );
if (FAILED(hr))
{
DUMP_ERROR_LINENUMBER();
DumpErrorHResult( hr, "pIAccessor->ReleaseAccessor" );
goto error;
}
pIAccessor->Release();
pIAccessor = NULL;

return ResultFromScode( S_OK );

error:
if (pIAccessor)
pIAccessor->Release();

return ResultFromScode( hr );
}