氛疵 发表于 2025-6-1 18:55:09

C# 调用 Win10/11 文件关联对话框

方法一:调用未公开接口 IOpenWithLauncher

Adobe Acrobat 应该是调用的未公开接口方法


interface IOpenWithLauncher
{
   
    int Launch(IntPtr hWndParent,
          string lpszPath,
         IMMERSIVE_OPENWITH flags);
}


enum IMMERSIVE_OPENWITH
{
    NONE = 0,
    OVERRIDE = 0x1,
    DONOT_EXEC = 0x4,
    PROTOCOL = 0x8,
    URL = 0x10,
    USEPOSITION = 0x20,
    DONOT_SETDEFAULT = 0x40,
    ACTION = 0x80,
    ALLOW_EXECDEFAULT = 0x100,
    NONEDP_TO_EDP = 0x200,
    EDP_TO_NONEDP = 0x400,
    CALLING_IN_APP = 0x800,
};

public static void ShowSetAssocDialog(string extension)
{
    var CLSID_ExecuteUnknown = new Guid("{E44E9428-BDBC-4987-A099-40DC8FD255E7}");
    var obj = Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_ExecuteUnknown));
    if (obj is IOpenWithLauncher launcher)
    {
      launcher.Launch(IntPtr.Zero, extension, IMMERSIVE_OPENWITH.DONOT_EXEC);
      Marshal.ReleaseComObject(launcher);
    }
}方法二:通过模拟点击属性对话框“更改”打开方式

此方法来自两年前我的自问自答,代码引用了 《C# 窗口过程消息处理 WndProc》 中的附加到其他窗口辅助类
Bandizip 使用的这种方法
static extern bool SHObjectProperties(IntPtr hWnd, SHOP shopObjectType, string pszObjectName, string pszPropertyPage);

enum SHOP
{
    PRINTERNAME = 1,
    FILEPATH = 2,
    VOLUMEGUID = 4
}


static extern int GetCurrentProcessId();


static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);


static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);


static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


static extern bool SetForegroundWindow(IntPtr hWnd);


static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

public static void ShowSetAssocDialog(string extension)
{
    string fileName = Path.ChangeExtension(Path.GetRandomFileName(), extension);
    string filePath = Path.Combine(Path.GetTempPath(), fileName);
    fileName = Path.GetFileNameWithoutExtension(fileName);
    File.WriteAllText(filePath, string.Empty); // 创建临时文件
    var frame = new DispatcherFrame();
    int pid = GetCurrentProcessId();
    SHObjectProperties(IntPtr.Zero, SHOP.FILEPATH, filePath, null); // 显示属性对话框
    while (true)
    {
      bool found = !EnumWindows((hWnd, lParam) => // 枚举窗口
      {
            GetWindowThreadProcessId(hWnd, out int id);
            if (id == pid) // 比较进程 id
            {
                const int MAX_PATH = 260;
                var sb = new StringBuilder(MAX_PATH);
                GetClassName(hWnd, sb, sb.Capacity);
                if (sb.ToString() == "#32770") // 对话框类名
                {
                  GetWindowText(hWnd, sb, sb.Capacity);
                  if (sb.ToString().Contains(fileName)) // 对话框标题是否包含文件名
                  {
                        SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP.HIDEWINDOW); // 隐藏属性对话框
                        MessageHooker.AddHook(hWnd, (ref Message m) =>
                        {
                            const int PSM_CHANGED = 0x400 + 104;
                            if (m.Msg == PSM_CHANGED) // 监测属性表页更改
                            {
                              frame.Continue = false;
                              PostMessage(hWnd, WM.CLOSE, 0, 0); // 等效 EndDialog(hWnd, 0)
                            }
                            return false;
                        });
                        SetForegroundWindow(hWnd);
                        SendKeys.SendWait("%C"); // ALT + C 快捷键
                        return false;
                  }
                }
            }
            return true;
      }, IntPtr.Zero);
      if (found) break;
    }
    File.Delete(filePath); // 删除临时文件
    Dispatcher.PushFrame(frame);
}相关资料

Redirecting Open With in Properties Dialog in Windows 10
How to call the "Open With" dialog used to associate file formats in Windows 10 or 11?

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: C# 调用 Win10/11 文件关联对话框