Win32::GuiTest - Alternate distribution of Perl GUI Test Utilities.
use Win32::GuiTest qw(FindWindowLike GetWindowText SetForegroundWindow SendKeys);
$Win32::GuiTest::debug = 0; # Set to "1" to enable verbose mode
my @windows = FindWindowLike(0, "^Microsoft Excel", "^XLMAIN\$"); for (@windows) { print "$_>\t'", GetWindowText($_), "'\n"; SetForegroundWindow($_); SendKeys("%fn~a{TAB}b{TAB}{BS}{DOWN}"); }
// This batch file comes with MS Visual Studio. Running // it first might help with various compilation problems. vcvars32.bat
perl makefile.pl nmake nmake test nmake install
See more details in the DEVELOPMENT section elswhere in this document.
If you are using ActivePerl 5.6 (http://www.activestate.com/Products/ActivePerl/index.html) you can install the binary package I am including instead. You will need to enter PPM (Perl Package Manager) from the command-line. Once you have extracted the files I send you to a directory of your machine, enter PPM and do like this:
C:\TEMP>ppm PPM interactive shell (2.0) - type 'help' for available commands. PPM> install C:\temp\win32-guitest.ppd Install package 'C:\temp\win32-guitest.ppd?' (y/N): Y Retrieving package 'C:\temp\win32-guitest.ppd'... Writing C:\Perl\site\lib\auto\Win32\GuiTest\.packlist PPM>
I extracted them to 'c:\temp', please use the directory where you extracted the files instead.
Most GUI test scripts I have seen/written for Win32 use some variant of Visual Basic (e.g. MS-VB or MS-Visual Test). The main reason is the availability of the SendKeys function.
A nice way to drive Win32 programs from a test script is to use OLE Automation (ActiveX Scripting), but not all Win32 programs support this interface. That is where SendKeys comes handy.
Some time ago Al Williams published a Delphi version in Dr. Dobb's (http://www.ddj.com/ddj/1997/careers1/wil2.htm). I ported it to C and packaged it using h2xs...
The tentative name for this module is Win32::GuiTest (mostly because I plan to include more GUI testing functions).
I've created a Yahoo Group for the module that you can join at http://groups.yahoo.com/group/perlguitest/join
Also, an initial version of a script recording application has been written to use with this module. A copy of it may be found with this distribution (Recorder\Win32GuiTest.exe) or can be obtained at http://sourceforge.net/projects/winguitest
If the documentation of these functions is not satisfactory, you can try running a search on http://msdn.microsoft.com/ using the name of the function. Some of these functions are described there.
The alternate distribution of this module - the one you are looking at now - has its own CVS repository at http://sourceforge.net/projects/winguitest Patches to both the code and the documentation are welcome.
SendKeys($keys[,$delay])
The keystrokes to send are specified in KEYS. There are several characters that have special meaning. This allows sending control codes and modifiers:
~ means ENTER + means SHIFT ^ means CTRL % means ALT
The parens allow character grouping. You may group several characters, so that a specific keyboard modifier applies to all of them.
E.g. SendKeys(``ABC'')
is equivalent to SendKeys(``+(abc)'')
The curly braces are used to quote special characters (SendKeys(``{+}{{}'') sends a '+' and a '{'). You can also use them to specify certain named actions:
Name Action
{BACKSPACE} Backspace {BS} Backspace {BKSP} Backspace {BREAK} Break {CAPS} Caps Lock {DELETE} Delete {DOWN} Down arrow {END} End {ENTER} Enter (same as ~) {ESCAPE} Escape {HELP} Help key {HOME} Home {INSERT} Insert {LEFT} Left arrow {NUMLOCK} Num lock {PGDN} Page down {PGUP} Page up {PRTSCR} Print screen {RIGHT} Right arrow {SCROLL} Scroll lock {TAB} Tab {UP} Up arrow {PAUSE} Pause {F1} Function Key 1 ... ... {F24} Function Key 24 {SPC} Spacebar {SPACE} Spacebar {SPACEBAR} Spacebar {LWI} Left Windows Key {RWI} Right Windows Key {APP} Open Context Menu Key
All these named actions take an optional integer argument, like in {RIGHT 5}. For all of them, except PAUSE, the argument means a repeat count. For PAUSE it means the number of milliseconds SendKeys should pause before proceding.
In this implementation, SendKeys always returns after sending the keystrokes. There is no way to tell if an application has processed those keys when the function returns.
SendMouse($command)
{LEFTDOWN} left button down {LEFTUP} left button up {MIDDLEDOWN} middle button down {MIDDLEUP} middle button up {RIGHTDOWN} right button down {RIGHTUP} right button up {LEFTCLICK} left button single click {MIDDLECLICK} middle button single click {RIGHTCLICK} right button single click {ABSx,y} move to absolute coordinate ( x, y ) {RELx,y} move to relative coordinate ( x, y )
Note: Absolute mouse coordinates range from 0 to 65535. Relative coordinates can be positive or negative. If you need pixel coordinates you can use MouseMoveAbsPix.
Also equivalent low-level functions are available:
SendLButtonUp() SendLButtonDown() SendMButtonUp() SendMButtonDown() SendRButtonUp() SendRButtonDown() SendMouseMoveRel(x,y) SendMouseMoveAbs(x,y)
MouseMoveAbsPix($x,$y)
# Moves to x=200, y=100 in pixel coordinates. MouseMoveAbsPix(200, 100);
MouseMoveWheel($change)
Positive or negative value to direct mouse wheel movement.
FindWindowLike($window,$titleregex,$classregex,$childid,$maxlevel)
You may specify the handle of the window to search under. The routine searches through all of this windows children and their children recursively. If 'undef' then the routine searches through all windows. There is also a regexp used to match against the text in the window caption and another regexp used to match against the text in the window class. If you pass a child ID number, the functions will only match windows with this id. In each case undef matches everything.
GetWindowID($window)
Returns the control Id of the specified window.
PushButton($button[,$delay])
PushChildButton(GetForegroundWindow, BUTTON, DELAY)
PushChildButton($parent,$button[,$delay])
parent - the parent window of the button
button - either the text in a button (e.g. ``Yes'') or the control ID of a button.
delay - the time (0.25 means 250 ms) to wait between the mouse down and the mouse up event. This is useful for debugging.
WaitWindowLike($parent,$wndtitle,$wndclass,$wndid,$depth,$wait)
parent - Where to start (parent window)
wndtitle - Regexp for the window title
wndclass - Regexp for the window class name
wndid - Numeric Window or Control ID
depth - How deep should we search before we stop
wait - How many seconds should we wait before giving up
WaitWindow($wndtitle,[$wait])
wndtitle - Regexp for the window title
wait - How many seconds should we wait before giving up
Determines if a window has the specified style. See sample script for more details.
Determines if a window has the specified extended style. See sample script for more details.
GetMenuItemCount($menu)
MenuSelect($menupath,$window,$menu)
Simple Examples: # Exit foreground application through application menu. MenuSelect(``&File|E&xit'');
# Exit foreground application through system menu MenuSelect("&Close", 0, GetSystemMenu(GetForegroundWindow(), FALSE));
Returns a hash of which there are currently 2 keys: type can be either ``string'' or ``separator'' - this is the type of the menu item text is the visible text of the menu item (provided only for ``string'' type)
WARNING: This is an experimental function. Its behavior might change.
window = Regexp for a Window caption / Child caption, or just a Child ID.
parent = Handle to parent window. Default is foreground window. Use
GetDesktopWindow()
return value for this if clicking on an application
title bar.
x_offset = Offset for X axis. Default is 0.
y_offset = Offset for Y axis. Default is 0.
button = {LEFT}, {MIDDLE}, {RIGHT}. Default is {LEFT}
delay = Default is 0. 0.50 = 500 ms. Delay between button down and button up.
Simple Examples:
# Click on CE button if its parent window is in foreground. MouseClick('^CE$');
# Right click on CE button if its parent window is in foreground MouseClick('^CE$', undef, undef, undef, '{RIGHT}');
# Click on 8 button window under the specified parent window; where # [PARENTHWND] will be replaced by a parent handle variable. MouseClick('8', [PARENTHWND]);
# Click on Calculator parent window itself MouseClick('Calculator', GetDesktopWindow());
WMGetText($hwnd)
*WMSetText(hwnd,text)
*GetCursorPos()
*GetCaretPos()
SetFocus(hWnd)
GetDesktopWindow()
*GetWindow(hwnd,uCmd)
*GetWindowText(hwnd)
*GetClassName(hwnd)
*See MSDN for more details.
You can also check out MSDN to see an overview of the Window Classes.
GetParent(hwnd)
*GetWindowLong(hwnd,index)
*SetForegroundWindow(hWnd)
*GetChildWindows(hWnd)
IsChild(hWndParent,hWnd)
*GetChildDepth(hAncestor,hChild)
Features/bugs: If the given ``ancsetor'' is not really an ancestor, the return value is the distance of child from the root window (0) If you supply the same id for both the ancestor and the child you get 1. If the ancestor you are checking is not 0 then the distance given is 1 larger than it should be.
see eg\get_child_depth.pl
SendMessage(hWnd,Msg,wParam,lParam)
*It is most likely you won't use this directly but through one of the functions implemented already in Win32::GuiTest.
See the guitest.xs for some examples.
PostMessage(hwnd,msg,wParam,lParam)
*CheckButton(hwnd)
UnCheckButton(hwnd)
GrayOutButton(hwnd)
IsCheckedButton(hwnd)
IsGrayedButton(hwnd)
IsWindow(hwnd)
*ScreenToClient(hwnd,x,y)
*ClientToScreen(hwnd,x,y)
*GetCaretPos(hwnd)
*ASetFocus(hWnd)
*AGetFocus(hwnd)
*AGetActiveWindow(hwnd)
*AGetForegroundWindow()
*SetActiveWindow(hwnd)
*AEnableWindow(hwnd,fEnable)
*ShowWindow(hwnd,nCmdShow)
*AScreenToNorm(x,y)
NormToScreen(x,y)
GetScreenRes()
GetWindowRect(hWnd)
*GetClientRect(hWnd)
*GetComboText(hwnd,index)
GetListText(hwnd,index)
GetComboContents(hWnd)
GetListContents(hWnd)
IsKeyPressed($key)
IsKeyPressed("ESC"); IsKeyPressed("A"); IsKeyPressed("DOWN");
SendRawKey($virtualkey,$flags)
KEYEVENTF_EXTENDEDKEY - Means it is an extended key (i.e. to distinguish between arrow keys on the numeric keypad and elsewhere). KEYEVENTF_KEYUP - Means keyup. Unspecified means keydown.
#Example use Win32::GuiTest qw/:FUNC :VK/;
while (1) { SendRawKey(VK_DOWN, KEYEVENTF_EXTENDEDKEY); SendKeys "{PAUSE 200}"; }
GetListViewContents($window)
Returns a list of the contents of the specified list view.
Selects an item in the list view based off an index (zero-based).
# Select first item, clears out any previous selections. SelListViewItem($win, 0); # Select an *additional* item. SelListViewItem($win, 1, 1);
Selects an item in the list view based off text (case insensitive).
# Select first item, clears out any previous selections. SelListViewItemText($win, 'Temp'); # Select an *additional* item. SelListViewItemText($win, 'cabs', 1);
Determines if the specified list view item is selected.
GetTabItems($window)
Returns a list of a tab control's labels.
Selects a tab based off an index (zero-based).
Selects a tab based off text label (case insensitive).
Determines if the specified tab item is selected.
Selects a tree view item based off a "path" (case insensitive).
# Select Machine item and Processors sub-item. SelTreeViewItemPath($window, "Machine|Processors");
SelTreeViewItemPath($window, "Item");
GetTreeViewSelPath($window)
Returns a string containing the path (i.e., "parent|child") of the currently selected tree view item.
$oldpath = GetTreeViewSelPath($window); SelTreeViewItemPath($window, "Parent|Child"); SelTreeViewItemPath($window, $oldpath);
A class to manage a Windows DIB section. Currently limited in functionality to 24-bit images. Pulled from old code into GuiTest when I (jurasz@imb.uni-karlsruhe.de) needed to create several grayscale screen dumps.
Possible future extenstions: other color resolutions, loading, comparison of bitmaps, getting from clipboard.
Synopsis:
$ds = new Win32::GuiTest::DibSect; $ds->CopyWindow($w); $ds->ToGrayScale(); $ds->SaveAs("bla.bmp"); $ds->ToClipboard();
$ds->CopyClient(GetDesktopWindow(), \@{[GetWindowRect($w)]});
If you would like to participate in the development of this module there are several thing that need to be done. For some of them you only need Perl and the latest source of the module from CVS for others you'll also need to have a C++ compiler.
To get the latest source code you need a CVS client and then do the following:
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/winguitest login cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/winguitest co Win32-GuiTest
See more detailed explanations here http://sourceforge.net/projects/winguitest/
To setup a development environment for compiling the C++ code you can either buy Visual Studio with Visual C++ or you can download a few things free of charge from Microsoft. There might be other ways too we have not explored.
The instructions to get the free environment are here:
From http://www.microsoft.com/ download and install:
1) Microsoft .NET Framework Version 1.1 Redistributable Package 2) .NET Framework SDK Version 1.1
This is not enough as there are a number of header files and libraries that are not included in these distributions. You can get them from Microsoft in two additional downloads. For these you will have to be using Internet Explorer. Visit
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
and install
1) Core SDK 2) Microsoft Data Access Components 2.7
Before you can compile you'll have to open a command prompt and execute the
sdkvars.bat
script from the.NET SDK that will set a number of environment
variables. In addition you'll have to run the setenv.bat
you got with the
Core SDK (and located in C:\Program Files\Microsoft SDK) with the appropriate
parameters. For me this was /XP32 /RETAIL
In order to finish the packaging you'll also need the tar, gzip and zip utilities from
http://gnuwin32.sourceforge.net/packages.html
I have not tried it yet.
After this you will probably be able to do the normal cycle:
perl makefile.pl nmake nmake test
or run
perl makedist.pl
Here are a few items where help would be welcome.
Improve Tests Improve documentation Add more examples and explain them
Add more calls to the C++ backend Fix current calls
32bit custom controls (some already implemented) Possibly Java interfaces Retreive the list of the menu of a given window.
1.50.3-ad
Moved to the CHANGES file.
The SendKeys function is based on the Delphi sourcecode published by Al Williams <http://www.al-williams.com/awc/> in Dr.Dobbs <http://www.ddj.com/ddj/1997/careers1/wil2.htm>.
Copyright (c) 1998-2002 Ernesto Guisado, (c) 2004 Dennis K. Paulsen. All rights reserved. This program is free software; You may distribute it and/or modify it under the same terms as Perl itself.
Ernesto Guisado (erngui@acm.org), http://triumvir.org
Jarek Jurasz (jurasz@imb.uni-karlsruhe.de), http://www.uni-karlsruhe.de/~gm07 wrote
DibSect and some other pieces (see Changes
for details).
Dennis K. Paulsen (ctrondlp@cpan.org) wrote various pieces (See Changes
for
details).
Thanks very much to: