How do I prevent users from pinning my program to the taskbar?

A customer wanted to prevent users from pinning their application to the taskbar.

I have an application that is launched as a helper by a main application. Users shouldn't be launching it directly, but rather should be launching the main application. But since the helper shows up in the taskbar, users may be tempted to right-click on the taskbar icon and select "Pin to taskbar." Unfortunately, this pins the helper program to the taskbar instead of the main application, and launching the helper program directly doesn't work. Is there a way I can prevent users from pinning the helper program?

It so happens that there are a number of ways of marking your helper program as Don't pin me. Given the description above, the most direct way is probably to set the System.App­User­Model.Prevent­Pinning property on the window created by the helper program.

Take our scratch program and make the following changes:

#include <shellapi.h>
#include <propsys.h>
#include <propkey.h>

HRESULT MarkWindowAsUnpinnable(HWND hwnd)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
PROPVARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
pps->Release();
}
return hr;
}

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
MarkWindowAsUnpinnable(hwnd);
return TRUE;
}


I set the PROP­VARIANT manually instead of using Init­Prop­Variant­From­Boolean just to emphasize that the boolVal must be VARIANT_TRUE and not TRUE. In real life, I probably would have used Init­Prop­Variant­From­Boolean.

Run this program and observe that "Pin this program to taskbar" does not appear on the menu when you right-click on the taskbar button.

Even better would be to permit pinning, but set the System.App­User­Model.Relaunch­Command, .Relaunch­Display­Name­Resource and optionally .Relaunch­Icon­Resource properties so that if the user tries to pin the helper, it actually pins the main application.

#include <shellapi.h>
#include <propsys.h>
#include <propkey.h>
#include <propvarutil.h>

HRESULT IPropertyStore_SetValue(IPropertyStore *pps,
REFPROPERTYKEY pkey, PCWSTR pszValue)
{
PROPVARIANT var;
HRESULT hr = InitPropVariantFromString(pszValue, &var);
if (SUCCEEDED(hr))
{
hr = pps->SetValue(pkey, var);
PropVariantClear(&var);
}
return hr;
}

BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
IPropertyStore_SetValue(pps,
PKEY_AppUserModel_ID, L"Contoso.Scratch");
IPropertyStore_SetValue(pps,
PKEY_AppUserModel_RelaunchCommand,
IPropertyStore_SetValue(pps,
PKEY_AppUserModel_RelaunchDisplayNameResource,
L"C:\\full\\path\\to\\scratch.exe,-1");
// optionally also set PKEY_AppUserModel_RelaunchIconResource
pps->Release();
}
return TRUE;
}

// resource file
STRINGTABLE BEGIN
1 "Open system.ini"
END



