DUMP.CPP
//-------------------------------------------------------------------- 
// Microsoft OLE DB Sample Consumer 
// (C) Copyright 1995 - 1996 Microsoft Corporation. All Rights Reserved. 
// 
// File name: DUMP.CPP 
// 
//      Dump\output routines for the SAMPCLNT sample OLE DB consumer. 
// 
//      See README.TXT for more information on the SAMPCLNT sample. 
// 
// Functions: 
// 
//      See SAMPCLNT.H for function prototypes 
// 
 
 
 
#include "sampclnt.h" 
 
 
 
 
void DumpErrorMsg 
( 
    const char* format, 
    ... 
) 
{ 
va_list argptr;     
 
assert(format != NULL); 
 
// log this message to stderr and to our log file 
va_start( argptr, format ); 
tvfprintf( stderr, format, argptr); 
    tvfprintf( g_fpLogFile, format, argptr); 
va_end( argptr ); 
}     
 
 
 
void DumpStatusMsg 
( 
    const char* format, 
    ... 
) 
{ 
va_list argptr; 
 
assert(format != NULL); 
 
// log this message to stdout and to our log file 
va_start( argptr, format ); 
tvfprintf( stdout, format, argptr ); 
    tvfprintf( g_fpLogFile, format, argptr ); 
va_end( argptr ); 
}     
 
 
 
 
HRESULT DumpErrorHResult 
( 
HRESULT      hr_return, 
const char  *format,// can be NULL 
...  
) 
{ 
char     buff[100]; 
int      cBytesWritten; 
va_list  argptr; 
 
// 
// Dump an error message. 
// Print the text of the HRESULT, 
// Return the HRESULT we were passed. 
 
    // these result codes were generated from the oledberr.h  
static Note ResultCodes[] = { 
// oledberr.h error codes 
NOTE(DB_E_BADACCESSORHANDLE), 
NOTE(DB_E_BADACCESSORHANDLE), 
NOTE(DB_E_ROWLIMITEXCEEDED), 
NOTE(DB_E_READONLYACCESSOR), 
NOTE(DB_E_SCHEMAVIOLATION), 
NOTE(DB_E_BADROWHANDLE), 
NOTE(DB_E_OBJECTOPEN), 
NOTE(DB_E_BADBINDINFO), 
NOTE(DB_SEC_E_PERMISSIONDENIED), 
NOTE(DB_E_NOTAREFERENCECOLUMN), 
NOTE(DB_E_NOCOMMAND), 
NOTE(DB_E_BADBOOKMARK), 
NOTE(DB_E_BADLOCKMODE), 
NOTE(DB_E_PARAMNOTOPTIONAL), 
NOTE(DB_E_BADRATIO), 
NOTE(DB_E_ERRORSINCOMMAND), 
NOTE(DB_E_CANNOTFREE), 
NOTE(DB_E_BADSTARTPOSITION), 
NOTE(DB_E_NOTREENTRANT), 
NOTE(DB_E_NOAGGREGATION), 
NOTE(DB_E_DELETEDROW), 
NOTE(DB_E_CANTFETCHBACKWARDS), 
NOTE(DB_E_ROWSNOTRELEASED), 
NOTE(DB_E_BADSTORAGEFLAG), 
NOTE(DB_E_BADSTATUSVALUE), 
NOTE(DB_E_CANTSCROLLBACKWARDS), 
NOTE(DB_E_INTEGRITYVIOLATION), 
NOTE(DB_E_ABORTLIMITREACHED), 
NOTE(DB_E_ROWSETINCOMMAND), 
NOTE(DB_E_DUPLICATEINDEXID), 
NOTE(DB_E_NOINDEX), 
NOTE(DB_E_INDEXINUSE), 
NOTE(DB_E_NOTABLE), 
NOTE(DB_E_CONCURRENCYVIOLATION), 
NOTE(DB_E_BADCOPY), 
NOTE(DB_E_BADPRECISION), 
NOTE(DB_E_BADSCALE), 
NOTE(DB_E_BADID), 
NOTE(DB_E_BADTYPE), 
NOTE(DB_E_DUPLICATECOLUMNID), 
NOTE(DB_E_DUPLICATETABLEID), 
NOTE(DB_E_TABLEINUSE), 
NOTE(DB_E_NOLOCALE), 
NOTE(DB_E_BADRECORDNUM), 
NOTE(DB_E_BOOKMARKSKIPPED), 
NOTE(DB_E_BADPROPERTYVALUE), 
NOTE(DB_E_INVALID), 
NOTE(DB_E_BADACCESSORFLAGS), 
NOTE(DB_E_BADSTORAGEFLAGS), 
NOTE(DB_E_BYREFACCESSORNOTSUPPORTED), 
NOTE(DB_E_NULLACCESSORNOTSUPPORTED), 
NOTE(DB_E_NOTPREPARED), 
NOTE(DB_E_BADACCESSORTYPE), 
NOTE(DB_E_WRITEONLYACCESSOR), 
NOTE(DB_SEC_E_AUTH_FAILED), 
NOTE(DB_E_CANCELED), 
NOTE(DB_E_BADSOURCEHANDLE), 
NOTE(DB_S_ROWLIMITEXCEEDED), 
NOTE(DB_S_COLUMNTYPEMISMATCH), 
NOTE(DB_S_TYPEINFOOVERRIDDEN), 
NOTE(DB_S_BOOKMARKSKIPPED), 
NOTE(DB_S_ENDOFROWSET), 
NOTE(DB_S_BUFFERFULL), 
NOTE(DB_S_CANTRELEASE), 
NOTE(DB_S_DIALECTIGNORED), 
NOTE(DB_S_UNWANTEDPHASE), 
NOTE(DB_S_COLUMNSCHANGED), 
NOTE(DB_S_ERRORSRETURNED), 
NOTE(DB_S_BADROWHANDLE), 
NOTE(DB_S_DELETEDROW), 
NOTE(DB_S_STOPLIMITREACHED), 
NOTE(DB_S_LOCKUPGRADED), 
NOTE(DB_S_PROPERTIESCHANGED), 
NOTE(DB_S_ERRORSOCCURRED), 
NOTE(DB_S_PARAMUNAVAILABLE), 
NOTE(DB_S_MULTIPLECHANGES), 
 
// winerr.h 
NOTE(E_UNEXPECTED), 
NOTE(E_NOTIMPL), 
NOTE(E_OUTOFMEMORY), 
NOTE(E_INVALIDARG), 
NOTE(E_NOINTERFACE), 
NOTE(E_POINTER), 
NOTE(E_HANDLE), 
NOTE(E_ABORT), 
NOTE(E_FAIL), 
NOTE(E_ACCESSDENIED), 
NOTE(S_OK), 
NOTE(S_FALSE), 
NOTE(E_UNEXPECTED), 
NOTE(E_NOTIMPL), 
NOTE(E_OUTOFMEMORY), 
NOTE(E_INVALIDARG), 
NOTE(E_NOINTERFACE), 
NOTE(E_POINTER), 
NOTE(E_HANDLE), 
NOTE(E_ABORT), 
NOTE(E_FAIL), 
NOTE(E_ACCESSDENIED), 
// BindMoniker Errors 
NOTE(MK_E_NOOBJECT), 
NOTE(MK_E_EXCEEDEDDEADLINE), 
NOTE(MK_E_CONNECTMANUALLY), 
NOTE(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED), 
NOTE(STG_E_ACCESSDENIED), 
NOTE(MK_E_SYNTAX), 
NOTE(MK_E_CANTOPENFILE), 
}; 
 
 
// Format the message. 
// Print name of hresult code. 
 
if (format) 
{ 
va_start( argptr, format ); 
cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr ); 
va_end( argptr ); 
} 
else 
strcpy( buff, "" ); 
 
