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