fix(log): share one handle between stdout and stderr on Windows

On Windows stdout and stderr were reopened onto the same log path with
two separate freopen() calls. Each stream then had its own file handle
and its own write offset into the one file, so the two writers
overwrote each other and interleaved into a garbled log.

- stdout is reopened (truncating the file per run), stderr is pointed at
  the same underlying handle via _dup2 so both share one offset and
  append in order.
- stderr is first given a valid descriptor, since a GUI-subsystem
  process may start with no fd 2, and is left unbuffered so crash
  diagnostics are not lost.

Also corrects a stale comment that named the old log file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 18:16:50 -05:00
co-authored by Claude Opus 4.8
parent 45d8bdccf0
commit 13059412ce
+6 -3
View File
@@ -49,6 +49,7 @@
#ifdef _WIN32
#include "platform/windows_backdrop.h"
#include <windows.h>
#include <io.h>
#include <dwmapi.h>
#include <shlobj.h>
#include <propkey.h>
@@ -553,7 +554,7 @@ static void drawWindowBackdrop(dragonx::App& app, ImDrawList* bgDL, ImVec2 p0, I
}
// ── Window-dimension diagnostic tracer ──────────────────────────────────────────────────────
// Always-on (writes to stderr → dragonx-debug.log on Windows) so it's visible in release builds,
// Always-on (writes to stderr → dragonx-stdout.log on Windows) so it's visible in release builds,
// unlike DRAGONX_DEBUG-gated DEBUG_LOGF. Grep the log for "[WINLOG]" to trace how the window size
// changes when dragging between monitors of different DPI. Low volume — only fires on window
// resize / move / DPI-scale events. TODO: remove once the multi-monitor sizing bug is resolved.
@@ -919,8 +920,10 @@ int main(int argc, char* argv[])
// above so the two writers don't interleave/contend on one file.
{
std::string logPath = (std::filesystem::path(dragonx::util::Platform::getObsidianDragonDir()) / "dragonx-stdout.log").string();
freopen(logPath.c_str(), "w", stdout);
freopen(logPath.c_str(), "a", stderr);
freopen(logPath.c_str(), "w", stdout); // per-run truncation
freopen("NUL", "w", stderr); // GUI subsystem may have no valid fd 2 — give it one first
_dup2(_fileno(stdout), _fileno(stderr)); // stderr now shares stdout's handle + file offset
setvbuf(stderr, nullptr, _IONBF, 0); // keep stderr unbuffered (crash diagnostics)
}
// Install crash handler for diagnostics
SetUnhandledExceptionFilter(CrashHandler);