WPF 程序作为任务栏中的另一个程序打开?

WPF program opening as another program in taskbar?

问题

我正在使用自定义 windows 上下文菜单项 在任何文件上创建 C# WPF 程序。

但是当任何 .lnk 文件快捷方式)被选中并右键单击以通过上下文菜单项用我的程序打开时, 它会在任务栏中用另一个程序的图标打开我的 WPF window。

.

例子

如图所示 on this gif 当右键单击 discord.lnk 文件并选择我的自定义上下文菜单项之一时,它会打开 my WPF window作为 Discord 的页面。任何其他 .lnk 文件也会发生同样的情况。

它应该在任务栏中打开 window,如 on this gif 所示。如图所示,如果文件不是 .lnk.

,它会正常运行

.

问题

这个问题有哪些可能的解决方案?有没有办法避免 .lnk 文件这样做,并打开我的程序?


应用程序启动代码:

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        Args = e.Args;

        TryAssociate();

        MainWin main = new MainWin();
        main.Show();
    }

// FILE ASSOCIATION

    public static void TryAssociate() { if (!IsAssociated()) Associate(); }
    static bool IsAssociated() { return Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.chi", false) != null; }

    public static void Deassociate()
    {
        try
        {
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\.chi");
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\Applications\chi.exe");
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.chi");
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\*\shell\chi_Action1");
            Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\*\shell\chi_Action2");


            SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
        }
        catch { }
    }

    public static void Associate()
    {
        Deassociate();
        try
        {
            RegistryKey FileReg = Registry.CurrentUser.CreateSubKey(@"Software\Classes\.chi", RegistryKeyPermissionCheck.ReadWriteSubTree);
            RegistryKey AppReg = Registry.CurrentUser.CreateSubKey(@"Software\Classes\Applications\chi.exe", RegistryKeyPermissionCheck.ReadWriteSubTree);
            RegistryKey AppAssoc = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.chi", RegistryKeyPermissionCheck.ReadWriteSubTree);

            RegistryKey Encrypt = Registry.CurrentUser.CreateSubKey(@"Software\Classes\*\shell\chi_Action1");
            RegistryKey Decrypt = Registry.CurrentUser.CreateSubKey(@"Software\Classes\*\shell\chi_Action2");

            Encrypt.SetValue("", "Action1 with Chi");
            Encrypt.SetValue("Icon", AppData + "clf_icon.ico");
            //Encrypt.SetValue("MultiSelectModel", "Player");
            Encrypt.CreateSubKey("command").SetValue("", "\"" + AppPath + "\" \"%1\" " + "/Action1");

            Decrypt.SetValue("", "Action2 with Chi");
            Decrypt.SetValue("Icon", AppData + "clf_icon.ico");
            //Decrypt.SetValue("MultiSelectModel", "Player");
            Decrypt.CreateSubKey("command").SetValue("", "\"" + AppPath + "\" \"%1\" " + "/Action2");

            FileReg.CreateSubKey("DefaultIcon").SetValue("", AppData + "clf_icon.ico");
            FileReg.CreateSubKey("PerceivedType").SetValue("", "Document");
            FileReg.CreateSubKey("OpenWithProgids").SetValue(@"Applications\chi.exe", new byte[0], RegistryValueKind.None );

            AppReg.CreateSubKey("shell\open\command").SetValue("", "\"" + AppPath + "\" \"%1\" " + "/Open");
            AppReg.CreateSubKey("DefaultIcon").SetValue("", AppData + "clf_icon.ico");

            AppAssoc.CreateSubKey("UserChoice").SetValue("ProgId", @"chi.exe");
        }
        finally
        {
            SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
        }
    }

说明

过了一段时间,我发现了一种改变正在发生的事情的方法。

当您启动 WPF 应用程序时,它会为您的应用程序创建一个带有 ID 的任务栏图标。出于某种原因,如果您尝试打开任何其他应用程序快捷方式 (.lnk) 作为您的应用程序,它将启动一个任务栏图标,就好像它是该快捷方式的程序一样。

要更改此行为,您只需在创建 WPF window 后立即以某种方式修改任务栏图标 ID。为此,您必须下载 Windows API 代码包(这可以在 VisualStudio 2019 中完成),然后您可以继续编码。

编码

这是您的 WPF 更改任务栏图标 ID 所需要的。

using System.Windows.Interop;
using Microsoft.WindowsAPICodePack.Taskbar;

然后在创建 window 时,您应该这样做:

private void Application_Startup(object sender, StartupEventArgs e)
{
    /* Creating a window */
    MainWindow main = new MainWindow();
    main.Show();

    /* Changing the window taskbar id */
    TaskbarManager.Instance.SetApplicationIdForSpecificWindow(new WindowInteropHelper(main).Handle, "WindowTaskbarID Here");

    /* Showing window in the taskbar */
    main.ShowInTaskbar = true;
}

完成!现在您的程序将作为一个新的任务栏图标打开,如果您更改每个 window ID,您甚至可以拥有无​​限的任务栏图标; (这也适用于 WinForms)

解释:

我一直在研究和发现 WPFWinForms 中的新事物。其中之一是,出于某种原因,当您在 regedit 上为任何文件创建 custom shell 命令 时,当您尝试使用它们在 .lnk 文件(window 快捷方式)上,它可能会如此混乱和错误。发生这种情况是因为快捷方式将解释 shell 命令发送的操作,并使用该快捷方式的根文件 ID 和参数直接打开它。


解决方法:

解决方法应该是将快捷方式作为 .lnk 文件而不是快捷方式读取。(我给出的另一个答案可以达到几乎相同的结果,但它没有修复 .lnk 文件)

为此,我们需要在快捷方式注册表上覆盖我们的自定义shell命令(如果您有多个shell 命令,你应该覆盖所有的命令)。 这里有一个例子:

using System;
using Microsoft.Win32;
using System.Runtime.InteropServices;

[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern void SHChangeNotify(uint Id, uint Flags, IntPtr Item1, IntPtr Item2);

RegistryKey programLnk = Registry.CurrentUser.CreateSubKey(@"Software\Classes\lnkfile\shell\MyProgram");

programLnk.SetValue("", "Open with MyProgram");
programLnk.SetValue("Icon", AppPath);
programLnk.CreateSubKey("command").SetValue("", "\"" + AppPath + "\" \"%1\"");

SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
// This will notify window of the change made in the registry.

这应该可以修复您的程序在任务栏中作为另一个程序打开并为其提供预期的参数。