Introduction

This tool allows you to alter the order of the window buttons in your taskbar.

Background

I normally have about 20 windows open, and switching between them is a lot quicker if they are in my "normal" order. For instance, I always like Outlook to be the first button in my taskbar, so I know where to click when I get mail. The problem is that when Outlook hangs, I have to kill its process. Then when I restart it, it appears at the end of the list, and is soon hidden among my other windows. With this tool, I can easily move Outlook back to the start where it belongs, without closing and re-opening all my other windows.

This problem is a pet peeve of mine - if the order of your windows doesn't bother you, then you don't need this tool

How it works

This tool uses a lot of PInvoke calls - if you want to see the details, look in the "Common/Kernel32.cs" and "Common/User32.cs" files in the source. It also uses an unsafe method (my first one), which made me feel a bit dirty . I started to write it in C++, but after four years of C#, it was just too painful.

The basic idea is to get a list of the windows in the taskbar, show them and allow them to be reordered, and then apply the new ordering. Sounds easy

Getting a list of the windows

This proved to be the hardest part. Fortunately, I found a way to do it, but it only works on Windows XP and later. Windows 2000 uses a different window hierarchy, and I don't know (or care about earlier OSs.

Getting the buttons window handle

Using Spy++, I found that the buttons window in the taskbar is actually a ToolbarWindow32 - one of the common Windows controls. This was luck, as Microsoft could easily have used a custom window class. As it is, we know quite a lot about ToolbarWindow32. The first thing to do is get its window handle. We can do this using GetDesktopWindow and FindWindowEx:

Note that the window count is more than we would expect. See Groups for an explanation.

Getting each button's info

This also should be easy, because we know about the TB_GETBUTTON message. It's a bit more complicated because we are accessing a ToolbarWindow32 from a different process, so we have to do a bit of memory management. See Cross-process memory access for details. It also uses an unsafe block, as we need to pass the addresses of structures as pointers. I will concentrate on the algorithm for now:

Note that this is a pseudo-code - it doesn't compile, it just gives you an idea of the algorithm.

We now have a TBBUTTON structure filled with information for each button. If we look at the fsState field, we find that some of the buttons are hidden, and that there are the expected number of visible buttons. Lucky again Note that the TBBUTTON structure has a 32-bit dwData field that is for user data. This will come in handy later on...

Getting the button text

The TBBUTTON structure has a String field, but it's not that easy to use. It's easier to use a TB_GETBUTTONTEXT message to let the control do the work:

Note that this message takes a CommandId, which we got from the TBBUTTON structure.

Getting the window handle

This was the really lucky part. I thought to myself: "Where would I keep the window handle?". They must keep it somewhere, to enable the activation of the correct window when a button is selected. The obvious place to keep it would be in a structure for each button, and the obvious place to keep the pointer to this structure would be in the dwData field of each TBBUTTON.

So I had a look at the dwData fields, and they appeared to be pointers. OK so far. Then I had a look at the memory they pointed to, and there they were: the first field stores the window handle )) Microsoft developers aren't so different, after all

Using the window handle

The rest is easy. I used Thomas Caudal [^] 's TreeListView [^] which made the UI easy. A bit of glue to handle moving the items around, and then the "Apply" button.

All we have to do to apply the new order is to hide all the windows using ShowWindow, and then show them one at a time in the desired order. Easy peasy

Points of interest

A few things could do with a bit more explanation.

Groups

In XP and later, you have the option of grouping similar taskbar buttons together. This tool works with this switched on or off. In either case, Explorer adds a dummy button, which is hidden, at the start of each group. This is why TB_BUTTONCOUNT reports more buttons than are visible. These extra buttons are easy to identify, as they don't have a window handle.

Out of interest, the behaviour is controlled by a couple of registry entries:

"HKCU\Software\Microsoft\Windows\CurrentVersion \Explorer\Advanced\TaskbarGlomming" is 0 if grouping is switched off, and 1 if it is on.

