fix: open folder pickers at current paths
This commit is contained in:
@@ -17,7 +17,7 @@ public sealed partial class MainWindow
|
||||
try
|
||||
{
|
||||
completion.TrySetResult(
|
||||
LegacyBackgroundFileDialog.Show(
|
||||
LegacyFileDialog.ShowBackgroundFile(
|
||||
WindowNative.GetWindowHandle(this),
|
||||
initialDirectory));
|
||||
}
|
||||
@@ -40,14 +40,54 @@ public sealed partial class MainWindow
|
||||
return completion.Task;
|
||||
}
|
||||
|
||||
private static class LegacyBackgroundFileDialog
|
||||
private Task<string?> PickOperatorFolderAsync(
|
||||
string? initialDirectory,
|
||||
string title)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(title);
|
||||
var canonicalInitialDirectory = initialDirectory is null
|
||||
? null
|
||||
: Path.GetFullPath(initialDirectory);
|
||||
var completion = new TaskCompletionSource<string?>(
|
||||
TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
void ShowPicker()
|
||||
{
|
||||
try
|
||||
{
|
||||
completion.TrySetResult(
|
||||
LegacyFileDialog.ShowFolder(
|
||||
WindowNative.GetWindowHandle(this),
|
||||
canonicalInitialDirectory,
|
||||
title));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
completion.TrySetException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (DispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
ShowPicker();
|
||||
}
|
||||
else if (!DispatcherQueue.TryEnqueue(ShowPicker))
|
||||
{
|
||||
completion.TrySetException(
|
||||
new InvalidOperationException("The folder picker could not reach the UI thread."));
|
||||
}
|
||||
|
||||
return completion.Task;
|
||||
}
|
||||
|
||||
private static class LegacyFileDialog
|
||||
{
|
||||
private const int CancelledHResult = unchecked((int)0x800704C7);
|
||||
private const uint FileSystemPathDisplayName = 0x80058000;
|
||||
private static readonly Guid FileOpenDialogClassId =
|
||||
new("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7");
|
||||
|
||||
public static string? Show(nint owner, string initialDirectory)
|
||||
public static string? ShowBackgroundFile(nint owner, string initialDirectory)
|
||||
{
|
||||
if (owner == nint.Zero || !Directory.Exists(initialDirectory))
|
||||
{
|
||||
@@ -127,6 +167,89 @@ public sealed partial class MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
public static string? ShowFolder(
|
||||
nint owner,
|
||||
string? initialDirectory,
|
||||
string title)
|
||||
{
|
||||
if (owner == nint.Zero)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The folder picker requires an owner window.");
|
||||
}
|
||||
|
||||
if (initialDirectory is not null &&
|
||||
!Directory.Exists(initialDirectory))
|
||||
{
|
||||
throw new DirectoryNotFoundException(
|
||||
"The initial folder-picker directory is unavailable.");
|
||||
}
|
||||
|
||||
IFileDialog? dialog = null;
|
||||
IShellItem? initialFolder = null;
|
||||
IShellItem? result = null;
|
||||
try
|
||||
{
|
||||
var dialogType = Type.GetTypeFromCLSID(
|
||||
FileOpenDialogClassId,
|
||||
throwOnError: true) ??
|
||||
throw new InvalidOperationException(
|
||||
"The Windows folder dialog is unavailable.");
|
||||
dialog = (IFileDialog)(Activator.CreateInstance(dialogType) ??
|
||||
throw new InvalidOperationException(
|
||||
"The Windows folder dialog could not be created."));
|
||||
dialog.GetOptions(out var existingOptions);
|
||||
dialog.SetOptions(
|
||||
existingOptions |
|
||||
FileOpenOptions.NoChangeDirectory |
|
||||
FileOpenOptions.PickFolders |
|
||||
FileOpenOptions.ForceFileSystem |
|
||||
FileOpenOptions.PathMustExist |
|
||||
FileOpenOptions.NoDereferenceLinks |
|
||||
FileOpenOptions.DoNotAddToRecent);
|
||||
dialog.SetTitle(title);
|
||||
dialog.SetOkButtonLabel("폴더 선택");
|
||||
|
||||
if (initialDirectory is not null)
|
||||
{
|
||||
var shellItemId = typeof(IShellItem).GUID;
|
||||
var createResult = SHCreateItemFromParsingName(
|
||||
initialDirectory,
|
||||
nint.Zero,
|
||||
ref shellItemId,
|
||||
out initialFolder);
|
||||
Marshal.ThrowExceptionForHR(createResult);
|
||||
dialog.SetFolder(initialFolder);
|
||||
dialog.SetDefaultFolder(initialFolder);
|
||||
}
|
||||
|
||||
var showResult = dialog.Show(owner);
|
||||
if (showResult == CancelledHResult)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Marshal.ThrowExceptionForHR(showResult);
|
||||
dialog.GetResult(out result);
|
||||
result.GetDisplayName(FileSystemPathDisplayName, out var pathPointer);
|
||||
try
|
||||
{
|
||||
return Marshal.PtrToStringUni(pathPointer) ??
|
||||
throw new IOException("The selected folder path is unavailable.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(pathPointer);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseComObject(result);
|
||||
ReleaseComObject(initialFolder);
|
||||
ReleaseComObject(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReleaseComObject(object? value)
|
||||
{
|
||||
if (value is not null && Marshal.IsComObject(value))
|
||||
@@ -139,6 +262,7 @@ public sealed partial class MainWindow
|
||||
private enum FileOpenOptions : uint
|
||||
{
|
||||
NoChangeDirectory = 0x00000008,
|
||||
PickFolders = 0x00000020,
|
||||
ForceFileSystem = 0x00000040,
|
||||
PathMustExist = 0x00000800,
|
||||
FileMustExist = 0x00001000,
|
||||
|
||||
Reference in New Issue
Block a user