using System.Runtime.InteropServices; using WinRT.Interop; namespace MBN_STOCK_WEBVIEW.LegacyParityApp; public sealed partial class MainWindow { private Task PickBackgroundFileAsync(string trustedBackgroundDirectory) { ArgumentException.ThrowIfNullOrWhiteSpace(trustedBackgroundDirectory); var initialDirectory = Path.GetFullPath(trustedBackgroundDirectory); var completion = new TaskCompletionSource( 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 PickOperatorFolderAsync( string? initialDirectory, string title) { ArgumentException.ThrowIfNullOrWhiteSpace(title); var canonicalInitialDirectory = initialDirectory is null ? null : Path.GetFullPath(initialDirectory); var completion = new TaskCompletionSource( 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); } }