How do I customize how my application windows are grouped in the Taskbar?

Benjamin Smedberg wants to know how to customize the icon used in the Taskbar for applications that are grouped, when the application is a runtime for multiple applications. (This is the other scenario I hinted at last time.)

Actually, customizing the icon is only part of what you want to happen when your application is a runtime. In that case, you really want each inner application to be exposed to the user as an entirely separate application. In other words, if your application is hosting Product A and Product B, you want the windows for Product A and Product B to group separately, have separate icons, maintain separate jump lists, all that stuff. Because from the user's point of view, they are separate programs. It just happens that under the covers, they're all being driven by a single EXE.

In Windows, the concept of an application is captured in something called an Application User Model ID, or AppID for short. For backward compatibility, if your application does not provide an explicit AppID, the shell will autogenerate one based on your EXE name. Therefore, the starting point for AppIDs is that an AppID maps to an EXE. But once you start customizing your AppID, you can play with this default correspondence.

All the information in this article came from the article Application User Model IDs (AppUserModelIDs) in MSDN.

Okay, so suppose your application is really a runtime for other applications. What you need to do is assign a different AppID to each of the applications you are hosting. The mechanism for this is up to you. Your applications might explicitly provide a unique ID, or you may be able to infer one. For example, if you are Internet Explorer and your "applications" are pinned Web sites, you can use the URL of the site being pinned as the unique ID.

You then get to take your unique IDs and create AppIDs for them. The format of an AppID is


where the Sub­Product is optional, and the Version­Information is present only if you want different versions of your app to be treated as distinct. (If you want an upgraded version to be a replacement for the old version, then omit the Version­Information so that the old and new versions use the same AppID.)

Note that you have to be careful how you auto-generate your AppIDs, since the resulting AppID needs to be legal. For example, you cannot just take a URL and use it as the Sub­Product of an AppID. URLs contain embedded periods, which violates the overall format, and they can be longer than 128 characters and can contain spaces, both of which are also called out in the documentation as prohibited. Internet Explorer addresses this problem by using a hash of the URL as its Sub­Product rather than the full URL.

You then assign this AppID to every window associated with the "application". You can do this for an entire process by calling SetCurrentProcessExplicitAppUserModelID, or you can do it on a window-by-window basis by setting the PKEY_AppUserModel_ID property.

Okay, let's write a program that shows how a runtime for other applications can use AppIDs to control its treatment in the taskbar. Of course, our sample won't actually be a runtime for anything; the "applications" that it hosts will simply be icons.

Start with the scratch program and make these changes:

#include <shellapi.h>
#include <shlobj.h>
#include <strsafe.h>

#define HOSTAPPID L"Contoso.Host"

void SetProcessAppId(LPCWSTR pszTarget)
  if (pszTarget[0]) {
    WCHAR szAppId[256];
    DWORD dwHash = 0;
    HashData((BYTE*)pszTarget, wcslen(pszTarget) * sizeof(WCHAR),
             (BYTE*)&dwHash, sizeof(dwHash));
    StringCchPrintfW(szAppId, ARRAYSIZE(szAppId),
                     L"%s.hosted-%08x", HOSTAPPID, dwHash);
  } else {
    StringCchPrintfW(szAppId, ARRAYSIZE(szAppId),
                     L"%s.main", HOSTAPPID);

int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                   LPWSTR lpCmdLine, int nShowCmd)

    ShowWindow(hwnd, SW_NORMAL);

    SetWindowText(hwnd, lpCmdLine);
    if (lpCmdLine[0]) {
      WCHAR szIcon[256];
      StringCchCopyW(szIcon, ARRAYSIZE(szIcon), ptszCmdLine);
      int iIcon = PathParseIconLocation(szIcon);
      if (iIcon == -1) iIcon = 0;
      HICON hico = ExtractIcon(hinst, szIcon, iIcon);
      SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hico);

Our simple host program just hosts an icon. The path to the icon is passed on the command line in the form "path,id", and for good measure, we put the icon path in the caption so you can see how it groups.

The real work happens in the SetProcessAppId function. If there is no command line, then we are running in standalone mode and set our Sub­Product to main. If we have a command line, then we hash it and use the hash to build our Sub­Product. I'm just using a four-byte hash with a simple has function; depending on how paranoid you are, you could use some other hash function, but make sure you can get the resulting AppID to fit into 128 characters. (This means that hex-encoded SHA512 is too big.)

Once we figure out what our AppID is, we set it for the entire process by calling SetCurrentProcessExplicitAppUserModelID.

Okay, let's take this program out for a spin. You can run it with the command lines

scratch %windir%\explorer.exe,0
scratch %windir%\explorer.exe,0
scratch %windir%\explorer.exe,1
scratch %windir%\explorer.exe,1

to see four copies of the program, two with one icon and two with another. Observe that when they group in the taskbar, the icon for the group is preserved, and that the two sets of programs group separately.

