TravelingFox
TravelingFox

Reputation: 573

How to allow launcher application to be pinned to the taskbar

We have a simple launcher.cmd script that sets up some prerequisites (network shares, configuration files etc.), depending on command-line arguments, and then starts a third-party application. It also launches a custom splash screen (Windows Forms application), but that doesn't have any functionality beyond the splash screen. The external application also contains a plugin that we have written.

Imagine something like this:

set appEnvironment=%1
MyCustomSplashScreen\MyCustomSplashScreen.exe %appEnvironment%
net use X: /delete /yes
net use X: \\someserver\%appEnvironment%
copy Configuration\%appEnvironment%.config ExternalApp\ExternalApp.MyPlugin.config
start ExternalApp\ExternalApp.exe

It is invoked like this:

launcher.cmd Production

The challenge we are facing is that some users pin ExternalApp.exe (the actual application, after launcher.cmd and the splash screen have terminated) to the taskbar. The taskbar shortcut then launches ExternalApp.exe directly and not our launcher script - so all kinds of strange things can happen. As we have control over the plugin, it would be possible to move some of the logic directly into ExternalApp.exe, but that wouldn't solve the issue of losing the environment parameter. The best solution I can come up with would be different ways of making sure the application can only be launched via the launcher script, essentially making it useless for the user to pin the application to the taskbar.

However, I have thought about being a little more creative. I am planning to do the following:

  1. Move the launcher.cmd logic into MyCustomSplashScreen.exe
  2. In MyCustomSplashScreen.exe, start ExternalApp.exe and make it a docked child window (cf. Docking Window inside another Window ).
  3. Instead of using a parameter, create copies (or links) of MyCustomSplashScreen.exe that reflect the environment, e.g. Launch_Production.exe, Launch_Staging.exe etc.

The consequence would be that only MyCustomSplashScreen would appear on the taskbar and be pinnable. Pinning the application would result in the application specific to that environment (e.g. Launch_Staging.exe) being pinned, just what the user expects.

I am quite confident that this would work. But maybe there is a simpler solution? What I'm looking for is some way to make only my launcher application on the taskbar and not the application it launches.

I found similar questions here and here where it was suggested to manipulate the pinning process itself. Maybe that's a better solution? I'm just not sure if my plugin has enough control over ExternalApp.exe to implement this, so would need to test it.

Upvotes: 1

Views: 1850

Answers (2)

Coden
Coden

Reputation: 2868

I wasn't successful in setting the RelaunchCommand and the RelaunchDisplayNameResource on the newest Windows 10 update. But i have found another possibility to set that the Launcher is pinned to the taskbar: Pinning to the taskbar a "chained process"

Upvotes: 0

TravelingFox
TravelingFox

Reputation: 573

The solution from Control path to pinned exe in Windows taskbar and start menu works just fine. I put something like this into my plugin and it does exactly what I want:

private static void SetTaskbarRelaunchCommand(string environment)
{
    // 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

    const string appID = "MyAppID";
    string path = $@"C:\Launcher.exe {environment}";
    IntPtr windowHandle = Process.GetCurrentProcess().MainWindowHandle;

    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(windowHandle, id, appID);
    WindowProperties.SetWindowProperty(windowHandle, relaunchCommand, path);
    WindowProperties.SetWindowProperty(windowHandle, relaunchDisplayNameResource, $"My App {environment}");
}

It seems like the original version of the Windows API CodePack published by Microsoft in 2009 is no longer officially available. Instead there are a dozen inofficial NuGet packages that (claim to) contain the original library, as well as some where bugs have been fixed. I ended up using this NuGet package as it seems to be actively maintained.

Of course I am in the fortunate situation that I could manipulate the behavior of the external application through my plugin. Without this, there probably is no other solution than running the application as a docked child window.

UPDATE: It seems like there is another way to achieve thisn when you cannot control the application you're launching. You simply set the System.AppUserModel.Id for the launched application's window. More details here.

Upvotes: 1

Related Questions