2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OutputDebugString()を受信するプログラム

Last updated at Posted at 2025-03-04

はじめに

Win32APIを利用するプログラムや、.NETのDebug.WriteLine()で利用されるデバッガに出力するAPI(OutputDebugString())を受信するサンプルプログラムです

OutputDebugString()は、デバッガーに引数の文字列を送信するAPIです

通常、IDEのコンソールに表示されますが、プログラムを単独で起動してもDebugViewなどで受信することができます

つまり、必要な時にだけデータを収集できるログとして利用することができます。ログファイルのように後片付けも不要なので、重宝していました

DebugView簡易版を作りたくなったので、ChatGPT(o3-min-high)に相談したのですが、一部正しくなかったので修正して動くようにしてみました

OutputDebugString()を受信するプログラムの流れ

OutputDebugString()は、共有メモリ(DBWIN_BUFFER)経由でデータを受け渡します。
また、プロセス間でリソースを共有するため、DBWIN_BUFFER_READYDBWIN_DATA_READYという2つのイベントを利用して共有メモリの書き込み排他制御を行います

DBWIN_BUFFER:受け取り側が作成する共有メモリ領域

DBWIN_BUFFER_READY:受け取り側が共有メモリ領域を作成し受信の準備ができたことを通知するイベント

DBWIN_DATA_READYOutputDebugString()呼び出し側が共有メモリに書き込み完了したことを通知するイベント

OutputDebugString()呼び出し側の流れ(概要)

  1. 共有メモリDBWIN_BUFFERがあれば開く。なければ処理を終了する
  2. イベントDBWIN_BUFFER_READYDBWIN_DATA_READYを開く。なければ処理を終了する
  3. イベントDBWIN_BUFFER_READYが通知され(シグナル状態にな)るまで待機
  4. 通知する文字列を共有メモリに書き込む
  5. イベントBWIN_DATA_READYをシグナル状態にして受信側に通知する

OutputDebugString()受信側の流れ(概要)

  1. 2つのイベントDBWIN_BUFFER_READYと、DBWIN_DATA_READYを作成
  2. 共有メモリDBWIN_BUFFERを作成(CreateFileMapping())。共有メモリは4Kbytで、最初の4ByteはプロセスID、残りは受信するNull終端文字列
  3. DBWIN_BUFFER_READYイベントをシグナル状態にして、共有メモリが使用可能になったことをOutputDebugString()呼び出し側プロセスへ通知
  4. DBWIN_DATA_READYイベントが通知されるまで待機(WaitForSingleObject())。OutputDebugString()が共有メモリに書き込み完了したタイミングで通知される
  5. 共有メモリからプロセスIDと、通知された文字列を読み込む
  6. ステップ3に戻る(次の文字列を受信する)

OutputDebugString()を受信するソース

#include <windows.h>
#include <stdio.h>

// DBWIN(共有メモリの構造体)
struct DBWIN_BUFFER_STRUCT {
    DWORD dwProcessId;
    char szData[4096 - sizeof(DWORD)];
};

void cleanUp(HANDLE hBufferReady, HANDLE hDataReady = NULL, HANDLE hMapFile = NULL, LPVOID pBuf = NULL)
{
    if (!pBuf) { UnmapViewOfFile(pBuf); }
    if (!hMapFile) { CloseHandle(hMapFile); }
    if (!hDataReady) { CloseHandle(hDataReady); }
    if (!hBufferReady) { CloseHandle(hBufferReady); }
}

int main(void)
{
    // DBWIN_BUFFER_READY イベントを作成(自動リセットではなく手動リセット)
    HANDLE hBufferReady = CreateEvent(NULL, TRUE, FALSE, TEXT("DBWIN_BUFFER_READY"));
    if (!hBufferReady) {
        printf("CreateEvent(DBWIN_BUFFER_READY) failed with error %lu\n", GetLastError());
        return 1;
    }

    // DBWIN_DATA_READY イベントを作成
    HANDLE hDataReady = CreateEvent(NULL, FALSE, FALSE, TEXT("DBWIN_DATA_READY"));
    if (!hDataReady) {
        printf("CreateEvent(DBWIN_DATA_READY) failed with error %lu\n", GetLastError());
        cleanUp(hBufferReady);
        return 1;
    }

    // 共有メモリ領域 DBWIN_BUFFER を作成
    HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
        0, sizeof(DBWIN_BUFFER_STRUCT), TEXT("DBWIN_BUFFER"));
    if (!hMapFile) {
        printf("CreateFileMapping failed with error %lu\n", GetLastError());
        cleanUp(hBufferReady, hDataReady);
        return 1;
    }

    // バッファが使用可能になったことを通知
    SetEvent(hBufferReady);

    // 共有メモリをマップする
    LPVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, sizeof(DBWIN_BUFFER_STRUCT));
    if (!pBuf) {
        printf("MapViewOfFile failed with error %lu\n", GetLastError());
        cleanUp(hBufferReady, hDataReady, hMapFile);
        return 1;
    }
    

    printf("Waiting for debug output...\n");

    // 無限ループでデバッグ文字列を受信
    while (true) {
        // DBWIN_DATA_READY イベントがシグナル状態になるのを待つ
        DWORD dwWait = WaitForSingleObject(hDataReady, INFINITE);
        if (dwWait == WAIT_OBJECT_0) {
            // 共有メモリから送信元プロセスIDと文字列を取得
            DBWIN_BUFFER_STRUCT* pDBWin = reinterpret_cast<DBWIN_BUFFER_STRUCT*>(pBuf);
            DWORD pid = pDBWin->dwProcessId;
            char* msg = pDBWin->szData;
            printf("PID %lu: %s", pid, msg);
        }
    }

    cleanUp(hBufferReady, hDataReady, hMapFile, pBuf);

    return 0;
}

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?