The goal of this project is to create a program which can capture a defined region of a running tasks window. This is done by first choosing the task from a list similar to Windows’ own task switching control (it appears in the center of the screen when the user presses the Alt and Tab key), after that you can select the region to be captured by clicking and dragging a rectangle around it.
Using the Code
First of all, let us introduce files of the solution:
- Config: Configure application behavior
- TestForm: Used to test different task switching modes
- FormSelectApp: Form displaying appropriate applications. It is a clone of the built-in control of Windows XP
- Win32: Encapsulates Win32 API calls
TestForm - Test Environment
This is the main form of the program where the user can set how screen capturing will work. If capture Alt + Tab is not checked, first the user has to click on the Custom button to show the task switching control (
FormSelectApp), while if checked, the program behaves as a clone of Windows’ built-in task switching tool, captures the event of pressing Alt + Tab and then shows
FormSelectApp. Win32 classes
RegisterHotkey() function is called to register Alt+Tab and Alt+Shift+Tab as a hotkey with ids 111 and 112. To disable Windows’ built-in functionality, we override Windows’ message handling (
WndProc()), and watch for pressings of hot keys with ids 111 and 112. Each hot key starts
FormSelectApp first by calling
OpenFormSelectApp(), and besides that, repeatedly pressing them tells it to iterate through the elements shown in its list. To imitate Windows’ task switching mechanism, we have to watch out for the release of the left Alt key. This is done by using global keyboard hooks and by using the code presented in this article.
When an Alt+Tab or Alt+Shift+Tab hot key event is processed first,
OpenFormSelectApp() initializes a global hook for listening to
KeyUp events. The
KeyUp() eventhandler processes the release of left Alt key, and when
ShouldSwitchToSelected is set to
true, it calls
SwitchToSelected. It brings the selected
ListViewItem’s task to the foreground and closes
ShouldSwitchToSelected is set to
false when the user cancels the Alt+Tab mechanism by pressing something else other than Alt, Tab, Shift, Enter or the arrow keys. The pressing of these keys is handled by the
KeyDown eventhandler. Pressing the right arrow key tells
FormSelectApp to select the next
ListViewItem, the left selects the previous item. Pressing the Enter key triggers the release of the left Alt key, i.e. switching to the selected task. Pressing anything else other than these turns
false and raises a
KeyUp event with left Alt.
closes the form and unregisters the eventlisteners for pressing and releasing keys.
FormSelectApp - Displaying Applications
This is the form which is used to select the task to capture from. It has a
ListView control on it showing icons of the running apps and a
TextBox showing the title of the currently selected ones.
Depending on the forms'
DisplayMode property, it can show icons only or icons and titles under them. In the constructor, we use the
EnumWindows() function of Win32 with a callback pointer created from Win32’s
EnumWindowsCallBack() delegate function. Their exact functionality is described in a separate section.
EnumWindows() is used to enumerate through all the currently running Windows, and by passing a callback pointer of our function to it, we can examine them one by one.
FormSelectApps AddWindowItem() is used as a delegate function for Win32’s
AddWindowItem delegate to add a
WindowItem to the
EnumWindowsCallback() function calls it. This is done by creating a new
ListViewItem containing the title of the passed
WindowItem, and when passed, the index of its icon is added to an
DrawItem() function is overridden for its look to be as close to Windows’ own task switching components' look as possible. Without custom drawing, it is impossible to implement the blue border around the selected item. When the user clicks on a
SetForegroundWindow() function is called to bring it to the foreground. This double clicking event handler is called upon pressing the enter key, or when the user releases the left Alt key if it is captured.
SelectPrevious() functions are called when the user presses the arrow keys, or if Alt + Tab is captured and Tab or Shift+Tab is pressed along with left Alt.
In order to be as close to the Windows’ own task switching components' look as possible, we have to use bevel around the controls. The implementation is based on this article.
It has been changed to derive from
Panel instead of
Control (to make it easier to put items in it) and a new property,
BevelWidth was added.
The reason for creating this class was to separate Windows’ low-level calls which it handles from the presentation layer (
CallBackPtr is a delegate used in user32.dll's
EnumWindows function to call back from every running application. We use
EnumWindowsCallBack as a delegate function for
EnumWindows to call with the actual Windows handle. While processing these Windows with
EnumWindowsCallBack, we collect the ones to display and add them to our
ListView. The decision is made by calling user32.dll's
GetParent function, which returns a zero
int pointer for Windows without parents. User32.dll's
GetWindowLong() functions are used to filter Windows without owners in tool style and Windows with an owner in application style. The title of the Windows is extracted by user32.dll's
We get hold of the icons with the
GetWindowBigIcon() function. For each adequate window, we create a
WindowItem and call the delegate function
AddWindowItem() with it.
GetWindowBigIcon() returns with the icon of the window handle it was passed. It uses user32.dll’s
SendMessageTimeout() function with a message to return with the passed window handlers icon (
WM_GETICON). If it fails (the icon handle created from the result variable is
null), then we use another user32.dll function,
GCL_HICON to retrieve the required icon. If it still fails, we use
SendMessageTimeout() again with a message
Points of Interest
The article introduces how to use delegates to communicate between low level API classes and GUI. Retrieving icons seems to be a simple task, however most people forget the case that some applications cannot respond to
SendMessage correctly and the returned icon is
null. The article also describes how to use custom draw of
Listview items to display a frame around the selected item.
- 2008.05.29: First version of the article