将“链式进程"固定到任务栏 [英] Pinning to the taskbar a "chained process"

查看:21
本文介绍了将“链式进程"固定到任务栏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个名为 launcher.exelaunchee.exe 的程序.启动器显示一个按钮,单击该按钮时会启动 launchee.exelaunchee.exe 是一个简单的 hello world 程序.

Let's say I have two programs called launcher.exe and launchee.exe. The launcher display a button which -when clicked- starts launchee.exe and launchee.exe is a simple hello world program.

如果我不采取任何措施来阻止它,当用户将 hello world 程序固定到任务栏"时,它会固定 launchee.exe 并且不会通过启动器启动应用程序.

If I do nothing to prevent it, when the user will "pin to the taskbar" the hello world program, it will pin launchee.exe and will not go through the launcher to start the application.

告诉 Windows 固定 launcher.exe 而不是 launchee.exe 的最佳方法是什么?

What is the best way to tell Windows to pin launcher.exe and not launchee.exe ?

为了使事情具体化,这里有一个在 C# 中实现 launcher.exe 的示例:

To make things concrete here's an example of implementation of launcher.exe in C#:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

public class Launcher : Form
{
    static public void Main ()
    {
        Application.Run (new Launcher ());
    }

    public Launcher ()
    {
        Button b = new Button ();
        b.Text = "Launch";
        b.Click += new EventHandler (Button_Click);
        Controls.Add (b);
    }

    private void Button_Click (object sender, EventArgs e)
    {
        Process.Start("launchee.exe");
        System.Environment.Exit(0);
    }
}

launchee.exe:

using System;
using System.Drawing;
using System.Windows.Forms;

public class Launchee : Form
{
    static public void Main ()
    {
        Application.Run (new Launchee ());
    }

    public Launchee ()
    {
        Label b = new Label();
        b.Text = "Hello World !";
        Controls.Add (b);
    }
}

推荐答案

我提出了一个基于绑定低级 API 的 anwser 来访问 AppUserModelID.我发现这个解决方案脆弱而凌乱.它很大程度上受到了 Windows Api CodePack 的启发,它似乎已经被微软停产.我希望有人会提出一个更清洁的解决方案.

I propose an anwser based on binding the low level API to access the AppUserModelID. I find this solution fragile and messy. It is largely inspired by the Windows Api CodePack that seems to have been discontinued by Microsoft. I hope someone will propose a cleaner solution.

其目的是将 AppUserId 设置为Stackoverflow.chain.process.pinning"并手动设置 RelaunchCommand 以及 DisplayName 属性(它们必须根据 AppUserModelID).

Its purpose is to set the AppUserId to be "Stackoverflow.chain.process.pinning" and manually set the RelaunchCommand as well as the DisplayName properties (they have to be set together according to AppUserModelID).

要在示例实现中使用它,需要在Launcher<中分别调用TaskBar.SetupLauncher(this)TaskBar.SetupLaunchee(this)/code> 和 Launchee 构造函数.

To use it in the example implementation, one needs to call TaskBar.SetupLauncher(this) and TaskBar.SetupLaunchee(this) respectively in the Launcher and Launchee constructors.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

internal struct PropertyKey
{
  Guid formatId;
  int propertyId;

  internal PropertyKey(Guid guid, int propertyId)
  {
      this.formatId = guid;
      this.propertyId = propertyId;
  }
}

[StructLayout(LayoutKind.Explicit)]
internal struct PropVariant
{
  [FieldOffset(0)] internal ushort vt;
  [FieldOffset(8)] internal IntPtr pv;
  [FieldOffset(8)] internal UInt64 padding;

  [DllImport("Ole32.dll", PreserveSig = false)]
  internal static extern void PropVariantClear(ref PropVariant pvar);

  internal PropVariant(string value)
  {
      this.vt = (ushort)VarEnum.VT_LPWSTR;
      this.padding = 0;
      this.pv = Marshal.StringToCoTaskMemUni(value);
  }

  internal void Clear()
  {
    PropVariantClear (ref this);
  }
}

[ComImport,
Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IPropertyStore
{
  int GetCount([Out] out uint propertyCount);
  void GetAt([In] uint propertyIndex, [Out, MarshalAs(UnmanagedType.Struct)] out PropertyKey key);
  void GetValue([In, MarshalAs(UnmanagedType.Struct)] ref PropertyKey key, [Out, MarshalAs(UnmanagedType.Struct)] out PropVariant pv);
  void SetValue([In, MarshalAs(UnmanagedType.Struct)] ref PropertyKey key, [In, MarshalAs(UnmanagedType.Struct)] ref PropVariant pv);
  void Commit();
}

internal static class TaskBar {
  [DllImport("shell32.dll")]
  static extern int SHGetPropertyStoreForWindow(
      IntPtr hwnd,
      ref Guid iid /*IID_IPropertyStore*/,
      [Out(), MarshalAs(UnmanagedType.Interface)]out IPropertyStore propertyStore);

  internal static class Key {
    private static Guid propGuid = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3");
    internal static PropertyKey AppId = new PropertyKey(propGuid, 5);
    internal static PropertyKey RelaunchCommand = new PropertyKey(propGuid, 2);
    internal static PropertyKey DisplayName = new PropertyKey(propGuid, 4);
  }

  private static void ClearValue(IPropertyStore store, PropertyKey key) {
      var prop = new PropVariant();
      prop.vt = (ushort)VarEnum.VT_EMPTY;
      store.SetValue(ref key, ref prop);
  }

  private static void SetValue(IPropertyStore store, PropertyKey key, string value) {
    var prop = new PropVariant(value);
    store.SetValue(ref key, ref prop);
    prop.Clear();
  }

  internal static IPropertyStore Store(IntPtr handle) {
    IPropertyStore store;
    var store_guid = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99");
    int rc = SHGetPropertyStoreForWindow(handle, ref store_guid, out store);
    if (rc != 0) throw Marshal.GetExceptionForHR(rc);
    return store;
  }

  internal static void SetupLauncher(Form form) {
    IntPtr handle = form.Handle;
    var store = Store(handle);
    SetValue (store, Key.AppId, "Stackoverflow.chain.process.pinning");
    form.FormClosed += delegate { Cleanup(handle); };
  } 

  internal static void SetupLaunchee(Form form) {
    IntPtr handle = form.Handle;
    var store = Store(handle);
    SetValue (store, Key.AppId, "Stackoverflow.chain.process.pinning");
    string exePath = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "launcher.exe");
    SetValue (store, Key.RelaunchCommand, exePath);
    SetValue (store, Key.DisplayName, "Test");
    form.FormClosed += delegate { Cleanup(handle); };
  }

  internal static void Cleanup(IntPtr handle) {
    var store = Store(handle);
    ClearValue (store, Key.AppId);
    ClearValue (store, Key.RelaunchCommand);
    ClearValue (store, Key.DisplayName);
  }
}

这篇关于将“链式进程"固定到任务栏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