96710


"エクスプローラーでファイルを右クリック→プログラムから開く→別のプログラムで開く"で表示されるダイアログボックスをC#､WinUI3で作っているアプリで使いたいです｡

"プログラムから開く"ダイアログボックスを使用して表示している画像を別のアプリで開けるようにしたいです｡


CC BY-SA 4.0
2023-11-07T11:55:10.830
96705
2023-11-09T02:37:54.447
2023-11-07T14:02:58.563
60345
60345
question
c#
Windowsの"アプリを選択して_ファイルを開く"ダイアログボックスをアプリで使用したい


自己解決しました。

[code]

    //pathはstring型でファイルパスをもつ
     if(path != null)
     {
         var _path = await StorageFile.GetFileFromPathAsync(path);
         var options = new LauncherOptions() 
         {
             DisplayApplicationPicker = true,
         };
         await Launcher.LaunchFileAsync(_path,options);
     }
    
[/code]

"プログラムから開く"ダイアログボックスの表示に使った`LaunchFileAsync(IStorageFile,
LauncherOptions)`についてのMicrosoft公式ドキュメントです｡

[LaunchFileAsync(IStorageFile, LauncherOptions) | Microsoft
Learn](https://learn.microsoft.com/ja-
jp/uwp/api/windows.system.launcher.launchfileasync?view=winrt-22621#windows-
system-launcher-launchfileasync\(windows-storage-istoragefile-windows-system-
launcheroptions\))


CC BY-SA 4.0
2023-11-08T07:46:30.967
96710
2023-11-09T02:37:54.447
2023-11-09T02:37:54.447
3060
60345
96705
answer


既に解決したようですが別解です。

`shell32.dll`の[`SHOpenWithDialog`](https://learn.microsoft.com/ja-
jp/windows/win32/api/shlobj_core/nf-shlobj_core-
shopenwithdialog)関数を使って「プログラムから開く」ダイアログを呼び出すことができます。  
なお「どのプログラムからファイルを開いたのか」は判別できませんが、「開いた」 or「 開くのをキャンセルした」を判別することは可能です。

下記のサンプルコードがC#での使用例です。  
`SHOpenWithDialog`に親ウィンドウのハンドルとファイルパスを送るラッパーとして`OpenFileWith`関数を作成しました。  
WPFで`MyButton_Click`関数から`OpenFileWith`関数を経由して「プログラムから開く」ダイアログを呼び出して、実行したかキャンセルしたかを判定しています。  
Windows 11、.NET Framework 4.8、WPFで動作を確認しています。  
※WinUI3では確認していません。

**サンプルコード**