I'm pulling a fast one here and pretending that Notepad is my main application. Obviously you'd use your actual main application. (I'm also hard-coding the path to my scratch program.)

When you run this program, right-click on the taskbar button. Observe that the option to run a new copy of the program is called Open system.ini and if you pick it (or use the middle-mouse-button shortcut), Notepad runs. If you pin the program, the pinned icon runs Notepad.

Even if you don't need to redirect the pinned item to another program, you can use this second technique to pass a custom command line for the pinned icon.

Tags

1. BC_Programmer says:

I would wonder why the helper program needs a taskbar icon to begin with; most of them do one-shot tasks, and as presented by the customer here aren't really "user-servicable" so why make them visible at all? Without the taskbar button the user can't pin the program, can they?

[They broke their application's functionality into multiple EXEs for whatever reason, so the windows are all logically part of the single application, although they physically belong to different EXEs. Imagine if, for example, Excel were broken up into EXCEL.EXE (the spreadsheet grid), CHART.EXE and PIVOTTABLE.EXE. -Raymond]
2. Gozer says:

I think what BC is saying is that, at least in the .Net world, you can set ShowInTaskBar = false in order to get an application with no taskbar icon when it runs. Which would alleviate the need to prevent pinning per se because there would be nothing to actually pin when the secondary program ran. I think you do this by removing the WS_EX_APPWINDOW style via the API.

That said, the user may WANT the icon to appear, maybe to let the user see some sort of action is taking place. In which case they would need a method to prevent the pin.

3. Joshua says:

Oh nice I hadn't even realized the need to do this.

We have a loader EXE that displays splash, checks for updates, then launches the main EXE in the correct directory (carefully handing off focus to the main EXE's first window).

If they pin the main EXE it probably won't work due to starting in the wrong current directory, and even if it did it would fail to update itself when it needs to.

4. Mordachai says:

My thoughts were the same as BC & Gozer.  Basically, it seems like these other sub-modules don't need an icon at all (depending on the behavior of the main application).  i.e. if the external .exe windows are owned by the main .exe's main window, would that be enough to give full control to the user without each sub-module having it's own icon in the taskbar?

5. Leo Davidson says:

Neat! I knew about the AppId stuff (important for ensuring start menu shortcuts inherit your jumplist sometimes, too) but I had somehow missed the ability to set the pinned commandline. Thank you for pointing it out, Raymond.

6. AsmGuru62 says:

I have seen cases where main window would still be on TaskBar even if WS_EX_APPWINDOW is not set. Probably, some famous compatibility shim.

7. Mike Caron says:

It's also possible that the helper app /should/ show up in the task bar, because it's both physically and conceptually a separate window. In that case, hiding the icon is not desirable.

8. Mike Dunn says:

One issue I've seen is that when I set the Relaunch­Command, Relaunch­Display­Name­Resource, and Relaunch­Icon­Resource properties on a window, sometimes a shortcut shows up in the Start menu's MFU list with the colored highlight that normally only gets applied to newly-installed software.

I'm not sure why the shortcut would get promoted right away to the MFU list. Maybe it inherits points from the process that creates it? (I.e., if foo.exe creates a second window and sets those properties, the window starts out with the same number of points as foo.exe. In my case, foo.exe is my job, so it gets run all the time.)

9. Leo Davidson says:

Presumably the command-line settings are only persisted when the icon is pinned, so if it was already pinned by the user (in the past when the code didn't specify a command-line) then there is no way to update them except to ask the user to un-pin and re-pin the app?

10. ray says:

What we all really want to know is how to force our program to be pinned to the taskbar.

11. Hopefully people don't start using this technique to mark competitor programs as "don't pin me."

12. Unsure says:

Does anybody know how this informations can be updated? It seems that the values assigned to one AppID are not changeable after they are set for the first time: The shell creates a lnk file in a hidden directory that represents the pinned icon, but will not change it when the program sets different values with the methods shown by Raymond.

So, if your application needs to change the command line in the pinned icon: How can this be done? (Also note that the icons in the start menu and in the task bar are different hidden lnk files!)

13. Ken Hagan says:

@Maurits: "Hopefully people don't start using this technique to mark competitor programs as "don't pin me.""

If they do, Microsoft could simply add them to the list of malware that is removed by Windows Update each month.

14. Mordachai says:

@Mike Caron: I wondered about that too – but think of other software that does this: it's obnoxious, in most cases.  Much better when the icons stack, such as with Beyond Compare or Windows Explorer (they're all top-level windows in one sense, but they definitely should not occupy separate slots on the task bar)

15. WndSks says:

@AsmGuru62: A window does not need WS_EX_APPWINDOW for it to be displayed in the taskbar, that style is used to force a window onto the taskbar (and in other places?) even if it had other styles that would normally hide it from the taskbar (WS_EX_TOOLWINDOW).

The algorithm used by the taskbar is not documented AFAIK but it probably goes something like: ShowInTaskbar = (style&WS_VISIBLE)!=0 && ((!(exstyle&WS_EX_TOOLWINDOW) || !IsOwnedWindow(hwnd)) || (exstyle&WS_EX_APPWINDOW)!=0)

16. Joe says:

Process Explorer has the same problem on x64. The 32 bit version launches the x64 version but the pinned shortcut is the x64 version which is only temporary.

17. Judging by the comments, I guess the follow up question to this is: can I make my application pin a different executable than the one that spawned the window the user is trying to pin?

So if the user is pinning a window created by helper.exe, I want it to actually pin launcher.exe. Can this be done?

[Didn't I spend the entire second half of the article explaining how to do exactly that? -Raymond]
18. Worf says:

A good example of where a helper program might desire to have a taskbar icon but be unpinnable (or rather, relaunch the main executable if pinned) is a helper that does heavy lifting. E.g., a transcoder. The main application may offload encoding a video to a separate application it can control as a convenience (perhaps it was a port, or the component is supplied under a different license, e.g. open-source).

That subprocess may have a need to communicate with the user – e.g., a progress bar, some statistics, etc, so it needs its own taskbar icon, but unpinnable since it's useless without a host controlling app.

Perhaps the subprocess is a plugin that has high memory demands and can't fit with the main app. Or the main app is 32-bit and launches 64-bit plugins to process the user data faster.

All sorts of things can be designed this way, and if the may display information, you want a taskbar icon (I hate losing process windows in my window stack when they don't have taskbar icons. Explorer properties windows, for example… they sometimes take forever to tally up so you leave thhem open while you do other things only to have them buried 15 windows deep)

19. Simon says:

I read your description of what the user wanted to do, and was then astounded when I read on to find that, instead of slagging the user off, you actually explained how he could do what he wanted to do :-)

20. Unsure says:

@Simom: I don't get it: Why should that developer "slagged off"? I also have applications which must be started via a starter program which check for updates before executing the main program. The starter program has no visible UI, but the application (of course). So, I also have the problem that the "pin to.." shell functions must pin the starter program, not the application.

To work really reliable, the pinned command line must always be correct which leads to the question why a window can provide dynamic informations but has no way to update the already pinned icons accordingly. There is a hole in this concept.

[Since users can edit the shortcuts for pinned items, you have the problems of (1) correlating the RelaunchCommand to a specific pinned shortcut, and then (2) merging the application changes with the user's changes. And of course triggering disk access each time an application sets the RelaunchCommand property in order to check all the pinned shortcuts to see if they need updating. As we saw earlier, pinning captures the state of the pinned item. It is not a live link. -Raymond]
21. Erzengel says:

Somewhat related, I have programs that I want pinned, but when I launch them the window appears for a few seconds, then turns back to the pinned icon and a new window appears as a group. They just aren't pinned anymore.

22. David Walker says:

You know, I really, really like Windows 7.  Even so, I have un-pinned all icons from the taskbar and created a QuickLaunch folder with shortcuts, and I have created a toolbar called QuickLaunch that holds only icons (no text).  It's at the left side of my double-height taskbar.  All is well with the world.

23. Unsure says:

@Raymond: "As we saw earlier, pinning captures the state of the pinned item. It is not a live link"

Here is the hole in the current concept I'm taking about: Yes, currently lnk files are used to store the data, but as you say, this only allows static storage of a snapshot. To make them unpalatable easily, the informations for the AppIDs that is used for the pinned icons should have been stored in the registry instead (for example). The usage of the lnk files is an internal implementation detail which does not justify the current behavior.

Also, there seems to be a bug in the management of the hidden lnk files: They are not deleted when the app is unpinned. So, if a new version of the app comes out, which happens to require a new command line, it does not help to ask the user to repin the application. The old information from the old hidden lnk file is used forever.

[Windows XP worked that way, and some people didn't like it because you could delete the thing that the metadata referred to, and then the pin broke. No matter what you do, somebody will call you an idiot. -Raymond]
24. 640k says:

The root causue of this mess is that windows uses lnk files instead of real soft links.

[Last I checked, soft links can't pass command line parameters. -Raymond]
25. Simon says:

@Unsure: My comment wasn't intended as any reflection on the developer in question, it was rather intended as a semi-humourous dig at the fact that so often, Old New Thing blogs start off in the vein of 'A developer asked how to make his app to do X in the Windows user interface', and then go on to explain at length not how to do X but why the developer is incredibly stupid for wanting to do X in the first place. I'm so used to seeing those kinds of blogs that I did actually have to do a double take on reading this one when the first paragraph kicked off with 'A developer asked how to make his app to do X in the Windows user interface' but Raymond then went on to explain how to do X.

I found the fact that the explanation came as such as surprise to me somewhat amusing, hence my previous post. Sorry if that post wasn't clear.

26. Unsure says:

@Raymond: But when the old hidden link files are not deleted at unpinning, and in addition, are not updated with the current infos when repinning an app with the same AppID: How on earth can this be the correct behavior?

Also, even a lnk file does not need to be a static data storage. There are this advertising shortcuts (correct term?) which have different behavior. So, if information gets recorded in a pinned icon with an AppID, another type of lnk file should be used, which does not have a fixed embedded command line, but instead uses whatever was last recently set by the application via PKEY_AppUserModel_RelaunchCommand (in turn, this requires the shell to record this setting per AppID).

@Simon: I'm not a native speaker. Just got confused.

[That solution doesn't roam, but you're also making the solution way more complicated than it really is. If you want your relaunch action to change dynamically, then put the dynamic behavior in your app! Just make the command "contoso.exe -relaunch" and then you can do all your logic inside contoso.exe to decide "What should my relaunch action be today?" Sometimes the simple solution is better. -Raymond]
27. Robert Morris says:

Wait, what's the "middle-button mouse shortcut"?

28. Unsure says:

@Raymond; "If you want your relaunch action to change dynamically, then put the dynamic behavior in your app! "

This is not solving the problem. It is the location of the startup program itself that is not the same *forever* (because its located on a share and the host of the share will change now and then, for technical or political reasons [server replacement, server consolidation, changes in company structure]).

The app always offers the current path to the startup program to the shell via PKEY_AppUserModel_RelaunchCommand. But the shell never updates the hidden lnk file, even when the program is unpinned and repinned, which is clearly a bug (I wrote this a few time now).

But even if repinning would work correctly, it would be a much better user experience if this manual step would not be required at all. All the information is there, but the shell ignores it happely.

[But the alternative doesn't work either. If the relaunch command works only if you've run the app before, what happens if you run the app for the first time via the pinned shortcut? (Think roaming profile.) -Raymond]
29. I have an application installed on a network server (through a share). The application is started with some command line options with overrides for username, local destination and similar. When launching the "start-program" it checks for updates and then downloads all files from the share to the local hard-drive with the display of an UI. The "real" main program is then launched from the local hard-drive without any command line parameters.

Without doing anything; pinning of the application creates an lnk to the local main program (and the customers end up callings support since the updates stop working). Trying the code in the example but with the PKEY_AppUserModel_RelaunchCommand pointing to the share does not work. Pointing the code to local notepad.exe works. Setting PreventPinning works, but makes my customers unhappy.

Is there a built in and hidden rules that prevent relaunch to/from a share? This rule seems to be in effect only when setting PKEY_AppUserModel_RelaunchCommand but not when editing the lnk from the Windows GUI.

My current solution for the customers is to provide an instruction on how to manually pin our application and then edit the command line in the hidden store. :-(

Does it have to be this difficult? I must say that copying the original starting link of the program to the hidden store would be much easier to handle (and Yes I know pinning is supposed to be a user action and not an install action)!

[The taskbar will not let you pin things from network drives or removable media. Sorry. You could copy a local program that trampolines back to the network drive. -Raymond]
30. Joshua says:

@Aldus: Have a small EXE and associated .INI file in the install dir to pin that executes the network EXE.