Note also that if you create shortcuts to your host program with a command line, you need to set the AppID in your shortcut, too. (Otherwise the shell won't know what the AppID of the resulting program will be, since you are setting it at runtime.)

Note also that we did not need to register the application as a host process because we explicitly set an AppID in our application and in our shortcuts. (Or at least, we said that we would. I didn't actually do it.)

Bonus reading: Developing for the Windows 7 Taskbar — Application ID.

Comments (17)
  1. skSdnW says:

    And going the other way (Two EXE's in the same group) has been possible since XP with the undocumented "TaskbarExceptionsIcons" registry key.

    And for people that don't like grouping? Explorer does not care about you:…/never-means-never-unless-you-are-the-taskbar

  2. henke37 says:

    Uhm, URLs can't contain spaces. Any such nonsense is the browser messing with the user by automatically doing substitution.

  3. Avi says:


    The screenshot in that blog post shows two invocations of calc and one of notepad.  The taskbar shows 2 buttons for calc and one for notepad.  I think someone doesn't know what Explorer means by "combine".

  4. John says:

    @Avi:  I think the complaint is that the two buttons for calc are connected to each other whereas the other buttons have space between them.  I tried it on my machine and it looks a bit odd.

  5. Avi says:


    That's my thought as well.  Unfortunately for the whiners, though, that's never what Explorer meant by "Never combine", at least in my experience.

  6. SomeGuyOnTheInternet says:

    VB.NET WinForms version… (I hope I got the declare correct!)

    Public Class frm1EXE2Group


     Declare Function SetCurrentProcessExplicitAppUserModelID Lib "Shell32.dll" (AppID As String) As Integer

     Private mstrAppID As String

     Public Sub New()

       ' This call is required by the designer.


       ' Add any initialization after the InitializeComponent() call.

       'AppID controls taskbar grouping. It is in the form CompanyName.ProductName.SubProduct.VersionInformation where SubProduct and VersionInformation are optional


       mstrAppID = "EDS.App" & Format(Int(Rnd() * 1000).ToString) 'for demonstration purposes, generate a random AppID

       Call SetCurrentProcessExplicitAppUserModelID(mstrAppID) 'N.B. this sets the AppID for the whole instance

       Me.Text = mstrAppID

     End Sub

     Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

       Dim f As New Form

       f.Text = "Child window of " & mstrAppID


     End Sub

    End Class

  7. xpclient says:

    Once again: Total end user control over the dumbed down Taskbar:

  8. Drak says:

    @WndSks: Combine means something different than 'group'. Combine means they become ONE icon/entry. Which it is not doing. Don't see the problem, really.

  9. xpclient says:

    @Avi, @Draks, MS is to blame here. They changed the meaning of "group" and "combine". In XP, there was "grouping" with actually the buttons were combined. In Windows 7, what was "grouping" behavior in XP is now "combining" and only combining can be disabled. Grouping of multiple windows of same app can't be disabled unless you change the AppIDs (not an end user thing)-7TT does it. Taking away options was Julie Larson Green's idea who was in charge of Windows 7 taskbar UX.

  10. skSdnW says:

    My point is that you cannot get a Win95-Win2000 style taskbar without 3rd party apps in Win7+, at least in XP and Vista you have a choice.

    Combining/Grouping makes no sense to me, I might have two Notepad windows open at the same time but that does not mean that they are related in any way.

  11. Rick C says:

    @WndSks:  That's what the preview thumbnails are for.  Sure, you can say you don't like them, and that's a legitimate point of view.  Over time, however, I've found for me that they're usually better than what you get from full-size taskbar buttons, for most windows, and grouped taskbar icons eliminate the problem of shrinking taskbar buttons.

  12. In my experience, the preview thumbnails are practically useless.  Three Notepad windows always look identical from the thumbnail.  For that matter, I generally have the same problem with multiple windows from the same app.

  13. Rick C says:

    @JamesJohnston, that is sometimes a problem.  Sometimes you can tell from the shape what the right document is, though.  It's probably easier if you're looking at code instead of text.

    Web browsers are where the thumbnails are great, as long as you don't have a bunch of tabs open to the same page, or to similar pages on the same site.

  14. From the fact this works with four separate instances of scratch, I take it this would work with, say, scratch32 and scratch64, without resorting to the undocumented registry key WndSks mentioned? (If I remember, I'll try it once I'm at a Windows machine.)

  15. skSdnW says:

    @jas88: AppUserModelIDs are Win7+ but they are much better than the registry key since you have grouping control over other things, not just the exe name. Besides, maybe someone else has the same .exe name as you…

  16. Programmerman says:

    Great, so the thumbnails are nearly useless. Hover over one for a moment and that window is brought to the front and all other windows are made fully transparent. Look at the content to see if it's what you want.

  17. 640k says:

    @Programmerman: Read above. The preview is of low quality (bilinear filtering) and in many cases the preview cannot distinguish instances uniquely.

Comments are closed.