MFCのTRACEマクロは手軽ですが、Debugビルド限定で、Releaseビルドでは何も出力しません。デバッガがアタッチされていない環境でもログを拾いたい場面では、Win32 APIのOutputDebugStringが使えます。
この記事では、OutputDebugStringの使い方とTRACEとの違いを解説します。
| 今やりたいこと | 読む記事 |
|---|---|
| コードからログを出したい | この記事(OutputDebugString) |
| デバッガなしでログを受け取りたい | DebugViewでログを捕捉する |
| Release固有の不具合をPDBや最適化設定まで含めて追いたい | Releaseビルドのデバッグ手順 |
目次
OutputDebugStringの基本
OutputDebugStringは、デバッガの出力ウィンドウに文字列を送るWin32 APIです。
OutputDebugString(_T("ここを通過しました\n"));
Visual Studioでデバッグ実行中なら、出力ウィンドウ(Ctrl+Alt+O)に表示されます。TRACEと異なり、Releaseビルドでもコンパイルされ、呼び出されます。
TRACEとの違い
| TRACE | OutputDebugString | |
|---|---|---|
| 定義場所 | MFCマクロ(afx.h) | Win32 API(debugapi.h) |
| Releaseビルド | 消える(何も出力しない) | 残る(呼び出される) |
| 書式指定 | printf形式で直接書ける | 文字列のみ(書式は自前で組み立てる) |
| デバッガなしで捕捉 | 不可 | DebugView等で可能 |
書式付き出力を行うヘルパー
OutputDebugString単体ではprintf形式の書式指定ができません。CString::Formatや _stprintf_sと組み合わせて使います。
// CString::Format を使う例
CString msg;
msg.Format(_T("値 = %d, 名前 = %s\n"), nValue, strName.GetString());
OutputDebugString(msg);
よく使うなら、ヘルパー関数を用意すると便利です。
void DebugLog(LPCTSTR fmt, ...)
{
va_list args;
va_start(args, fmt);
CString msg;
msg.FormatV(fmt, args);
OutputDebugString(msg);
va_end(args);
}
// 使い方
DebugLog(_T("OnTimer: count = %d\n"), m_nCount);
Releaseビルドで使う際の注意
OutputDebugStringはReleaseビルドでも呼び出されます。以下の点に注意してください。
- パフォーマンス面では、デバッガがアタッチされていない場合はほぼゼロコストですが、文字列の組み立て処理は実行されます。高頻度ループ内に入れると影響が出ることがあります
- 情報漏洩にも注意します。Releaseビルドの出力はDebugView等で誰でも見えるため、機密情報(パスワード、認証トークンなど)は出力しないでください
- Releaseで出力を無効にしたい場合は、条件コンパイルで #ifdef _DEBUGで囲むか、マクロで切り替えます
#ifdef _DEBUG
#define MY_TRACE(fmt, ...) DebugLog(fmt, __VA_ARGS__)
#else
#define MY_TRACE(fmt, ...) ((void)0)
#endif
デバッガなしで出力を見る:DebugView
Visual Studioをアタッチできない環境では、SysinternalsのDebugViewでOutputDebugStringの出力を捕捉できます。受信、フィルタ、ファイル保存の手順は「DebugViewでリリース後のログを見る」を参照してください。Release固有の不具合をPDBや最適化設定まで含めて追う場合は「Releaseビルドのデバッグ手順」へ進みます。
まとめ
- OutputDebugStringはReleaseビルドでも動作するWin32 APIのログ出力です
- TRACEはDebug限定、OutputDebugStringはReleaseでも残る点が最大の違いです
- 書式付き出力にはヘルパー関数を用意するのが実務的です
- Releaseで使う場合は、機密情報の漏洩とパフォーマンスに注意してください
