home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / oledb / sampclnt / dump.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-12  |  15.6 KB  |  700 lines

  1. //--------------------------------------------------------------------
  2. // Microsoft OLE DB Sample Consumer
  3. // (C) Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File name: DUMP.CPP
  6. //
  7. //      Dump\output routines for the SAMPCLNT sample OLE DB consumer.
  8. //
  9. //      See README.TXT for more information on the SAMPCLNT sample.
  10. //
  11. // Functions:
  12. //
  13. //      See SAMPCLNT.H for function prototypes
  14. //
  15.  
  16.  
  17.  
  18. #include "sampclnt.h"
  19.  
  20.  
  21.  
  22.  
  23. void DumpErrorMsg
  24.     (
  25.     const char* format,
  26.     ...
  27.     )
  28. {
  29.     va_list argptr;                            
  30.  
  31.     assert(format != NULL);
  32.  
  33.     // log this message to stderr and to our log file
  34.     va_start( argptr, format );
  35.     tvfprintf( stderr, format, argptr);
  36.     tvfprintf( g_fpLogFile, format, argptr);
  37.     va_end( argptr );
  38. }    
  39.  
  40.  
  41.  
  42. void DumpStatusMsg
  43.     (
  44.     const char* format,
  45.     ...
  46.     )
  47. {
  48.     va_list argptr;
  49.  
  50.     assert(format != NULL);
  51.  
  52.     // log this message to stdout and to our log file
  53.     va_start( argptr, format );
  54.     tvfprintf( stdout, format, argptr );
  55.     tvfprintf( g_fpLogFile, format, argptr );
  56.     va_end( argptr );
  57. }    
  58.  
  59.  
  60.  
  61.  
  62. HRESULT DumpErrorHResult
  63.     (
  64.     HRESULT      hr_return,
  65.     const char  *format,            // can be NULL
  66.     ... 
  67.     )
  68. {
  69.     char     buff[100];
  70.     int      cBytesWritten;
  71.     va_list  argptr;
  72.  
  73.     //
  74.     // Dump an error message.
  75.     // Print the text of the HRESULT,
  76.     // Return the HRESULT we were passed.
  77.  
  78.     // these result codes were generated from the oledberr.h 
  79.     static Note ResultCodes[] = {
  80.         // oledberr.h error codes
  81.         NOTE(DB_E_BADACCESSORHANDLE),
  82.         NOTE(DB_E_BADACCESSORHANDLE),
  83.         NOTE(DB_E_ROWLIMITEXCEEDED),
  84.         NOTE(DB_E_READONLYACCESSOR),
  85.         NOTE(DB_E_SCHEMAVIOLATION),
  86.         NOTE(DB_E_BADROWHANDLE),
  87.         NOTE(DB_E_OBJECTOPEN),
  88.         NOTE(DB_E_BADBINDINFO),
  89.         NOTE(DB_SEC_E_PERMISSIONDENIED),
  90.         NOTE(DB_E_NOTAREFERENCECOLUMN),
  91.         NOTE(DB_E_NOCOMMAND),
  92.         NOTE(DB_E_BADBOOKMARK),
  93.         NOTE(DB_E_BADLOCKMODE),
  94.         NOTE(DB_E_PARAMNOTOPTIONAL),
  95.         NOTE(DB_E_BADRATIO),
  96.         NOTE(DB_E_ERRORSINCOMMAND),
  97.         NOTE(DB_E_BADSTARTPOSITION),
  98.         NOTE(DB_E_NOTREENTRANT),
  99.         NOTE(DB_E_NOAGGREGATION),
  100.         NOTE(DB_E_DELETEDROW),
  101.         NOTE(DB_E_CANTFETCHBACKWARDS),
  102.         NOTE(DB_E_ROWSNOTRELEASED),
  103.         NOTE(DB_E_BADSTORAGEFLAG),
  104.         NOTE(DB_E_BADSTATUSVALUE),
  105.         NOTE(DB_E_CANTSCROLLBACKWARDS),
  106.         NOTE(DB_E_INTEGRITYVIOLATION),
  107.         NOTE(DB_E_ABORTLIMITREACHED),
  108.         NOTE(DB_E_DUPLICATEINDEXID),
  109.         NOTE(DB_E_NOINDEX),
  110.         NOTE(DB_E_INDEXINUSE),
  111.         NOTE(DB_E_NOTABLE),
  112.         NOTE(DB_E_CONCURRENCYVIOLATION),
  113.         NOTE(DB_E_BADCOPY),
  114.         NOTE(DB_E_BADPRECISION),
  115.         NOTE(DB_E_BADSCALE),
  116.         NOTE(DB_E_BADID),
  117.         NOTE(DB_E_BADTYPE),
  118.         NOTE(DB_E_DUPLICATECOLUMNID),
  119.         NOTE(DB_E_DUPLICATETABLEID),
  120.         NOTE(DB_E_TABLEINUSE),
  121.         NOTE(DB_E_NOLOCALE),
  122.         NOTE(DB_E_BADRECORDNUM),
  123.         NOTE(DB_E_BOOKMARKSKIPPED),
  124.         NOTE(DB_E_BADPROPERTYVALUE),
  125.         NOTE(DB_E_INVALID),
  126.         NOTE(DB_E_BADACCESSORFLAGS),
  127.         NOTE(DB_E_BADSTORAGEFLAGS),
  128.         NOTE(DB_E_BYREFACCESSORNOTSUPPORTED),
  129.         NOTE(DB_E_NULLACCESSORNOTSUPPORTED),
  130.         NOTE(DB_E_NOTPREPARED),
  131.         NOTE(DB_E_BADACCESSORTYPE),
  132.         NOTE(DB_E_WRITEONLYACCESSOR),
  133.         NOTE(DB_SEC_E_AUTH_FAILED),
  134.         NOTE(DB_E_CANCELED),
  135.         NOTE(DB_E_BADSOURCEHANDLE),
  136.         NOTE(DB_S_ROWLIMITEXCEEDED),
  137.         NOTE(DB_S_COLUMNTYPEMISMATCH),
  138.         NOTE(DB_S_TYPEINFOOVERRIDDEN),
  139.         NOTE(DB_S_BOOKMARKSKIPPED),
  140.         NOTE(DB_S_ENDOFROWSET),
  141.         NOTE(DB_S_BUFFERFULL),
  142.         NOTE(DB_S_CANTRELEASE),
  143.         NOTE(DB_S_DIALECTIGNORED),
  144.         NOTE(DB_S_UNWANTEDPHASE),
  145.         NOTE(DB_S_COLUMNSCHANGED),
  146.         NOTE(DB_S_ERRORSRETURNED),
  147.         NOTE(DB_S_BADROWHANDLE),
  148.         NOTE(DB_S_DELETEDROW),
  149.         NOTE(DB_S_STOPLIMITREACHED),
  150.         NOTE(DB_S_LOCKUPGRADED),
  151.         NOTE(DB_S_PROPERTIESCHANGED),
  152.         NOTE(DB_S_ERRORSOCCURRED),
  153.         NOTE(DB_S_PARAMUNAVAILABLE),
  154.         NOTE(DB_S_MULTIPLECHANGES),
  155.  
  156.         // winerr.h
  157.         NOTE(E_UNEXPECTED),
  158.         NOTE(E_NOTIMPL),
  159.         NOTE(E_OUTOFMEMORY),
  160.         NOTE(E_INVALIDARG),
  161.         NOTE(E_NOINTERFACE),
  162.         NOTE(E_POINTER),
  163.         NOTE(E_HANDLE),
  164.         NOTE(E_ABORT),
  165.         NOTE(E_FAIL),
  166.         NOTE(E_ACCESSDENIED),
  167.         NOTE(S_OK),
  168.         NOTE(S_FALSE),
  169.         NOTE(E_UNEXPECTED),
  170.         NOTE(E_NOTIMPL),
  171.         NOTE(E_OUTOFMEMORY),
  172.         NOTE(E_INVALIDARG),
  173.         NOTE(E_NOINTERFACE),
  174.         NOTE(E_POINTER),
  175.         NOTE(E_HANDLE),
  176.         NOTE(E_ABORT),
  177.         NOTE(E_FAIL),
  178.         NOTE(E_ACCESSDENIED),
  179.         // BindMoniker Errors
  180.         NOTE(MK_E_NOOBJECT),
  181.         NOTE(MK_E_EXCEEDEDDEADLINE),
  182.         NOTE(MK_E_CONNECTMANUALLY),
  183.         NOTE(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED),
  184.         NOTE(STG_E_ACCESSDENIED),
  185.         NOTE(MK_E_SYNTAX),
  186.         NOTE(MK_E_CANTOPENFILE),
  187.     };
  188.  
  189.  
  190.     // Format the message.
  191.     // Print name of hresult code.
  192.  
  193.     if (format)
  194.         {
  195.         va_start( argptr, format );
  196.         cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr );
  197.         va_end( argptr );
  198.         }
  199.     else
  200.         strcpy( buff, "" );
  201.  
  202.     // log to stderr and also to our log file
  203.     tfprintf( stderr, "%.*s: Returned %.30s\n", 
  204.         sizeof(buff), buff, 
  205.         GetNoteString( ResultCodes, NUMELEM(ResultCodes), GetScode(hr_return)) );
  206.         
  207.        tfprintf( g_fpLogFile, "%.*s: Returned %.30s\n", 
  208.         sizeof(buff), buff, 
  209.         GetNoteString( ResultCodes, NUMELEM(ResultCodes), GetScode(hr_return)) );
  210.  
  211.     return ResultFromScode( hr_return );
  212. }
  213.  
  214.  
  215.  
  216.  
  217. void DumpColumnsInfo
  218.     (
  219.     DBCOLUMNINFO* pColInfo,
  220.     ULONG          cCol
  221.     )
  222. {
  223.     ULONG j;
  224.  
  225.     assert(pColInfo != NULL);
  226.      
  227.     tfprintf( g_fpLogFile, "\nColumn Information:\n\n"); 
  228.         
  229.     for (j=0; j < cCol; j++)
  230.         {
  231.         WriteColumnInfo( g_fpLogFile, &pColInfo[j] );
  232.         }
  233. }
  234.  
  235.  
  236.  
  237. void WriteColumnInfo
  238.     (
  239.     FILE*            fp,
  240.     DBCOLUMNINFO*    p 
  241.     )
  242. {
  243.     DBID         *pCol;
  244.     DBKIND      eKind;
  245.     wchar_t     wszGuidBuff[MAX_GUID_STRING];
  246.     wchar_t     wszNameBuff[MAX_GUID_STRING];    
  247.     
  248.     static char *szDbcolkind[] = { "Guid+Name", "Guid+PropID", "Name", 
  249.         "Guid+Name", "Guid+PropID", "PropID", "Guid" };
  250.  
  251.     assert(p != NULL);
  252.  
  253.     // For DBTYPEENUM.  Doesn't need to be in order.
  254.     // Below we mask off the high bits.
  255.     static Note typenotes[] = 
  256.         {
  257.         NOTE(DBTYPE_EMPTY),
  258.         NOTE(DBTYPE_NULL),
  259.         NOTE(DBTYPE_I2),
  260.         NOTE(DBTYPE_I4),
  261.         NOTE(DBTYPE_R4),
  262.         NOTE(DBTYPE_R8),
  263.         NOTE(DBTYPE_CY),
  264.         NOTE(DBTYPE_DATE),
  265.         NOTE(DBTYPE_BSTR),
  266.         NOTE(DBTYPE_IDISPATCH),
  267.         NOTE(DBTYPE_ERROR),
  268.         NOTE(DBTYPE_BOOL),
  269.         NOTE(DBTYPE_VARIANT),
  270.         NOTE(DBTYPE_IUNKNOWN),
  271.         NOTE(DBTYPE_DECIMAL),
  272.         NOTE(DBTYPE_UI1),
  273.         NOTE(DBTYPE_ARRAY),
  274.         NOTE(DBTYPE_BYREF),
  275.         NOTE(DBTYPE_I1),
  276.         NOTE(DBTYPE_UI2),
  277.         NOTE(DBTYPE_UI4),
  278.         NOTE(DBTYPE_I8),
  279.         NOTE(DBTYPE_UI8),
  280.         NOTE(DBTYPE_GUID),
  281.         NOTE(DBTYPE_VECTOR),
  282.         NOTE(DBTYPE_RESERVED),
  283.         NOTE(DBTYPE_BYTES),
  284.         NOTE(DBTYPE_STR),
  285.         NOTE(DBTYPE_WSTR),
  286.         NOTE(DBTYPE_NUMERIC),
  287.         NOTE(DBTYPE_UDT),
  288.         NOTE(DBTYPE_DBDATE),
  289.         NOTE(DBTYPE_DBTIME),
  290.         NOTE(DBTYPE_DBTIMESTAMP),
  291.         };
  292.  
  293.     static Note flagnotes[] = 
  294.         {
  295.         NOTE(DBCOLUMNFLAGS_ISBOOKMARK),
  296.         NOTE(DBCOLUMNFLAGS_MAYDEFER),
  297.         NOTE(DBCOLUMNFLAGS_WRITE),
  298.         NOTE(DBCOLUMNFLAGS_WRITEUNKNOWN),
  299.         NOTE(DBCOLUMNFLAGS_ISFIXEDLENGTH),
  300.         NOTE(DBCOLUMNFLAGS_ISNULLABLE),
  301.         NOTE(DBCOLUMNFLAGS_MAYBENULL),
  302.         NOTE(DBCOLUMNFLAGS_ISLONG),
  303.         NOTE(DBCOLUMNFLAGS_ISROWID),
  304.         NOTE(DBCOLUMNFLAGS_ISROWVER),
  305.         NOTE(DBCOLUMNFLAGS_CACHEDEFERRED),
  306.         };
  307.  
  308.     pCol = & p->columnid;
  309.     eKind = pCol->eKind;
  310.  
  311.     // stringize GUID for pretty printing
  312.     switch (eKind)
  313.         {
  314.         case DBKIND_GUID_NAME:
  315.         case DBKIND_GUID_PROPID:
  316.         case DBKIND_GUID:
  317.             StringFromGUID2( pCol->uGuid.guid, wszGuidBuff, sizeof(wszGuidBuff) );
  318.             break;
  319.         case DBKIND_PGUID_NAME:
  320.         case DBKIND_PGUID_PROPID:          
  321.             StringFromGUID2( *(pCol->uGuid.pguid), wszGuidBuff, sizeof(wszGuidBuff) );
  322.             break;
  323.         default:
  324.             wcscpy( wszGuidBuff, L"<none>" );
  325.             break;    
  326.         }
  327.         
  328.     // stringize name or propID for pretty printing   
  329.     switch (eKind)
  330.         {
  331.         case DBKIND_GUID_NAME:
  332.         case DBKIND_NAME:
  333.         case DBKIND_PGUID_NAME:
  334.             swprintf( wszNameBuff, L"[name=%.50S]", pCol->uName.pwszName ? pCol->uName.pwszName : L"(unknown)" );
  335.             break;
  336.         case DBKIND_GUID_PROPID:
  337.         case DBKIND_PGUID_PROPID:
  338.         case DBKIND_PROPID:
  339.             swprintf( wszNameBuff, L"[propid=%lu]", pCol->uName.ulPropid );
  340.             break;
  341.         default:
  342.             wcscpy( wszNameBuff, L"" );
  343.             break;    
  344.         }   
  345.  
  346.     // pretty print column info
  347.     tfprintf( fp, "ColumnId [kind=%.40s] [guid=%.40S] %.60S\n", 
  348.         szDbcolkind[eKind], wszGuidBuff, wszNameBuff );
  349.         
  350.  
  351.     // Now move on to other stuff...
  352.     // Name in DBCOLUMNINFO different than name in DBCOLUMNID (maybe).
  353.     tfprintf(fp, "  Name          = '%.50S'\n", p->pwszName );
  354.     tfprintf(fp, "  iOrdinal      = %d\n", p->iOrdinal);
  355.     tfprintf(fp, "  wType         = %.100s\n", 
  356.         GetNoteString( typenotes, NUMELEM(typenotes),
  357.             p->wType & (~DBTYPE_BYREF) & (~DBTYPE_ARRAY) & (~DBTYPE_VECTOR) ) );
  358.     if (p->wType & DBTYPE_BYREF)
  359.         tfprintf(fp, "      (BYREF)\n");
  360.     if (p->wType & DBTYPE_ARRAY)
  361.         tfprintf(fp, "      (ARRAY)\n");
  362.     if (p->wType & DBTYPE_VECTOR)
  363.         tfprintf(fp, "      (VECTOR)\n");
  364.     tfprintf(fp, "  ulColumnSize  = %ld\n", p->ulColumnSize );
  365.     tfprintf(fp, "  bPrecision    = %d\n",  p->bPrecision );
  366.     tfprintf(fp, "  bScale        = %d\n",  p->bScale );
  367.     tfprintf(fp, "  dwFlags       = %s\n\n",
  368.         GetNoteStringBitvals( flagnotes, NUMELEM(flagnotes), p->dwFlags ) );
  369.     
  370.  
  371. }
  372.  
  373.  
  374. char* GetNoteString
  375.     ( 
  376.     Note * rgNote, 
  377.     int    cNote,
  378.     DWORD  dwValue 
  379.     )
  380. {
  381.     int j;
  382.  
  383.     assert(rgNote != NULL);
  384.  
  385.     // Scan a table of value/string,
  386.     // return ptr to string found.
  387.  
  388.     for (j=0; j < cNote; j++) {
  389.         if (rgNote[j].dwFlag == dwValue)
  390.             return rgNote[j].szText;
  391.     }
  392.     return "<unknown>";
  393. }
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401. char*    GetNoteStringBitvals
  402.     (
  403.     Note*     rgNote,
  404.     int     cNote,
  405.     DWORD   dwValue 
  406.     )
  407. {
  408.     static char buff[400];
  409.     int j;
  410.  
  411.     assert(rgNote != NULL);
  412.  
  413.     // Make a string that combines all the bits ORed together.
  414.  
  415.     strcpy(buff, "");
  416.     for (j=0; j < cNote; j++) {
  417.         if (rgNote[j].dwFlag & dwValue) {
  418.             if (buff[0])
  419.                 strcat( buff, " | " );
  420.             strcat( buff, rgNote[j].szText );
  421.         }
  422.     }
  423.     assert(strlen(buff) < sizeof(buff));
  424.     return buff;
  425. }
  426.  
  427.  
  428.  
  429.  
  430. ULONG CalcPrettyPrintMaxColWidth
  431.     (
  432.     DBBINDING*    rgBind,
  433.     ULONG       cBind
  434.     )
  435. {
  436.     ULONG    cMaxWidth;
  437.     ULONG   cTotalWidth;
  438.     ULONG    iBind;
  439.  
  440.     assert(rgBind != NULL);
  441.     
  442.     cMaxWidth = DEFAULT_CBMAXLENGTH;
  443.     while (1)
  444.         {
  445.         cTotalWidth = 0;
  446.         
  447.         for (iBind=0; iBind < cBind; iBind++)
  448.             cTotalWidth += min( cMaxWidth, rgBind[iBind].cbMaxLen ) + 1;
  449.         
  450.         if (cTotalWidth < PRETTYPRINT_MAXTOTALWIDTH || cMaxWidth < PRETTYPRINT_MINCOLWIDTH)
  451.             break;
  452.         
  453.         cMaxWidth--;
  454.         }
  455.  
  456.     return cMaxWidth;
  457. }
  458.  
  459.  
  460.  
  461. void DumpColumnHeadings
  462.     (
  463.     DBBINDING*        rgBind, 
  464.     ULONG            cBind, 
  465.     DBCOLUMNINFO*     pColInfo, 
  466.     ULONG            cCol,
  467.     ULONG            cMaxColWidth
  468.     )
  469. {
  470.     ULONG iBind;
  471.  
  472.     assert(rgBind != NULL);
  473.     assert(pColInfo != NULL);
  474.  
  475.     for (iBind=0; iBind < cBind; iBind++)
  476.         tfprintf( g_fpLogFile, "%-*.*S ",
  477.             min( cMaxColWidth, rgBind[iBind].cbMaxLen ),
  478.             min( cMaxColWidth, rgBind[iBind].cbMaxLen ),
  479.             LookupColumnName( pColInfo, cCol, rgBind[iBind].iOrdinal ) );
  480.     tfprintf( g_fpLogFile, "\n" );
  481.     for (iBind=0; iBind < cBind; iBind++)
  482.         tfprintf( g_fpLogFile, "%-*.*s ",
  483.             min( cMaxColWidth, rgBind[iBind].cbMaxLen ),
  484.             min( cMaxColWidth, rgBind[iBind].cbMaxLen ),
  485.             "------------------------------" );
  486.     tfprintf( g_fpLogFile, "\n" );
  487. }
  488.  
  489.  
  490.  
  491. WCHAR* LookupColumnName
  492.     (
  493.     DBCOLUMNINFO*    rgColInfo,
  494.     ULONG             cCol,
  495.     ULONG             iCol 
  496.     )
  497. {
  498.     ULONG j;
  499.  
  500.     assert(rgColInfo != NULL);
  501.  
  502.     // A really slow way to get the column name, given the ordinal.
  503.     // The problem is that result-set ordinals do not necessarily match
  504.     // the index into the ColumnInfo array.
  505.     // (May have bookmark, which is always column 0.)
  506.  
  507.     for (j=0; j < cCol; j++)
  508.         if (rgColInfo[j].iOrdinal == iCol)
  509.             return rgColInfo[j].pwszName;
  510.             
  511.     return L"Error";
  512. }
  513.  
  514.  
  515.  
  516.  
  517. void DumpRow
  518.     (
  519.     DBBINDING*     rgBind,
  520.     ULONG        cBind,
  521.     ULONG        cMaxColWidth,
  522.     BYTE*         pData
  523.     )
  524. {
  525.     ULONG         iBind;
  526.     COLUMNDATA*    pColumn;
  527.     
  528.     assert(rgBind);
  529.     assert( offsetof(COLUMNDATA, dwLength) == 0);    
  530.     
  531.     // Print each column we're bound to.
  532.     for (iBind=0; iBind < cBind; iBind++)
  533.         {
  534.         // Columns are bound differently; not so easy.
  535.         // Print out to at least DEFAULT_CBMAXLENGTH width (pretty),
  536.         // Limit to first dwLength characters.
  537.  
  538.         pColumn = (COLUMNDATA *) (pData + rgBind[iBind].obLength);
  539.         PrintColumn( pColumn, rgBind, iBind, cMaxColWidth );
  540.         }
  541.     tfprintf( g_fpLogFile, "\n" );
  542. }    
  543.  
  544.  
  545.  
  546.  
  547.  
  548. void PrintColumn
  549.     (
  550.     COLUMNDATA    *pColumn,
  551.     DBBINDING     *rgBind,
  552.     ULONG          iBind,
  553.     ULONG          cMaxColWidth 
  554.     )
  555. {
  556.     void*    p;
  557.     ULONG   ulPrintWidth;
  558.     ULONG   ulPrintPrecision;
  559.     DWORD   dwStatus;
  560.     DWORD   dwLength;
  561.     BOOL    fDidVariant;
  562.     BOOL    fIsUnicode;
  563.     char*    sFormat;
  564.     HRESULT hr;
  565.     
  566.     assert(pColumn != NULL);
  567.     assert(rgBind != NULL);
  568.     
  569.     // Pretty print a column.
  570.     // May have different type of binding.
  571.  
  572.     fDidVariant = FALSE;
  573.     fIsUnicode  = FALSE;
  574.     dwStatus = pColumn->dwStatus;
  575.     dwLength = pColumn->dwLength;
  576.  
  577.     if (dwStatus == DBSTATUS_S_ISNULL)
  578.         {
  579.         p = "<null>";
  580.         dwLength = strlen( (char *) p);
  581.         }
  582.     else if (dwStatus == DBBINDSTATUS_UNSUPPORTEDCONVERSION)
  583.         {
  584.         p = "<unsupportedconversion>";
  585.         dwLength = strlen( (char *) p);
  586.         }    
  587.     else
  588.         {
  589.         switch (rgBind[iBind].wType) 
  590.             {
  591.         case DBTYPE_STR:
  592.             // We have a string in our buffer, so use it.
  593.             p = (void *) &pColumn->bData;
  594.             break;
  595.         case DBTYPE_VARIANT:
  596.             // We have a variant in our buffer, so convert to string.
  597.             p = (void *) &pColumn->bData;
  598.             hr = VariantChangeTypeEx(
  599.                     (VARIANT *) p,            // Destination (convert in place)
  600.                     (VARIANT *) p,            // Source
  601.                     LOCALE_SYSTEM_DEFAULT,    // LCID
  602.                     0,                        // dwFlags
  603.                     VT_BSTR );
  604.             if (FAILED(hr))
  605.                 {
  606.                 DumpErrorHResult( hr, "VariantChangeTypeEx, field %d", iBind );
  607.                 return;
  608.                 }
  609.             p = (wchar_t *) (((VARIANT *)p)->bstrVal) ;
  610.             dwLength = ((DWORD *)p)[-1] / sizeof(wchar_t);
  611.             fDidVariant = TRUE;
  612.             fIsUnicode  = TRUE;
  613.             break;
  614.         default:
  615.             p = "??? unknown type ???";
  616.             break;
  617.             }
  618.         }
  619.  
  620.     // Print the column.
  621.     // If it has been truncated or rounded, print a '#' in
  622.     // the far right-hand column.
  623.     ulPrintWidth     = min( cMaxColWidth, rgBind[iBind].cbMaxLen );
  624.     ulPrintPrecision = min( cMaxColWidth, dwLength );
  625.     if (dwStatus == DBSTATUS_S_TRUNCATED ||  cMaxColWidth < dwLength)
  626.     {
  627.         ulPrintWidth--;
  628.         ulPrintPrecision--;
  629.     }
  630.  
  631.     sFormat = fIsUnicode ? "%-*.*S" : "%-*.*s";
  632.  
  633.     tfprintf( g_fpLogFile, sFormat, ulPrintWidth, ulPrintPrecision, p );
  634.  
  635.     if (dwStatus == DBSTATUS_S_TRUNCATED ||  cMaxColWidth < dwLength)
  636.         tfprintf( g_fpLogFile, "#" );
  637.     tfprintf( g_fpLogFile, " " );
  638.  
  639.     // Free memory used by the variant.
  640.     if (fDidVariant)
  641.         VariantClear( (VARIANT *) &pColumn->bData );
  642.         
  643.     return;
  644. }
  645.  
  646.  
  647.  
  648.  
  649. void tfprintf
  650.     (
  651.     FILE*        fp,
  652.     const char* format,
  653.     ... 
  654.     )
  655. {
  656.     int     cBytesWritten;
  657.     char     buff[400];
  658.     va_list argptr;
  659.  
  660.     assert(format != NULL);
  661.     
  662.     // Dump a formatted string.
  663.     // _vsnprintf prevents overflowing our buffer.
  664.     va_start( argptr, format );
  665.     cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr );
  666.     va_end( argptr );
  667.     buff[sizeof(buff)-1] = '\0';
  668.  
  669.     // Can't use fprintf, because string could contain '%'.
  670.     if (fp)
  671.         fputs( buff, fp );
  672. }
  673.  
  674.  
  675.  
  676. void tvfprintf
  677.     (
  678.     FILE*        fp,
  679.     const char* format,
  680.     va_list        argptr 
  681.     )
  682. {
  683.     int     cBytesWritten;
  684.     char     buff[400];
  685.  
  686.  
  687.     assert(format != NULL);
  688.  
  689.     // Dump a formatted string.
  690.     // _vsnprintf prevents overflowing our buffer.
  691.     cBytesWritten = _vsnprintf( buff, sizeof(buff), format, argptr );
  692.     buff[sizeof(buff)-1] = '\0';
  693.  
  694.     // Can't use fprintf, because string could contain '%'.
  695.     if (fp)
  696.         fputs( buff, fp );
  697. }
  698.  
  699.  
  700.