// log to stderr and also to our log file 
tfprintf( stderr, "%.*s: Returned %.30s\n",  
sizeof(buff), buff,  
GetNoteString( ResultCodes, NUMELEM(ResultCodes), GetScode(hr_return)) ); 
         
   tfprintf( g_fpLogFile, "%.*s: Returned %.30s\n",  
sizeof(buff), buff,  
GetNoteString( ResultCodes, NUMELEM(ResultCodes), GetScode(hr_return)) ); 
 
return ResultFromScode( hr_return ); 
} 
 
 
 
 
void DumpColumnsInfo 
( 
    DBCOLUMNINFO* pColInfo, 
    ULONG      cCol 
    ) 
{ 
ULONG j; 
 
assert(pColInfo != NULL); 
  
tfprintf( g_fpLogFile, "\nColumn Information:\n\n");  
     
for (j=0; j < cCol; j++) 
{ 
WriteColumnInfo( g_fpLogFile, &pColInfo[j] ); 
} 
} 
 
 
 
void WriteColumnInfo 
( 
FILE*fp, 
DBCOLUMNINFO*p  
) 
{ 
DBID     *pCol; 
    DBKIND      eKind; 
wchar_t wszGuidBuff[MAX_GUID_STRING]; 
    wchar_t     wszNameBuff[MAX_GUID_STRING];     
     
static char *szDbcolkind[] = { "Guid+Name", "Guid+PropID", "Name",  
    "Guid+Name", "Guid+PropID", "PropID", "Guid" }; 
 
assert(p != NULL); 
 
// For DBTYPEENUM.  Doesn't need to be in order. 
// Below we mask off the high bits. 
static Note typenotes[] =  
    { 
NOTE(DBTYPE_EMPTY), 
NOTE(DBTYPE_NULL), 
NOTE(DBTYPE_I2), 
NOTE(DBTYPE_I4), 
NOTE(DBTYPE_R4), 
NOTE(DBTYPE_R8), 
NOTE(DBTYPE_CY), 
NOTE(DBTYPE_DATE), 
NOTE(DBTYPE_BSTR), 
NOTE(DBTYPE_IDISPATCH), 
NOTE(DBTYPE_ERROR), 
NOTE(DBTYPE_BOOL), 
NOTE(DBTYPE_VARIANT), 
NOTE(DBTYPE_IUNKNOWN), 
NOTE(DBTYPE_DECIMAL), 
NOTE(DBTYPE_UI1), 
NOTE(DBTYPE_ARRAY), 
NOTE(DBTYPE_BYREF), 
NOTE(DBTYPE_I1), 
NOTE(DBTYPE_UI2), 
NOTE(DBTYPE_UI4), 
NOTE(DBTYPE_I8), 
NOTE(DBTYPE_UI8), 
NOTE(DBTYPE_GUID), 
NOTE(DBTYPE_VECTOR), 
NOTE(DBTYPE_RESERVED), 
NOTE(DBTYPE_BYTES), 
NOTE(DBTYPE_STR), 
NOTE(DBTYPE_WSTR), 
NOTE(DBTYPE_NUMERIC), 
NOTE(DBTYPE_UDT), 
NOTE(DBTYPE_DBDATE), 
NOTE(DBTYPE_DBTIME), 
NOTE(DBTYPE_DBTIMESTAMP), 
    }; 
 
static Note flagnotes[] =  
    { 
NOTE(DBCOLUMNFLAGS_ISBOOKMARK), 
NOTE(DBCOLUMNFLAGS_MAYDEFER), 
NOTE(DBCOLUMNFLAGS_WRITE), 
NOTE(DBCOLUMNFLAGS_WRITEUNKNOWN), 
NOTE(DBCOLUMNFLAGS_ISFIXEDLENGTH), 
NOTE(DBCOLUMNFLAGS_ISNULLABLE), 
NOTE(DBCOLUMNFLAGS_MAYBENULL), 
NOTE(DBCOLUMNFLAGS_ISLONG), 
NOTE(DBCOLUMNFLAGS_ISROWID), 
NOTE(DBCOLUMNFLAGS_ISROWVER), 
NOTE(DBCOLUMNFLAGS_CACHEDEFERRED), 
}; 
 
pCol = & p->columnid; 
    eKind = pCol->eKind; 
 
    // stringize GUID for pretty printing 
    switch (eKind) 
        { 
        case DBKIND_GUID_NAME: 
        case DBKIND_GUID_PROPID: 
        case DBKIND_GUID: 
        StringFromGUID2( pCol->uGuid.guid, wszGuidBuff, sizeof(wszGuidBuff) ); 
            break; 
        case DBKIND_PGUID_NAME: 
        case DBKIND_PGUID_PROPID:           
        StringFromGUID2( *(pCol->uGuid.pguid), wszGuidBuff, sizeof(wszGuidBuff) ); 
            break; 
        default: 
            wcscpy( wszGuidBuff, L"<none>" ); 
            break;     
        } 
         
    // stringize name or propID for pretty printing    
    switch (eKind) 
        { 
        case DBKIND_GUID_NAME: 
        case DBKIND_NAME: 
        case DBKIND_PGUID_NAME: 
            swprintf( wszNameBuff, L"[name=%.50S]", pCol->uName.pwszName ? pCol->uName.pwszName : L"(unknown)" ); 
            break; 
        case DBKIND_GUID_PROPID: 
        case DBKIND_PGUID_PROPID: 
        case DBKIND_PROPID: 
            swprintf( wszNameBuff, L"[propid=%lu]", pCol->uName.ulPropid ); 
            break; 
        default: 
            wcscpy( wszNameBuff, L"" ); 
            break;     
        }    
 
    // pretty print column info 
    tfprintf( fp, "ColumnId [kind=%.40s] [guid=%.40S] %.60S\n",  
        szDbcolkind[eKind], wszGuidBuff, wszNameBuff ); 
         
 
// Now move on to other stuff... 
// Name in DBCOLUMNINFO different than name in DBCOLUMNID (maybe). 
tfprintf(fp, "  Name          = '%.50S'\n", p->pwszName ); 
tfprintf(fp, "  iOrdinal      = %d\n", p->iOrdinal); 
tfprintf(fp, "  wType         = %.100s\n",  
GetNoteString( typenotes, NUMELEM(typenotes), 
    p->wType & (~DBTYPE_BYREF) & (~DBTYPE_ARRAY) & (~DBTYPE_VECTOR) ) ); 
if (p->wType & DBTYPE_BYREF) 
tfprintf(fp, "      (BYREF)\n"); 
if (p->wType & DBTYPE_ARRAY) 
tfprintf(fp, "      (ARRAY)\n"); 
if (p->wType & DBTYPE_VECTOR) 
tfprintf(fp, "      (VECTOR)\n"); 
tfprintf(fp, "  ulColumnSize  = %ld\n", p->ulColumnSize ); 
tfprintf(fp, "  bPrecision    = %b\n",  p->bPrecision ); 
tfprintf(fp, "  bScale        = %b\n",  p->bScale ); 
tfprintf(fp, "  dwFlags       = %s\n\n", 
GetNoteStringBitvals( flagnotes, NUMELEM(flagnotes), p->dwFlags ) ); 
 
 
} 
 
 
char* GetNoteString 
    (  
Note * rgNote,  
int    cNote, 
DWORD  dwValue  
) 
{ 
int j; 
 
assert(rgNote != NULL); 
 
// Scan a table of value/string, 
// return ptr to string found. 
 
for (j=0; j < cNote; j++) { 
if (rgNote[j].dwFlag == dwValue) 
return rgNote[j].szText; 
} 
return "<unknown>"; 
} 
 
 
 
 
 
 
 