"HKCU\Software\Microsoft\Windows\CurrentVersion \Explorer\Advanced\TaskbarGroupSize" is the minimum number of windows before they are collapsed into one button. I have this set to 99 to effectively turn it off. You can set this value with TweakUI from the XP PowerToys (it's probably safer).

Cross-process memory access

This was a tricky one. Some of the required Win32 functions use structures to move information around. This works well if you and your target are in the same process space, but falls apart in our case. The naive implementation doesn't work. If you declare a structure, and get a pointer to it to pass into your function, the pointer is to some address in your virtual memory space. The function can't know this, and thinks that it is pointing to a block of its virtual memory. This can only lead to General Protection Faults at best.

The solution is to allocate space for your structure in Explorer's virtual memory, call the function, and then copy the data back into your virtual memory so you can use it. This works without elevated privileges because Explorer runs under the local user account. As we use a few functions one after the other, I just allocate a page of memory, and use this for all the structures.

The basic algorithm is this:

use GetWindowThreadProcessId to get the process ID from a window handle.

use OpenProcess to turn the process ID into a process handle.

use VirtualAllocEx to allocate some memory in Explorer's virtual memory space.

call the required API function to fill the buffer.

use ReadProcessMemory to copy the buffer into our virtual memory space.

don't forget to use VirtualFreeEx to free the buffer, and CloseHandle to finish.

The unsafe keyword allows us to use pointers, and the fixed keyword locks the object in memory, so that the GC doesn't move it while we copy the data back to our virtual memory space. Apart from that, it's just a bit of P/Invoke. Check out the "Common\Kernel32.cs" and "Common\User32.cs" files in the source if you're interested.

Icons

This is just a bit of fluff. The TreeListView doesn't work properly without an ImageList, so I added one. Then I thought it would be good to show the default window icons to distinguish the items. My first attempt wasn't too successful. I used GetClassLong with the GCL_HICONSM parameter to get the small icon associated with the window class. This works for most applications, but not for my .NET apps. It seems that the framework generates a new window class each time an application runs, but it doesn't set the icon handle - nice These apps do, however, correctly return an icon handle when sent a WM_GETICON message, so that's what I did in the end. I also set the window class icon handle for this tool in the main form's OnHandleCreated override, but this isn't necessary.

Shameless plugs

I used the code from a couple of my previous articles in this project. I used my OSVersion [^] class to check that we're running on XP or later. And I used my TreeCollection [^] as a backing store. I also used the GlobalMemoryStatusEx API to display memory usage in the "OS Version" dialog - just for fun

Conclusion

Well, that's about it. If you just want to use this tool, that's fine - I hope you find it useful. If you want to dig a little deeper, then you can look at the code as an example of P/Invoke and cross-process memory access.

I just want to mention the pinvoke.net [^] wiki site - very handy and mostly accurate

Share

About the Author

I built my first computer, a Sinclair ZX80, on my 11th birthday in 1980.
In 1992, I completed my Computer Science degree and built my first PC.
I discovered C# and .NET 1.0 Beta 1 in late 2000 and loved them immediately.
I have been writing concurrent software professionally, using multi-processor machines, since 1995.

In real life, I have spent 3 years travelling abroad,
I have held a UK Private Pilots Licence for 20 years,
and I am a PADI Divemaster.

I now live near idyllic Bournemouth in England.

If you would like help with multithreading, please contact me via my website:

Hint: The GetTBButton method can be used to get the buttons in the System Tray as well. Since it is a ToolbarWindow32 as well, it was just copy and paste. To get the System Tray instead of the Toolbar, you use the following code:

In Windows 7, the TaskList was completely revamped. To my knowledge it is no longer a ToolbarWindow32.
The class is now "MSTaskListWClass". I believe that it no longer responds to any of the TB_ window messages.
Do you (or anyone reading) have any ideas about how to interact with this new taskbar? I am particularly interested in retrieving the RECT of the taskbar items...something which the ITaskList does not provide (to my knowledge).

... your code is vastly undocumented. I know this doesn't bother some people but some others as me really can't read it.

I was looking at your code because I am searching a way to resize the taskbar buttons in the ToolbarWindow32, height and width. I know how to change the width using registry but not the height. If you have any idea, I'll be happy to know.

Somehow, even though the TM_ButtonCount seems to work fine, it does not work when trying to access the Text and handle.
Running under Windows Vista X64.
I suspect that the new security layer is preventing my C# program to access the explorer process correctly through SendMessage.

I am able to use the approach on vista 32bit. I havent tested it on 64bit. But my point is the security layer is not blocking anything as the explorer process and this process run at same integrity level(Medium).

The problem is related to the padding bytes in the middle of the structure.

I solved the problem creating my own TBBUTTON32 and TBBUTTON64 structures and choosing the one to use at runtime. I know this is not the best solution, but I need to have only one executable for both 32 and 64 bit system, and this solution is good for me