///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Microsoft WMI OLE DB Client test app
// (C) Copyright 1999 Microsoft Corporation. All Rights Reserved.
//
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#define INITGUID
#define DBINITCONSTANTS
#include "wmiclnt.h"
#include <headers.h>

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

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void AllocateAndConvertAnsiToUnicode(char * pstr, WCHAR *& pszW)
{
    pszW = NULL;

    int nSize = strlen(pstr);
    if (nSize != 0 ){

        // Determine number of wide characters to be allocated for the
        // Unicode string.
        nSize++;
		try{
			pszW = new WCHAR[nSize * 2];
			if (NULL != pszW){
            // Covert to Unicode.
				MultiByteToWideChar(CP_ACP, 0, pstr, nSize,pszW,nSize);
	        }	
		}
		catch(...){
			throw;
		}
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
BOOL GetDataFromCommandLine(int argc,int & i, WCHAR *& p, char * argv[])
{
    i++;
    if( i > argc ){
        return FALSE;
    }

    AllocateAndConvertAnsiToUnicode(argv[i],p);


    return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
BOOL ParseCommandLine(int argc, char * argv[])
{
    
    BOOL	fFlag = FALSE;

	if ( argc > 1 ){
        g_pwcsTable = NULL;
        g_pwcsNamespace = NULL;

        //======================================================================
        //  Now, go thru and get the ones we support
        //======================================================================
        for( int i = 0; i < argc; i++){

            //==================================================================
            //  NAMESPACE
            //==================================================================
            if(_stricmp(argv[i],"-Namespace")== 0){
                GetDataFromCommandLine(argc,i,g_pwcsNamespace,argv);
            }
            //==================================================================
            //  TABLE
            //==================================================================
            if(_stricmp(argv[i],"-Table")== 0){
                GetDataFromCommandLine(argc,i,g_pwcsTable,argv);
            }
        }
        if( g_pwcsTable && g_pwcsNamespace ) {
            fFlag = TRUE;
        }
    }

    if( !fFlag ){
		printf( "-Namespace name\n" );
		printf( "-Table tablename\n" );
	} 


    return fFlag;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
void InitializeLogFile()
{
	time_t	ttime;
 
  	g_fpLogFile = fopen( "wmiclnt.out", "at");
    if (!g_fpLogFile){
    	DumpErrorMsg( "Warning: cannot open log file wmiclnt.out\n" );
    }
  
  	time(&ttime);
    
    DumpStatusMsg( "\n-------------------------\n\n");
    DumpStatusMsg( "running wmiclnt.exe\n%s\n\n", ctime(&ttime) );
}




//**********************************************************************
//  
// GetWMIOLEDBDataSource
// 
// Purpose:
//
//     Calls OLE to find and load the WMIOLEDB data provider. 
//     Returns an IDBInitialize interface pointer on WMIOLEDB'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 WMIOLEDB's CLSID.
//     The pointer returned through ppIDBInitialize_out has been AddRef'ed,
//     it must be Release'd later by the caller.
//  
//**********************************************************************

HRESULT GetWMIOLEDBDataSource(IDBInitialize**	ppIDBInitialize_out	)
{
	IDBInitialize*	pIDBInit = NULL;
	IDBProperties*	pIDBProperties = NULL;
	DBPROPSET		dbPropSet[1];
	DBPROP			dbProp[1];

	HRESULT	hr;


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

	assert(ppIDBInitialize_out != NULL);

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

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

    //===========================================================================
	// Initialize this provider with the namespace
    //===========================================================================
	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( g_pwcsNamespace );
    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 );
}    





//**********************************************************************
// 
// 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;
    ULONG			cCol;
    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(TMPCOLUMNDATA,bData);
 		rgBind_out[iBind].obLength  = dwOffset + offsetof(TMPCOLUMNDATA,dwLength);
        rgBind_out[iBind].obStatus  = dwOffset + offsetof(TMPCOLUMNDATA,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( TMPCOLUMNDATA, 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
// 	ULONG		cBind           - 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,
	ULONG		cBind,
	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	
//     ULONG			cBind	  	- for pretty printing  
//     DBCOLUMNINFO*	pColumnInfo - for pretty printing  
//     ULONG			cCol		- 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,	  	  
                 ULONG			cBind,	  	    DBCOLUMNINFO*	pColumnInfo,    ULONG			cCol		  
	)
{
	ULONG 	cRowsObtained;
	ULONG	iRow;
	BYTE*	pRowData = NULL;
	HROW 	rghRows[NUMROWS_CHUNK];
	HROW*	pRows = &rghRows[0];
	ULONG	cMaxColWidth;	  	// 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 , the only cleanup that the rowset needs to do is
//     release the accessor handle. 
// 
//**********************************************************************

HRESULT CleanupRowset
	(
	IRowset*	pIRowset,
	HACCESSOR 	hAccessor
	)
{
	IAccessor*	pIAccessor = NULL;
	HRESULT		hr;
    
    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 );    
}


//**********************************************************************
// 
// 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;
	DBBINDING		rgBind[MAX_BINDINGS];
	HACCESSOR		hAccessor		= 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 );    
}





//**********************************************************************
//  
//  DoTests
//  
//  Purpose:
//
//     Hooks up to the  WMIOLEDB provider application,

//  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 GetWMIOLEDBDataSource,
//     GetDBSessionDataSource, and GetDataFromRowset    
//  
//**********************************************************************


HRESULT DoTests
	(
	)
{
	IDBInitialize*	    pIDBInitialize 	= NULL;
    IOpenRowset*        pIOpenRowset    = NULL;
    IRowset*		    pIRowset		= NULL;
	HRESULT			    hr;


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

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

    pIDBInitialize->Release();
    pIDBInitialize = NULL;    

    hr = GetRowsetFromDBSession( pIOpenRowset, g_pwcsTable, &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 wmiclnt.out\n" );	
	return ResultFromScode( S_OK );
    
error:    
	if (pIRowset) 
		pIRowset->Release();
    if (pIOpenRowset)
        pIOpenRowset->Release();    
    if (pIDBInitialize)
    	pIDBInitialize->Release();	    
	
	return ResultFromScode( hr );
}						    















///////////////////////////////////////////////////////////////////////////////////////////////////////////
//extern "C" int wmain( int argc, WCHAR * argv[] )
int main( int argc, char * argv[] )
{
	DWORD   dwVersion;
	HRESULT hr;
    BOOL 	fOleInitialized = FALSE;
	char	ch;

    //=====================================================================================
    //  Parse the command line
    //=====================================================================================
    if( !ParseCommandLine(argc,argv) ){
        return 1;
    }

    InitializeLogFile();

	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 'wmiclnt.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 -1;
    
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 'wmiclnt.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 2;    
}