char*GetNoteStringBitvals 
( 
Note* rgNote, 
int     cNote, 
DWORD   dwValue  
) 
{ 
static char buff[400]; 
int j; 
 
assert(rgNote != NULL); 
 
// Make a string that combines all the bits ORed together. 
 
strcpy(buff, ""); 
for (j=0; j < cNote; j++) { 
if (rgNote[j].dwFlag & dwValue) { 
if (buff[0]) 
strcat( buff, " | " ); 
strcat( buff, rgNote[j].szText ); 
} 
} 
assert(strlen(buff) < sizeof(buff)); 
return buff; 
} 
 
 
 
 
ULONG CalcPrettyPrintMaxColWidth 
    ( 
    DBBINDING*rgBind, 
    ULONG       cBind 
    ) 
{ 
ULONGcMaxWidth; 
    ULONG   cTotalWidth; 
    ULONGiBind; 
 
assert(rgBind != NULL); 
 
cMaxWidth = DEFAULT_CBMAXLENGTH; 
while (1) 
{ 
cTotalWidth = 0; 
 
for (iBind=0; iBind < cBind; iBind++) 
cTotalWidth += min( cMaxWidth, rgBind[iBind].cbMaxLen ) + 1; 
 
if (cTotalWidth < PRETTYPRINT_MAXTOTALWIDTH || cMaxWidth < PRETTYPRINT_MINCOLWIDTH) 
break; 
 
cMaxWidth--; 
} 
 
return cMaxWidth; 
} 
 
 
 
