Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to pass formatted output to OutputDebugString?

Typically, when I need to do debug output in Windows, I use the following C code segment:

#ifdef _DEBUG
#define DBGPRINT( kwszDebugFormatString, ... ) \
{ \
    wprintf_s( L"[%s:%d] ", __FUNCTIONW__, __LINE__ ); \
    wprintf_s( kwszDebugFormatString, __VA_ARGS__ ); \
}
#else
#define DBGPRINT( kwszDebugFormatString, ...) ;;
#endif

I'd like to recode this to use OutputDebugString which doesn't accept a format string. I consider statically allocating a small array on the stack (e.g., WCHAR wszBuf[100] = {0};) to be somewhat crude since it might consume significantly more or less memory than what is allocated and either truncate output or waste memory. I wrote the following code that addresses all of these concerns, but concerns me because the macro is a bit large.

#ifdef _DEBUG
#define DBGPRINT( kwszDebugFormatString, ... ) \
{ \
    INT iLineNumber = __LINE__; \
    FILE *fileNul = NULL; \
    INT cbFormatString = 0; \
    PWCHAR wszDebugString = NULL; \
    size_t st_Offset = 0; \
    \
    /* Determine the number of characters in the format string by writing to NUL. */\
    fopen_s( &fileNul, "nul", "w" ); \
    cbFormatString = fwprintf_s( fileNul, L"[%s:%d]", __FUNCTIONW__, iLineNumber ) * sizeof( WCHAR ); \
    cbFormatString += fwprintf_s( fileNul, kwszDebugFormatString, __VA_ARGS__ ) * sizeof( WCHAR ) + 2; \
    \
    /* Depending on the size of the format string, allocate space on the stack or the heap. */ \
    wszDebugString = (PWCHAR)_malloca( cbFormatString ); \
    \
    /* Populate the buffer with the contents of the format string. */ \
    StringCbPrintfW( wszDebugString, cbFormatString, L"[%s:%d]", __FUNCTIONW__, iLineNumber ); \
    StringCbLengthW( wszDebugString, cbFormatString, &st_Offset ); \
    StringCbPrintfW( &wszDebugString[st_Offset / sizeof(WCHAR)], cbFormatString - st_Offset, kwszDebugFormatString, __VA_ARGS__ ); \
    \
    OutputDebugStringW( wszDebugString ); \
    \
    _freea( wszDebugString ); \
    fclose( fileNul ); \
}
#else
#define DBGPRINT( kwszDebugFormatString, ... ) ;;
#endif

A few notes:

  • I used _malloca() and _freea() so that stack allocations could be used if the output should be small enough.
  • I'm not aware of any way to get the correct size of a fully expanded format string. The best solution I could come up with was to dump it to NUL and calculate the correct sizes from there.
  • I used a macro because I don't believe there is a simple way to achieve the same result when using a function.

My question is, quite simply, would this macro be considered bad practice for one reason or another (particularly for size or inefficiency)? If so, what alternatives should I be considering?

Solution

In case anyone else is wondering, I used the suggestions from the comments and the selected answer and came up with the following code. Thank you so much to everyone who commented or answered - feel free to add more if you have a clever way of doing this that you want to share!

#ifdef _DEBUG
#define DBGPRINT(kwszDebugFormatString, ...) _DBGPRINT(__FUNCTIONW__, __LINE__, kwszDebugFormatString, __VA_ARGS__)

VOID _DBGPRINT( LPCWSTR kwszFunction, INT iLineNumber, LPCWSTR kwszDebugFormatString, ... ) \
{
    INT cbFormatString = 0;
    va_list args;
    PWCHAR wszDebugString = NULL;
    size_t st_Offset = 0;

    va_start( args, kwszDebugFormatString );

    cbFormatString = _scwprintf( L"[%s:%d] ", kwszFunction, iLineNumber ) * sizeof( WCHAR );
    cbFormatString += _vscwprintf( kwszDebugFormatString, args ) * sizeof( WCHAR ) + 2;

    /* Depending on the size of the format string, allocate space on the stack or the heap. */
    wszDebugString = (PWCHAR)_malloca( cbFormatString );

    /* Populate the buffer with the contents of the format string. */
    StringCbPrintfW( wszDebugString, cbFormatString, L"[%s:%d] ", kwszFunction, iLineNumber );
    StringCbLengthW( wszDebugString, cbFormatString, &st_Offset );
    StringCbVPrintfW( &wszDebugString[st_Offset / sizeof(WCHAR)], cbFormatString - st_Offset, kwszDebugFormatString, args );

    OutputDebugStringW( wszDebugString );

    _freea( wszDebugString );
    va_end( args );
}
#else
#define DBGPRINT( kwszDebugFormatString, ... ) ;;
#endif
like image 205
Kevin Avatar asked Mar 14 '15 13:03

Kevin


2 Answers

I'm currently using this way. Simple and fast. Absurdly simple :)

#define MY_PRINTF(...) {char cad[512]; sprintf(cad, __VA_ARGS__);  OutputDebugString(cad);}
like image 156
ffelagund Avatar answered Oct 22 '22 17:10

ffelagund


It would be much simpler and less error prone if you'd put all the code into a normal varargs function and then called that in your macro, similar to this:

void dbgprint(const wchar_t *func, int line, const wchar_t *fmt, ...) {
   // Fomat the string, maybe with vsprintf, log it, etc.
}

#define DBGPRINT(fmt, ...) dbgprint(__WFUNCTION__, __LINE__, fmt, __VA_ARGS__)
like image 23
sth Avatar answered Oct 22 '22 17:10

sth