[code]

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Windows;
    using System.Windows.Interop;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void MyButton_Click(object sender, RoutedEventArgs e)
            {
                // WPFのウィンドウハンドルを取得
                var hwnd = new WindowInteropHelper(this).Handle;
                var hresult = OpenFileWith(hwnd, "hoge.png");
                string message;
                switch(hresult)
                {
                    case 0:  // S_OK https://so-zou.jp/software/tech/programming/tech/error/hresult.htm
                        message = "表示しました。";
                        break;
                    case -2147023673: // ERROR_CANCELLED https://www.hresult.info/FACILITY_WIN32/0x800704C7
                        message = "この操作はユーザによって取り消されました。";
                        break;
                    default:
                        message = new Win32Exception(hresult).Message;
                        break;
                        //例外にする場合はこちら
                        throw new Win32Exception(hresult);
                }
                MessageBox.Show(message);
            }
    
            /// <summary>
            /// [ファイルを 開く ] ダイアログ ボックスを表示します。
            /// </summary>
            /// <param name="hwndParent">親ウィンドウのハンドル。 この値は NULL にすることができます。</param>
            /// <param name="oainfo">結果のダイアログの内容を指定する OPENASINFO 構造体</param>
            /// <returns>この関数が成功すると、 S_OKが返されます。 そうでない場合は、HRESULT エラー コードを返します。</returns>
            /// <seealso href="https://learn.microsoft.com/ja-jp/windows/win32/api/shlobj_core/nf-shlobj_core-shopenwithdialog">SHOpenWithDialog 関数 (shlobj_core.h)</seealso>
            [DllImport("shell32.dll", EntryPoint = "SHOpenWithDialog", CharSet = CharSet.Unicode)]
            private static extern int SHOpenWithDialog(IntPtr hwndParent, ref tagOpenAsInfo oainfo);
    
            /// <summary>
            /// SHOpenWithDialog 関数の情報を格納します。
            /// </summary>
            /// <seealso href="https://learn.microsoft.com/ja-jp/windows/win32/api/shlobj_core/ns-shlobj_core-openasinfo">OPENASINFO 構造体 (shlobj_core.h)</seealso>
            private struct tagOpenAsInfo
            {
                [MarshalAs(UnmanagedType.LPWStr)]
                public string cszFile;
                [MarshalAs(UnmanagedType.LPWStr)]
                public string cszClass;
                [MarshalAs(UnmanagedType.I4)]
                public tagOAIF oaifInFlags;
            }
    
            /// <summary>
            /// [SHOpenWithDialog] ダイアログ ボックスの特性。 次の値のうち 1 つ以上。
            /// </summary>
            [Flags]
            private enum tagOAIF
            {
                /// <summary>[常にこのプログラムを使用する] チェック ボックスをオンにします。 渡されない場合は無効になります。</summary>
                OAIF_ALLOW_REGISTRATION = 0x00000001,
                /// <summary>ユーザーが [OK] ボタンをクリックした後、登録 を行います。</summary>
                OAIF_REGISTER_EXT = 0x00000002,
                /// <summary>登録後にファイルを実行します。</summary>
                OAIF_EXEC = 0x00000004,
                /// <summary>[ 常にこのプログラムを使用 する] チェック ボックスをオンにします。 通常、この値を渡すときは、OAIF_ALLOW_REGISTRATION フラグは使用しません。</summary>
                OAIF_FORCE_REGISTRATION = 0x00000008,
                /// <summary>Windows Vista で導入されました。 [ 常にこのプログラムを使用 する] チェック ボックスを非表示にします。 このフラグを指定すると、OAIF_ALLOW_REGISTRATIONおよびOAIF_FORCE_REGISTRATIONフラグは無視されます。</summary>
                OAIF_HIDE_REGISTRATION = 0x00000020,
                /// <summary>Windows Vista で導入されました。 渡される拡張機能の値は実際にはプロトコルであるため、[ 開く ] ダイアログ ボックスには、そのプロトコルを処理できるとして登録されているアプリケーションが表示されます。</summary>
                OAIF_URL_PROTOCOL = 0x00000040,
                /// <summary>Windows 8 で導入されました。 pcszFile パラメーターが指す場所は URI として指定されます。</summary>
                OAIF_FILE_IS_URI = 0x00000080,
            }
    
            /// <summary>
            /// 「プログラムから開く」ダイアログ経由で外部アプリケーションにファイルを送ります。
            /// ※ Windows 2000, XP以降のみ使用可能
            /// </summary>
            /// <param name="hwndParent">親ウィンドウのハンドル。 この値は NULL にすることができます。</param>
            /// <param name="filePath">開くファイルのパス</param>
            /// <returns>この関数が成功すると、 S_OKが返されます。 そうでない場合は、HRESULT エラー コードを返します。</returns>
            private static int OpenFileWith(IntPtr hwndParent, string filePath)
            {
                var fullPath = System.IO.Path.GetFullPath(filePath);
                tagOpenAsInfo info = new tagOpenAsInfo
                {
                    cszFile = fullPath,
                    cszClass = "",
                    oaifInFlags = tagOAIF.OAIF_EXEC,
                };
                return SHOpenWithDialog(hwndParent, ref info);
            }
        }
    }
    
[/code]

**参考資料**

  * [SHOpenWithDialog (shell32)](https://www.pinvoke.net/default.aspx/shell32/SHOpenWithDialog.html)
  * [`SHOpenWithDialog`](https://learn.microsoft.com/ja-jp/windows/win32/api/shlobj_core/nf-shlobj_core-shopenwithdialog)
  * ["How to fetch the message string from Win32 provided HRESULT C#"の回答](https://stackoverflow.com/a/65026179)


CC BY-SA 4.0
2023-11-08T07:53:35.083
96711
2023-11-08T07:53:35.083
9820
96705
answer