Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Created April 16, 2019 19:20
Show Gist options
  • Save mmozeiko/bfe0ce5762c496f92d8c775983c41694 to your computer and use it in GitHub Desktop.
Save mmozeiko/bfe0ce5762c496f92d8c775983c41694 to your computer and use it in GitHub Desktop.
Example how to capture OutputDebugString on Windows
#include <windows.h>
#include <intrin.h>
#define Assert(x) do { if (!(x)) __debugbreak(); } while (0)
static struct
{
DWORD process_id;
char data[4096 - sizeof(DWORD)];
}* ods_buffer;
static HANDLE ods_data_ready;
static HANDLE ods_buffer_ready;
static DWORD WINAPI ods_proc(LPVOID arg)
{
DWORD ret = 0;
HANDLE stderr = GetStdHandle(STD_ERROR_HANDLE);
Assert(stderr);
for (;;)
{
SetEvent(ods_buffer_ready);
DWORD wait = WaitForSingleObject(ods_data_ready, INFINITE);
Assert(wait == WAIT_OBJECT_0);
DWORD length = 0;
while (length < sizeof(ods_buffer->data) && ods_buffer->data[length] != 0)
{
length++;
}
if (length != 0)
{
DWORD written;
WriteFile(stderr, ods_buffer->data, length, &written, NULL);
}
}
}
void ods_capture()
{
if (IsDebuggerPresent())
{
return;
}
HANDLE file = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(*ods_buffer), "DBWIN_BUFFER");
Assert(file != INVALID_HANDLE_VALUE);
ods_buffer = MapViewOfFile(file, SECTION_MAP_READ, 0, 0, 0);
Assert(ods_buffer);
ods_buffer_ready = CreateEventA(NULL, FALSE, FALSE, "DBWIN_BUFFER_READY");
Assert(ods_buffer_ready);
ods_data_ready = CreateEventA(NULL, FALSE, FALSE, "DBWIN_DATA_READY");
Assert(ods_data_ready);
HANDLE thread = CreateThread(NULL, 0, ods_proc, NULL, 0, NULL);
Assert(thread);
}
int main()
{
ods_capture();
OutputDebugStringA("hello\n");
OutputDebugStringW(L"unicode?\n");
OutputDebugStringA("...\n");
}
@mmozeiko
Copy link
Author

When ods_data_ready event is signaled you can compare process_id with your process id, if it does not match, then ignore it - skip WriteFile in my example.

@sir-pinecone
Copy link

Thank you!

@OrangeLightning219
Copy link

Hi, I'm having trouble integrating this into my game that's using DirectX 11 for rendering. When my renderer is enabled output from OutputDebugString is properly redirected to the console but when I exit my game everything freezes and I have to kill the process from task manager. If I disable the renderer everything works fine and my game closes properly. Right now when I exit my game I don't release any DirectX objects and let the os clean them up, but I also tried releasing the device and device context before exiting but it didn't change anything. Do you have any ideas what may be causing this?

@mmozeiko
Copy link
Author

mmozeiko commented Jan 25, 2025

Are you doing "no crt" build? If yes, then if you're returning from entry point you must shut down all threads, otherwise process will be waiting on these threads. Alternatively explicitly call ExitProcess that will terminate regardless whether other threads are running or not (that's what CRT does for you after main or WinMain returns).

@OrangeLightning219
Copy link

I'm using jai and I'm not sure if it's linking with crt (but I think it doesn't). Calling ExitProcess at the end of main didn't change anything. I just found that creating the DirectX device with the D3D11_CREATE_DEVICE_DEBUG flag is causing this issue. If I create the device without this flag everything works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment