Search code examples
c#taskbar

Control path to pinned exe in Windows taskbar and start menu


I'm working with a pair of programs, the "starter"(updater) and the "main" program. Where starter updates and launches main.

The program must behave nicely when pinned to the windows taskbar or start-menu. For example the user should be able to:

  1. Start "starter"
  2. Pin the running "main" to the task bar
  3. Close the program
  4. Starts the program using the pinned item on the taskbar.

Is it possible to make the pinned shortcut point to the "starter" and not the "main" directly?

I've tried to group them using a common ID, but that doesn't affect the path being pinned.

Right now I'm loading "main" within the process of "starter". This works as expected but have the problem that all updates are limited by the .NET version of "starter" which right now is getting quite old, .NET 3.5.


Solution

  • Set the following properties for System.AppUserModel.

    • System.AppUserModel.ID
    • System.AppUserModel.RelaunchCommand
    • System.AppUserModel.RelaunchDisplayNameResource

    In C# you can use Windows-API-Code-Pack or its NuGet package WindowsAPICodePack-Shell.

    Note that for unknown reason you can't easily change the path once it's set.

    void SetTaskbarRelaunchCommand(Form form)
    {
        // WARNING, once RelaunchCommand has been set it can't be changed for any given appID.
        // Workaround: delete all links here related to our app.
        // %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\ImplicitAppShortcuts
        // %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
        // Source: https://stackoverflow.com/a/28388958/33236
    
        var appID = "MyAppID";
        var path = @"C:\Program Files (x86)\MyApp\Updater.exe");
        var handle = form.Handle;
    
        var propGuid = new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}");
        var ID = new PropertyKey(propGuid, 5);                          // System.AppUserModel.ID
        var RelaunchCommand = new PropertyKey(propGuid, 2);             // System.AppUserModel.RelaunchCommand
        var RelaunchDisplayNameResource = new PropertyKey(propGuid, 4); // System.AppUserModel.RelaunchDisplayNameResource
    
        WindowProperties.SetWindowProperty(handle, ID, appID);
        WindowProperties.SetWindowProperty(handle, RelaunchCommand, path);
        WindowProperties.SetWindowProperty(handle, RelaunchDisplayNameResource, "Label of My App");
    }
    

    You can also prevent pinning the app completely. As opposed to RelaunchCommand you can change this value any time.

    void PreventPinning(IntPtr handle)
    {
        var appID = "MyAppNoPin";
    
        var propGuid = new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}");
        var ID = new PropertyKey(propGuid, 5);              // System.AppUserModel.ID
        var PreventPinning = new PropertyKey(propGuid, 9);  // System.AppUserModel.PreventPinning
    
        //Important: Set PreventPinning before ID
        WindowProperties.SetWindowProperty(handle, PreventPinning, "True");
        WindowProperties.SetWindowProperty(handle, ID, appID);
    }