Skip to content

Commit a2adb76

Browse files
authored
Fix Windows crash saving tool definitions to a read-only install dir (#5)
On Windows GetDataDirectory() returned ".", so tool definitions and CSV recordings were written relative to the working directory. For an app installed under C:\Program Files\ that directory is read-only, so create_directories() failed; SaveToolDefinition() used the throwing overload, so the filesystem_error propagated to main() and terminated the app when clicking "Add Tool Definition" (reported with no device attached, but the save path is device-independent). - GetDataDirectory(): use %LOCALAPPDATA%\IR Tracking App on Windows, a writable per-user location, mirroring the macOS Application Support handling. _wdupenv_s avoids the MSVC getenv deprecation warning under /W4 and preserves non-ASCII user names. - SaveToolDefinition(): use the non-throwing create_directories overload; log and skip persistence on failure instead of crashing. The tool stays active for the session even if it can't be written to disk.
1 parent be55964 commit a2adb76

1 file changed

Lines changed: 44 additions & 22 deletions

File tree

src/ViewerWindow.cpp

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,62 @@ double GetCurrentUnixTimestamp() {
3838
}
3939

4040
// Base directory for app-managed data (tool definitions under Tools/, recorded
41-
// CSVs). On macOS the app runs as a .app bundle whose working directory is "/",
42-
// so relative paths like "Tools" would resolve under root and silently fail to
43-
// load/save. Use a stable per-user location instead, so it behaves the same
44-
// whether launched from Finder or a terminal. On Windows and Linux the working
45-
// directory is kept unchanged (paths stay relative to ".").
41+
// CSVs). When launched from a bundle/installer the working directory is not a
42+
// writable, predictable location: a macOS .app runs with CWD "/", and a Windows
43+
// app installed under C:\Program Files\ has a read-only CWD. Relative paths like
44+
// "Tools" would then resolve somewhere unwritable and create/load/save fails (on
45+
// Windows the throwing create_directories used to crash the whole app). Use a
46+
// stable per-user location instead, so behavior is the same whether launched
47+
// from Finder/Explorer or a terminal. Falls back to "." only when the per-user
48+
// location can't be determined (or on platforms without one wired up, e.g. Linux).
4649
static fs::path GetDataDirectory()
4750
{
51+
fs::path dir;
4852
#if defined(__APPLE__)
4953
if (const char* home = std::getenv("HOME"); home && *home)
54+
dir = fs::path(home) / "Library" / "Application Support" / "IR Tracking App";
55+
#elif defined(_WIN32)
56+
// %LOCALAPPDATA% (e.g. C:\Users\<user>\AppData\Local) — a writable per-user
57+
// location. _wdupenv_s avoids the MSVC getenv deprecation warning and keeps
58+
// non-ASCII user names intact (wide -> fs::path is exact on Windows).
59+
wchar_t* localAppData = nullptr;
60+
size_t len = 0;
61+
if (_wdupenv_s(&localAppData, &len, L"LOCALAPPDATA") == 0 && localAppData && *localAppData)
62+
dir = fs::path(localAppData) / "IR Tracking App";
63+
free(localAppData); // safe even when null
64+
#endif
65+
66+
if (dir.empty())
67+
return fs::path("."); // per-user location unavailable / not wired up
68+
69+
std::error_code ec;
70+
fs::create_directories(dir, ec);
71+
if (ec)
5072
{
51-
fs::path dir = fs::path(home) / "Library" / "Application Support" / "IR Tracking App";
52-
std::error_code ec;
53-
fs::create_directories(dir, ec);
54-
if (ec)
55-
{
56-
// Don't fall back to "." here: launched from Finder the working
57-
// directory is "/", so that would reintroduce the very problem this
58-
// helper exists to avoid. Log why and return the per-user path
59-
// anyway, so subsequent file operations fail in an actionable place.
60-
std::cerr << "Failed to create data directory " << dir.string()
61-
<< ": " << ec.message() << std::endl;
62-
}
63-
return dir;
73+
// Don't fall back to "." here: the install/bundle CWD is typically not
74+
// writable, so that would reintroduce the very problem this helper exists
75+
// to avoid. Log why and return the per-user path anyway, so subsequent
76+
// file operations fail in an actionable, discoverable place.
77+
std::cerr << "Failed to create data directory " << dir.string()
78+
<< ": " << ec.message() << std::endl;
6479
}
65-
#endif
66-
return fs::path(".");
80+
return dir;
6781
}
6882

6983
void ViewerWindow::SaveToolDefinition(const Tool &tool)
7084
{
7185
fs::path toolsDir = GetDataDirectory() / "Tools";
72-
if (!fs::exists(toolsDir))
86+
std::error_code ec;
87+
fs::create_directories(toolsDir, ec); // no-op when it already exists
88+
if (ec)
7389
{
74-
fs::create_directories(toolsDir); // Create the Tools directory if it doesn't exist
90+
// Never throw out of this UI click handler: a read-only data directory
91+
// previously propagated a filesystem_error all the way up and terminated
92+
// the app. Log and skip persisting instead; the tool is still active for
93+
// this session (the tracker already accepted it), it just isn't saved.
94+
std::cerr << "Failed to create tools directory " << toolsDir.string()
95+
<< ": " << ec.message() << std::endl;
96+
return;
7597
}
7698

7799
// Construct the filename using the tool name

0 commit comments

Comments
 (0)