Hi Dennis, Hi Gabor,
Have a look at the code below. I am providing results of diff -c.
The following changes were done:
1. FindWindowLike was fixed. It was treating Control ID = 0 and
Control ID = undef as the same.
This is the fix:
***************
Fragment with the fix
*** 385,394 ****
(!$classre || $sClassname =~ /$classre/))
{
DbgShow("Matched $1\n") if $1;
! if (not defined $ID) {
# If find a match add handle to array:
push @found, $hwnd;
! } elsif ( defined $sID) {
if ($sID == $ID) {
# If find a match add handle to array:
push @found, $hwnd;
----------------
Fragment without the fix
--- 385,394 ----
(!$classre || $sClassname =~ /$classre/))
{
DbgShow("Matched $1\n") if $1;
! if (!$ID) {
# If find a match add handle to array:
push @found, $hwnd;
! } elsif ($sID) {
if ($sID == $ID) {
# If find a match add handle to array:
push @found, $hwnd;
2. Added function PushChildById.
It is something like PushChildButton but considers only Control ID of
the button and allows specifying a depth of a search in the window
hierarchy tree. It allows to avoid problems with PushChildButton which
I described earlier. The function code (together with the decription)
follows:
=item PushChildById( $parent, $button, $level, $delay )
Allows pushing a button, which control id is eqaul to a given
parameter.
PushChildButton tries to match parameter against control id or
caption.
PushChildById matches only against control id. Secondly, PushChildById
allows specifying search depth in the windows hierarchy tree.
The default is 2, which means that only direct children will be
pushed.
=cut
sub PushChildById {
my $parent = shift;
my $button = shift;
my $level = shift;
my $delay = shift;
$level = 2 unless defined( $level );
$delay = 0 unless defined($ delay );
my @buttons = FindWindowLike( $parent, undef, undef, $button,
$level );
PostMessage($buttons[ 0 ], WM_LBUTTONDOWN(), 0, 0);
# Allow for user to see that button is being pressed by waiting
some ms.
select(undef, undef, undef, $delay) if $delay;
PostMessage($buttons[ 0 ], WM_LBUTTONUP(), 0, 0);
return;
}
3. Added WaitForReady function, which waits until the application is
ready to receive more input. The code follows:
/*
* Piotr Kaluski <pkaluski@...>
*
* WaitForWindowInputIdle is wrapper for WaitForInputIdle Win32
function.
* The function waits until the application is ready to accept input
* (keyboard keys, mouse clicks). It is useful, for actions, which
take a long
* time to complete. Instead of putting sleeps of arbitrary length,
we can just
* wait until the application is ready to respond. Original function
takes
* a process handle as an input. However, in GUI tests we more often
operate
* on windows then on applications.
*
*/
DWORD WaitForWindowInputIdle( HWND hwnd, DWORD milliseconds )
{
DWORD pid = 0;
DWORD dwThreadId = GetWindowThreadProcessId( hwnd, &pid );
HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, TRUE, pid );
if( hProcess == NULL ){
TCHAR szBuf[80];
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
printf( "OpenProcess failed with error %d: %s",
dw, lpMsgBuf );
}
//printf( "Calling WaitForInputIdle for pid %ld\n", pid );
DWORD result = WaitForInputIdle( hProcess, milliseconds );
if( result == WAIT_FAILED ){
TCHAR szBuf[80];
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
printf( "WaitForInputIdle failed with error %d: %s",
dw, lpMsgBuf );
}else{
if( result == WAIT_TIMEOUT ){
// printf( "WaitForInputIdle returned after TIME OUT" );
}
if( result == 0 ){
// printf( "WaitForInputIdle returned after wait was
satisfied" );
}
}
return result;
}
DWORD
WaitForReady( hWnd, dwMilliseconds = 200000 )
HWND hWnd;
DWORD dwMilliseconds;
CODE:
RETVAL = WaitForWindowInputIdle( hWnd, dwMilliseconds );
OUTPUT:
RETVAL
I would appreciate if you include it in the next release. It should
not do any harm to existing code, from the other hand it provides some
useful functionality, which I already use, so it would be easier for
me to have the most recent version in source forge instead of local
drives on all my machines. If you have any questions, let me know.
-Piotr