void DumpColumnHeadings 
( 
DBBINDING*rgBind,  
ULONGcBind,  
DBCOLUMNINFO* pColInfo,  
ULONGcCol, 
    ULONGcMaxColWidth 
) 
{ 
ULONG iBind; 
 
assert(rgBind != NULL); 
    assert(pColInfo != NULL); 
  
for (iBind=0; iBind < cBind; iBind++) 
tfprintf( g_fpLogFile, "%-*.*S ", 
min( cMaxColWidth, rgBind[iBind].cbMaxLen ), 
min( cMaxColWidth, rgBind[iBind].cbMaxLen ), 
LookupColumnName( pColInfo, cCol, rgBind[iBind].iOrdinal ) ); 
tfprintf( g_fpLogFile, "\n" ); 
for (iBind=0; iBind < cBind; iBind++) 
tfprintf( g_fpLogFile, "%-*.*s ", 
min( cMaxColWidth, rgBind[iBind].cbMaxLen ), 
min( cMaxColWidth, rgBind[iBind].cbMaxLen ), 
"------------------------------" ); 
tfprintf( g_fpLogFile, "\n" ); 
} 
 
 
 
WCHAR* LookupColumnName 
( 
DBCOLUMNINFO*rgColInfo, 
ULONG cCol, 
ULONG iCol  
) 
{ 
ULONG j; 
 
assert(rgColInfo != NULL); 
 
// A really slow way to get the column name, given the ordinal. 
// The problem is that result-set ordinals do not necessarily match 
// the index into the ColumnInfo array. 
// (May have bookmark, which is always column 0.) 
 
for (j=0; j < cCol; j++) 
if (rgColInfo[j].iOrdinal == iCol) 
return rgColInfo[j].pwszName; 
             
return L"Error"; 
} 
 
 
 
 
void DumpRow 
( 
    DBBINDING* rgBind, 
    ULONGcBind, 
    ULONGcMaxColWidth, 
    BYTE* pData 
    ) 
{ 
ULONG iBind; 
    COLUMNDATA*pColumn; 
     
assert(rgBind); 
assert( offsetof(COLUMNDATA, dwLength) == 0); 
     
// Print each column we're bound to. 
for (iBind=0; iBind < cBind; iBind++) 
{ 
// Columns are bound differently; not so easy. 
// Print out to at least DEFAULT_CBMAXLENGTH width (pretty), 
// Limit to first dwLength characters. 
 
pColumn = (COLUMNDATA *) (pData + rgBind[iBind].obLength); 
PrintColumn( pColumn, rgBind, iBind, cMaxColWidth ); 
} 
tfprintf( g_fpLogFile, "\n" ); 
}     
 
 
 
 
 
