#!perl -w
# This was ripped off from Win32::FindWindow & modified
# to suit my own purpose - it was going to be like so:
#
# Given a process ID and optionally a window class,
# return the windows matching the request with classnames.
# Then we can monitor for unwanted dialogs in crusty
# applications that we automated the starting of with
# CreateProcess() and stored the PID. Also we can use OLE
# to monitor for evil text in places where the original
# developer should have used logging(!).
#
# However, due to some issues on Windows 2003 with Callback
# (which is probably environmental - no issues on XP),
# I've opted to use a non-callback funtion to achive the
# same result: GetWindow(xx,GW_HWNDNEXT) and
# GetWindow(xx,GW_CHILD)
#
# The problem I had was running this code on Windows
# 2003 SP2 machines. It was throwing access violation
# DrWatsons when the Perl was based anywhere except
# C:\Perl. I was aiming for D:\sausage\perl.
# I also tried it on a Windows 2003 SP2 machine
# I have at home, to avoid the issue being a "customised
# build" problem in the workplace - same result. I tried
# the latest versions of ActiveState 5.8 and 5.10. Same
# again. As mentioned before though, no problem with re-
# basing Perl on my XP SP3 machine. Although that only has
# a C: drive, I was able to simply rename the Perl
# directory and run successfully.
#
# Anyway, here's as far as I got down the Callback route:
#
# Test script contributed by Jim Shaw.
use strict;
use warnings;
use Test::More;
plan tests => 6;
use Win32::API;
use Win32::API::Callback;
#$Win32::API::DEBUG=1;
use Data::Dumper;
use_ok('Win32::API');
use_ok('Win32::API::Callback');
use_ok('Win32::API::Test');
ok(1, 'loaded');
Win32::API->Import("user32", "GetWindowThreadProcessId", "NP", "N");
Win32::API->Import("user32", "GetClassName", "NPI", "I");
Win32::API->Import("user32", "GetWindowTextLength", "N", "I");
Win32::API->Import("user32", "GetWindowText", "NPI", "I");
Win32::API->Import("user32", "GetDesktopWindow", "", "N");
Win32::API->Import("user32", "EnumChildWindows", "NKP", "I");
my %_window_pids;
my $max_str = 1024;
my $pass_pid = 1;
my $pass_hwnd = 1;
my $enumended = 0;
#keeps cpu usage/time reasonable during nmake test
my $runlimit = 100;
my $runcount = 0;
#change to 1 to enable printing to console
my $print = 0;
my $window_enumerator = sub {
$runcount++;
if($runcount > $runlimit){
$enumended = 1; #set flag
return 0; #per EnumChildProc callback function docs, 0 stops the enum
}
die "0 returned but enumeration didn't stop" if $enumended;
my ($hwnd) = @_;
$pass_hwnd = $pass_hwnd && $hwnd;
# Get process ID associated with hwnd
my $pid_raw_value = "\x00" x length(pack('L',0));
if(!GetWindowThreadProcessId($hwnd, $pid_raw_value)){
die "GetWindowThreadProcessId failed, GLR=".Win32::GetLastError()."\n";
}
#to original author/Jim Shaw,you used undocumented api,and I broke it~bulk88
my $window_pid = unpack('L', $pid_raw_value);
$pass_pid = $pass_pid && $window_pid;
print "window_enumerator - hwnd=[$hwnd], PID=[$window_pid]\n" if $print;
if ($window_pid) {
my $class_size = Win32::API::Type->sizeof("CHAR*") * $max_str;
my $window_class = "\x0" x $class_size;
GetClassName($hwnd, $window_class, $class_size);
$window_class =~ s/\0//g;
$_window_pids{$window_pid}{$hwnd}{window_class} = $window_class;
my $text_size = GetWindowTextLength($hwnd);
if (Win32::API::IsUnicode()) {
$text_size = $text_size * 2;
}
$text_size++;
my $window_text = "\x0" x $text_size;
GetWindowText($hwnd, $window_text, $text_size);
$window_text =~ s/\0//g;
$_window_pids{$window_pid}{$hwnd}{window_text} = $window_text;
}
return 1;
};
my $callback_routine = Win32::API::Callback->new($window_enumerator, "NN", "I");
sub get_window_pids {
my ($callback) = @_;
my $hwnd = GetDesktopWindow();
print "get_window_pids: Desktop hwnd: $hwnd\n";
EnumChildWindows($hwnd, $callback, 0);
}
get_window_pids($callback_routine);
print Dumper(\%_window_pids) if $print;
ok($pass_pid, "no 0 PIDs found");
ok($pass_hwnd, "no 0 HWNDs found");
#
# End of tests