369 lines
13 KiB
C#
369 lines
13 KiB
C#
using System.Runtime.InteropServices;
|
|
using WinRT.Interop;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyParityApp;
|
|
|
|
public sealed partial class MainWindow
|
|
{
|
|
private Task<string?> PickBackgroundFileAsync(string trustedBackgroundDirectory)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(trustedBackgroundDirectory);
|
|
var initialDirectory = Path.GetFullPath(trustedBackgroundDirectory);
|
|
var completion = new TaskCompletionSource<string?>(
|
|
TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
void ShowPicker()
|
|
{
|
|
try
|
|
{
|
|
completion.TrySetResult(
|
|
LegacyFileDialog.ShowBackgroundFile(
|
|
WindowNative.GetWindowHandle(this),
|
|
initialDirectory));
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
completion.TrySetException(exception);
|
|
}
|
|
}
|
|
|
|
if (DispatcherQueue.HasThreadAccess)
|
|
{
|
|
ShowPicker();
|
|
}
|
|
else if (!DispatcherQueue.TryEnqueue(ShowPicker))
|
|
{
|
|
completion.TrySetException(
|
|
new InvalidOperationException("The background picker could not reach the UI thread."));
|
|
}
|
|
|
|
return completion.Task;
|
|
}
|
|
|
|
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? ShowBackgroundFile(nint owner, string initialDirectory)
|
|
{
|
|
if (owner == nint.Zero || !Directory.Exists(initialDirectory))
|
|
{
|
|
throw new DirectoryNotFoundException(
|
|
"The trusted background 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 file-open dialog is unavailable.");
|
|
dialog = (IFileDialog)(Activator.CreateInstance(dialogType) ??
|
|
throw new InvalidOperationException(
|
|
"The Windows file-open dialog could not be created."));
|
|
dialog.GetOptions(out var existingOptions);
|
|
dialog.SetOptions(
|
|
existingOptions |
|
|
FileOpenOptions.NoChangeDirectory |
|
|
FileOpenOptions.ForceFileSystem |
|
|
FileOpenOptions.PathMustExist |
|
|
FileOpenOptions.FileMustExist |
|
|
FileOpenOptions.NoDereferenceLinks |
|
|
FileOpenOptions.DoNotAddToRecent);
|
|
dialog.SetTitle("다음 장면 배경 선택");
|
|
dialog.SetOkButtonLabel("선택");
|
|
dialog.SetDefaultExtension("vrv");
|
|
dialog.SetFileTypes(
|
|
3,
|
|
new[]
|
|
{
|
|
new DialogFilter("VRV/JPG/PNG 배경", "*.vrv;*.jpg;*.jpeg;*.png"),
|
|
new DialogFilter("VRV 영상", "*.vrv"),
|
|
new DialogFilter("JPG/PNG 이미지", "*.jpg;*.jpeg;*.png")
|
|
});
|
|
dialog.SetFileTypeIndex(1);
|
|
|
|
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 background path is unavailable.");
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeCoTaskMem(pathPointer);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ReleaseComObject(result);
|
|
ReleaseComObject(initialFolder);
|
|
ReleaseComObject(dialog);
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
_ = Marshal.FinalReleaseComObject(value);
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
private enum FileOpenOptions : uint
|
|
{
|
|
NoChangeDirectory = 0x00000008,
|
|
PickFolders = 0x00000020,
|
|
ForceFileSystem = 0x00000040,
|
|
PathMustExist = 0x00000800,
|
|
FileMustExist = 0x00001000,
|
|
NoDereferenceLinks = 0x00100000,
|
|
DoNotAddToRecent = 0x02000000
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private readonly struct DialogFilter(string name, string specification)
|
|
{
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
public readonly string Name = name;
|
|
|
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|
public readonly string Specification = specification;
|
|
}
|
|
|
|
[ComImport]
|
|
[Guid("42F85136-DB7E-439C-85F1-E4075D135FC8")]
|
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
private interface IFileDialog
|
|
{
|
|
[PreserveSig]
|
|
int Show(nint owner);
|
|
|
|
void SetFileTypes(
|
|
uint count,
|
|
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
|
|
DialogFilter[] filters);
|
|
|
|
void SetFileTypeIndex(uint index);
|
|
|
|
void GetFileTypeIndex(out uint index);
|
|
|
|
void Advise(nint events, out uint cookie);
|
|
|
|
void Unadvise(uint cookie);
|
|
|
|
void SetOptions(FileOpenOptions options);
|
|
|
|
void GetOptions(out FileOpenOptions options);
|
|
|
|
void SetDefaultFolder(IShellItem folder);
|
|
|
|
void SetFolder(IShellItem folder);
|
|
|
|
void GetFolder(out IShellItem folder);
|
|
|
|
void GetCurrentSelection(out IShellItem item);
|
|
|
|
void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string name);
|
|
|
|
void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string name);
|
|
|
|
void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string title);
|
|
|
|
void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string text);
|
|
|
|
void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string label);
|
|
|
|
void GetResult(out IShellItem item);
|
|
|
|
void AddPlace(IShellItem item, uint placement);
|
|
|
|
void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string extension);
|
|
|
|
void Close(int result);
|
|
|
|
void SetClientGuid(ref Guid guid);
|
|
|
|
void ClearClientData();
|
|
|
|
void SetFilter(nint filter);
|
|
}
|
|
|
|
[ComImport]
|
|
[Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
|
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
private interface IShellItem
|
|
{
|
|
void BindToHandler(
|
|
nint bindContext,
|
|
ref Guid handlerId,
|
|
ref Guid interfaceId,
|
|
out nint result);
|
|
|
|
void GetParent(out IShellItem parent);
|
|
|
|
void GetDisplayName(uint displayName, out nint name);
|
|
|
|
void GetAttributes(uint mask, out uint attributes);
|
|
|
|
void Compare(IShellItem item, uint hint, out int order);
|
|
}
|
|
|
|
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = true)]
|
|
private static extern int SHCreateItemFromParsingName(
|
|
[MarshalAs(UnmanagedType.LPWStr)] string path,
|
|
nint bindContext,
|
|
ref Guid interfaceId,
|
|
[MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
|
|
}
|
|
}
|