Introduction
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 KeyDown
and KeyUp
events. The KeyUp()
eventhandler processes the release of left Alt key, and when ShouldSwitchToSelected
is set to true
, it calls FormSelectApp
’s SwitchToSelected
. It brings the selected ListViewItem
’s task to the foreground and closes FormSelectApp
by CloseFormSelectApp()
. 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 ShouldSwitchToSelected
to false
and raises a KeyUp
event with left Alt. CloseFormSelectApp()
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 ListView
when 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 ImageList
named appIconList
. The ListView
’s 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 ListViewItem
, Win32’s 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. SelectNext()
and 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.
Win32
The reason for creating this class was to separate Windows’ low-level calls which it handles from the presentation layer (MainForm
and FormSelectApp
).
Finding Windows
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 GetWindow()
and 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 GetWindowText()
function.
Retrieving Icons
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, GetClassLong()
with GCL_HICON
to retrieve the required icon. If it still fails, we use SendMessageTimeout()
again with a message WM_QUERYDRAGICON
.
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.
History
- 2008.05.29: First version of the article
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.