Transilvania JUG



Customizing Windows taskbar groups

With the release of Windows 7, the well-known taskbar has gained a new feature: grouping icons from windows belonging to a specific process (or processes having the same name — e.g. web browser processes), in order to avoid clutter. Note that this behavior is different from its Windows XP counterpart, where grouping is performed only in the case of a full taskbar. In Windows 7 (and following version releases), taskbar grouping is performed based on a property called Application User Model ID. An extensive documentation about this property can be found at [2].

We developed a small-scale application [1] that illustrates how this default grouping policy can be altered in order to achieve the following derivative behavior: if an application (living inside a single process) contains multiple windows, these windows should not all be grouped together. Instead, each window should be able to place itself in a group of arbitrary choice. More formally, an application containing N windows will place each of these windows in one of the K available groups, where K can take values between 1 and N. Moreover, one should be able to move a window from one group to another, at runtime.

Our application creates several dummy JFrames and one controller JFrame that contains group selection facilities for each dummy frame. Initially, all frames belong to the same group (the default behavior for taskbar icons):

In our sample application, there are 4 groups: A, B, C, and D. The default group is A. Each frame can be moved into a different group by simply making the corresponding choice in the main (controller) frame. This is what the grouping looks like after performing several selections:

In what follows, a more technical insight will reveal implementation details. This project has two main sub-projects: the Java application itself (the window manager), which in turns relies on a small Windows-level library that manages the actual group assignment. Since in Java code there is no possibility of directly accessing and manipulating certain window properties (like the taskbar group), we need to turn to native function calls. Such functions would be bundled into .dll files loaded by the JVM at runtime.

The Java application (TaskbarJFrameGrouping) contains one native function signature:

private native void assignWindowToGroup(long windowHandle, String group);

This function has two arguments: a window (frame) handle, and a string representing the group with which this window must be associated. Calling this function implies a JNI call to a native function with an equivalent signature. This native function will do the actual grouping. Essentially, the Windows operating system maintains a property store for each window. One entry in the property store is the taskbar group, which can be modified. Detailed information about the actual native code involved can be found at [3]. Getting the window handle can easily be done in Java, using the (partially deprecated) class. See [4] for details. We want our application to be cross-platform. For other platforms, the grouping functionality should simply be disabled, no crashes are allowed. For this reason, the WComponentPeer class is accessed using reflection only when the platform provides it.

After having written the Java application, one problem still remains unsolved: providing the implementation for the native function. Using the straightforward JNI approach [5], a C header file is generated. This file contains the signature of the native function, with all types mapped accordingly:

JNIEXPORT void JNICALL Java_com_example_taskbar_TaskbarJFrameGrouper_assignWindowToGroup(JNIEnv*, jobject, jlong, jstring);

What remains to be done is providing an implementation for this function. A second file (.cpp) is created, where an implementation for this function is provided. For convenience, these files are incorporated into a Microsoft Visual C++ DLL project (where the binary output will be a .dll file). The project is provided with configuration files as well, because certain include and build options were modified from their default values. For example:

  • Project | Properties | Configuration properties | Platform Toolset: changed from v100 to Windows 7.1 SDK (this component has to be installed separately, as the platform is not able to generate x64 .dlls otherwise)
  • Project | Properties | Configuration properties | VC++ Directories | Include Directories: added the Java include\win32 and include\ subdirectories (necessary because these are the locations for the JNI header files)
  • Project | Properties | Configuration properties | Linker | Input | Additional dependencies: added shlwapi.lib.

Generating either an x86 or an x64 .dll is performed by setting the execution platform:

  • Build | Configuration manager | Platform: Win32 or x64.

The Java sub-project is provided with a .bat file that can perform clean, compile and run operations, as well as .dll packaging inside a .jar file. The operation type is provided as argument to the .bat file. For example:

> target.bat clean

> target.bat compile

> target.bat run

We chose a .bat file instead of a nmake file because we didn’t want users to install extra software on their Windows machine. For Linux machines, we provided similar targets in a Makefile.

This project is in alpha stage only, so feedback is most welcome. Thank you. 🙂







Leave a Reply

Your email address will not be published. Required fields are marked *