Skip to content

Handle invalid CWD when creating file dialogs - #840

Merged
cyanfish merged 3 commits into
cyanfish:masterfrom
nikoskalogridis:fix/savefiledialog-invalid-cwd-crash
Jul 22, 2026
Merged

Handle invalid CWD when creating file dialogs#840
cyanfish merged 3 commits into
cyanfish:masterfrom
nikoskalogridis:fix/savefiledialog-invalid-cwd-crash

Conversation

@nikoskalogridis

Copy link
Copy Markdown
Contributor

Summary

  • fix a crash when SaveFileDialog is created while the process current working directory has been deleted/unmounted
  • proactively validate and repair current directory before creating file dialogs
  • retry SaveFileDialog construction after setting a safe fallback directory
  • apply the same pre-check before OpenFileDialog creation

Root cause

On Linux/GTK, constructing Eto file dialogs may call Environment.CurrentDirectory (GetCwd). If the original working directory no longer exists, this throws System.IO.FileNotFoundException and the dialog never opens.

Changes

  • NAPS2.Lib/EtoForms/EtoDialogHelper.cs
    • add EnsureCurrentDirectoryIsAccessible()
    • add SetSafeCurrentDirectory() with fallback order:
      1. Environment.SpecialFolder.UserProfile
      2. Path.GetTempPath()
    • add CreateSaveFileDialog() wrapper that catches FileNotFoundException, resets CWD, and retries
    • use CreateSaveFileDialog() in PromptToSavePdfOrImage, PromptToSavePdf, and PromptToSaveImage
    • call EnsureCurrentDirectoryIsAccessible() in PromptToImport

Validation

  • file diagnostics for EtoDialogHelper.cs: clean
  • full build not run in this environment (dotnet CLI unavailable)

Fixes #839

@nikoskalogridis
nikoskalogridis marked this pull request as ready for review June 23, 2026 19:50
@cyanfish
cyanfish force-pushed the master branch 2 times, most recently from 2cda1cb to ba9d7ec Compare July 18, 2026 22:51
@nikoskalogridis

Copy link
Copy Markdown
Contributor Author

@cyanfish it would be great if you can consider this for the next release

Comment thread NAPS2.Lib/EtoForms/EtoDialogHelper.cs Outdated

private static void SetSafeCurrentDirectory()
{
var fallbackDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpecialFolder.MyDocuments probably makes more sense for Windows (Windows users don't usually access the user profile folder)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated: fallback now uses SpecialFolder.MyDocuments on Windows and SpecialFolder.UserProfile on non-Windows.

Comment thread NAPS2.Lib/EtoForms/EtoDialogHelper.cs Outdated
var fallbackDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (string.IsNullOrWhiteSpace(fallbackDirectory) || !Directory.Exists(fallbackDirectory))
{
fallbackDirectory = Path.GetTempPath();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, and TempPath doesn't really make sense as a fallback anyway

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed the TempPath fallback entirely.

{
fallbackDirectory = Path.GetTempPath();
}
Environment.CurrentDirectory = fallbackDirectory;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting FileDialog.Directory probably makes more sense than changing the environment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to prefer FileDialog.Directory defaults now: if no explicit path is provided, dialogs are pointed at the platform fallback directory. Environment.CurrentDirectory is only set in the constructor-exception recovery path.

Comment thread NAPS2.Lib/EtoForms/EtoDialogHelper.cs Outdated
try
{
var currentDirectory = Directory.GetCurrentDirectory();
if (!Directory.Exists(currentDirectory))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do three different checks:

  • Check if the current directory exists
  • Catch exceptions when checking the current directory
  • Catch FileNotFoundException when creating a dialog

Which of these is actually necessary? Ideally we'd handle this in one way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — simplified this to one primary handling path: catch FileNotFoundException during dialog construction, set a platform fallback CWD, then retry once. Removed the separate pre-check logic.

{
FileName = GetDefaultFileName(defaultPath, lastExt!)
};
var sd = CreateSaveFileDialog();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm, I assume you've tested this just on Linux (and not Windows/Mac)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, issue reproduction and validation were on Linux. I have not run Windows/Mac runtime validation in this environment.

@nikoskalogridis

Copy link
Copy Markdown
Contributor Author

Implemented the requested updates in commit 273089d:

  • switched fallback preference to SpecialFolder.MyDocuments on Windows, SpecialFolder.UserProfile elsewhere
  • removed the Path.GetTempPath fallback
  • simplified handling to a single recovery path (constructor catch + one retry)
  • set FileDialog.Directory to a platform fallback when no explicit path is provided
  • kept environment mutation only for constructor-failure recovery

Also confirming: reproduction and validation for this bug were done on Linux in this environment.

@nikoskalogridis
nikoskalogridis force-pushed the fix/savefiledialog-invalid-cwd-crash branch from 273089d to 0e6096b Compare July 22, 2026 06:05
@cyanfish
cyanfish merged commit e21d9b2 into cyanfish:master Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SaveFileDialog crashes when current working directory is deleted/unmounted at runtime

2 participants