void PrintColumn 
( 
COLUMNDATA    *pColumn, 
DBBINDING     *rgBind, 
ULONG          iBind, 
ULONG          cMaxColWidth  
) 
{ 
void*p; 
ULONG   ulPrintWidth; 
ULONG   ulPrintPrecision; 
DWORD   dwStatus; 
DWORD   dwLength; 
BOOL    fDidVariant; 
BOOL    fIsUnicode; 
char*sFormat; 
HRESULT hr; 
 
    assert(pColumn != NULL); 
    assert(rgBind != NULL); 
     
// Pretty print a column. 
// May have different type of binding. 
 
fDidVariant = FALSE; 
fIsUnicode  = FALSE; 
dwStatus = pColumn->dwStatus; 
dwLength = pColumn->dwLength; 
 
if (dwStatus == DBSTATUS_S_ISNULL) 
{ 
p = "<null>"; 
dwLength = strlen( (char *) p); 
} 
    else if (dwStatus == DBBINDSTATUS_UNSUPPORTEDCONVERSION) 
        { 
        p = "<unsupportedconversion>"; 
dwLength = strlen( (char *) p); 
        }     
else 
{ 
switch (rgBind[iBind].wType)  
{ 
case DBTYPE_STR: 
// We have a string in our buffer, so use it. 
p = (void *) &pColumn->bData; 
break; 
case DBTYPE_VARIANT: 
// We have a variant in our buffer, so convert to string. 
p = (void *) &pColumn->bData; 
hr = VariantChangeTypeEx( 
(VARIANT *) p,// Destination (convert in place) 
(VARIANT *) p,// Source 
LOCALE_SYSTEM_DEFAULT,// LCID 
0,// dwFlags 
VT_BSTR ); 
if (FAILED(hr)) 
            { 
DumpErrorHResult( hr, "VariantChangeTypeEx, field %d", iBind ); 
                return; 
                } 
p = (wchar_t *) (((VARIANT *)p)->bstrVal) ; 
dwLength = ((DWORD *)p)[-1] / sizeof(wchar_t); 
fDidVariant = TRUE; 
fIsUnicode  = TRUE; 
break; 
default: 
p = "??? unknown type ???"; 
break; 
} 
} 
 
// Print the column. 
// If it has been truncated or rounded, print a '#' in 
// the far right-hand column. 
ulPrintWidth     = min( cMaxColWidth, rgBind[iBind].cbMaxLen ); 
ulPrintPrecision = min( cMaxColWidth, dwLength ); 
if (dwStatus == DBSTATUS_S_TRUNCATED ||  cMaxColWidth < dwLength) 
{ 
ulPrintWidth--; 
ulPrintPrecision--; 
} 
 
sFormat = fIsUnicode ? "%-*.*S" : "%-*.*s"; 
 
tfprintf( g_fpLogFile, sFormat, ulPrintWidth, ulPrintPrecision, p ); 
 
if (dwStatus == DBSTATUS_S_TRUNCATED ||  cMaxColWidth < dwLength) 
tfprintf( g_fpLogFile, "#" ); 
tfprintf( g_fpLogFile, " " ); 
 
// Free memory used by the variant. 
if (fDidVariant) 
VariantClear( (VARIANT *) &pColumn->bData ); 
         
return; 
} 
 
 
 
 
void tfprintf 
( 
FILE*fp, 
const char* format, 
...  
) 
{ 
int cBytesWritten; 
char buff[400]; 
va_list argptr; 
 
assert(format != NULL); 
 
// Dump a formatted string. 
    // _vsnprintf prevents overflowing our buffer. 
va_start( argptr, format ); 
cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr ); 
va_end( argptr ); 
buff[sizeof(buff)-1] = '\0'; 
 
// Can't use fprintf, because string could contain '%'. 
if (fp) 
fputs( buff, fp ); 
} 
 
 
 
void tvfprintf 
( 
FILE*fp, 
const char* format, 
va_listargptr  
) 
{ 
int cBytesWritten; 
char buff[400]; 
 
 
assert(format != NULL); 
 
// Dump a formatted string. 
    // _vsnprintf prevents overflowing our buffer. 
cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr ); 
buff[sizeof(buff)-1] = '\0'; 
 
// Can't use fprintf, because string could contain '%'. 
if (fp) 
fputs( buff, fp ); 
}