ozboomer has asked for the wisdom of the Perl Monks concerning the following question:

First-up, I'm running ActiveState perl v5.8.9 under Windows XP SP3 (32-bit) on a fairly reasonable Inter Core2 Duo PC @ 3GHz and with 4GB RAM...

I have an application where a number of instances of an .exe run and they each have different title text on their windows.

I'm trying to work a way that I can capture the window title for each of the instances and if one of the items is missing, I can do something to restart it, viz:

list of jobs = (123, 456, 789) for each running job process if process window title matches one of job list note job number is running go to next process endif endfor for each required job if not flagged as running create the job endif endfor

Now, I can use something like Win32::Process::Info to get some basic info... but not the window title (although, that module seems to be rather unreliable anyway). If I use Win32::API, I can get into GetConsoleTitle() in kernel32.dll and retrieve the window title for my own cmd.exe process... but how do I get the window title for another process?

I can do things a very klunky way and shell out a 'tasklist' command and then parse the results, viz:

c:\>cmd.exe /c "tasklist /v"

Image Name     PID Session Name ...   CPU Time Window Title                                                            
=========== ====== ============ ... ========== ==============...
Socks.exe    10968 Console             0:00:01 2442 - Socks                                                            
Socks.exe     1240 Console             0:00:00 2745 - Socks                                                            
Socks.exe    20720 Console             0:00:01 4085 - Socks                                                            
Socks.exe     6188 Console             0:00:00 2423 - Socks

...but I would think there'd have to be a 'native perl' -way to do it?

I've scoured through CPAN and had a look through the Monks' site but have had no joy.

I would appreciate any thoughts on a 'more elegant' means to approach the problem.

Thanks.

Replies are listed 'Best First'.
Re: How to Retrieve another Process' Window Title?
by Anonymous Monk on Feb 07, 2011 at 01:12 UTC

      I'll admit I haven't used the Win32::Process::Info before today but consider the following code:

      use Win32::Process::Info my $st = Win32::Process::Info->import('NT', 'WMI'); my $pi = Win32::Process::Info->new (undef, 'WMI'); my @pids = $pi->ListPids (); my @info = $pi->GetProcInfo (); for my $prc (@info){ printf("%s, %s, %s\n", $prc->{"ProcessId"}, $prc->{"Name"}, $prc->{"Description"} ); }

      When run, this code will make the cmd.exe session variously: a) run without error; b) fail with a "...has encountered a problem and needs to close..." dialog being shown; c) fail with a "The memory could not be 'read'." dialog being shown; d) hang until I terminate the perl.exe image manually with Task Manager. This appears, at least, not very robust to me.

      Re: a 'native' way.. I tend to think if there's some CLI utility that produces some sort of useful output, there has to be a way that Perl can do the same (providing someone has built-up a module)... or at least if it's something that's managed/controlled by Windows, I should be able to call Windows to do my job for me, rather than starting from scratch; I just have this thing that I don't like to 'shell' to the OS -- it just seems to be like "giving-up" or something...(!)

      ...and I guess my queries in the Supersearch were lacking, 'coz somehow I didn't catch those Monks' site nodes, thanks.

      I'll go and see if I can decipher the intricacies of Win32::GuiTest and Win32::API::Callback...

        You say:

        I just have this thing that I don't like to 'shell' to the OS -- it just seems to be like "giving-up" or something...(!)

        After just having said:

        I should be able to call Windows to do my job for me,

        Run this:

        tasklist /nh /v | perl -nle"@a=unpack'A29A5A123A*',$_; print join'|',@ +a[1,3]"

        That's Windows "doing your job for you".


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        This appears, at least, not very robust to me.

        What versions of each are you running? (perl , os, modules/Devel::Modlist)

Re: How to Retrieve another Process' Window Title?
by BrowserUk (Patriarch) on Feb 07, 2011 at 02:40 UTC
    ...but I would think there'd have to be a 'native perl' -way to do it?

    Why? Why should Perl--a console-based scripting language born of Unix--have a "native way" of accessing the title texts of Windows windows?

    As a mechanism for checking whether a particular instance of a process is running, this seems to be the dumbest mechanism possible.

    If these are processes you started--they're none of your business otherwise right?--then, why not just retain the pids of the processes you started and periodically check whether they are still running?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How to Retrieve another Process' Window Title?
by ozboomer (Friar) on Feb 10, 2011 at 22:08 UTC

    Just for completeness, I thought I'd drop through a note to say that the job monitoring is working well at present... *fingers' crossed*...

    Here's the segment of code where all the magic happens:-

    use Win32::GuiTest qw(FindWindowLike GetWindowText); [...] sub Check_Job { my ($site_num) = @_; my ($buf, $num_windows); my (@windows); $buf = sprintf("^%s - Socks", $site_num); @windows = FindWindowLike(0, $buf); $num_windows = @windows; if ($num_windows == 0) { # NO WINDOWS! Need to resubmit $st = 0; } elsif ($num_windows != 1) { # MULTI-WINDOWS! Oops!! printf("%s: WARNING: There are %d Socks windows for site %d!!!\n +", $my_proc, $num_windows, $site_num); $st = 2; } else { # ONE-ONLY WINDOW - GOOD! $st = 1; } return($st); } # end Check_Job

    Many thanks, once again, for everyone's help.

      #!/usr/bin/perl -- use strict; use warnings; use Win32::Process qw/ NORMAL_PRIORITY_CLASS CREATE_NEW_CONSOLE /; use Win32::GuiTest qw/ IsWindowVisible IsWindowEnabled FindWindowLike GetWindowText GetClassName GetChildDepth GetDesktopWindow WaitWindow /; use Win32::API; $Win32::GuiTest::debug = @ARGV; exit Fafafa(); BEGIN { Win32::API::->Import("user32","DWORD GetWindowThreadProcessId( HWN +D hWnd, LPDWORD lpdwProcessId)") or die $^E; sub TidPid { my $pid = pack 'L!',0 ; my $tid = GetWindowThreadProcessId($_[0], $pid); $pid = unpack 'L!', $pid; return $tid, $pid ; } sub Tid { ( TidPid(@_))[0] } sub Pid { ( TidPid(@_))[1] } } sub ErrorReport { print Win32::FormatMessage( Win32::GetLastError() ); } sub Fafafa { my %cmd; for( 1 .. 3 ){ Win32::Process::Create( my $cmdp, "$ENV{WINDIR}\\system32\\cmd.exe", "/K title $_ - Socks", 0,# don't inherit nothing NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, '.' ) or die ErrorReport(); $cmd{ $cmdp->GetProcessID } = $cmdp; } print "pid $$\n"; print "sleep 1, so the GUIs come up\n"; for(1..3){ WaitWindow("$_ - Socks"); } # too fast, so sleep after +wards sleep 1; #~ for( FindWindowLike(undef,' - Socks','ConsoleWindowClass') ){ for (FindWindowLike((undef) x 4, 1)) { next unless IsWindowEnabled($_) or IsWindowVisible($_); next unless $cmd{ Pid( $_ ) }; # the parent is us printf "0x%08X (%8d) %8d/%8d %-30s '%s'\n", $_, $_, TidPid($_), GetClassName($_), GetWindowText($_) ; } for ( values %cmd ){ printf "Killing %d\n", $_->GetProcessID(); $_->Kill(0); } 0; } __END__ pid 1204 sleep 1, so the GUIs come up 0x002403D0 ( 2360272) 1888/ 1684 ConsoleWindowClass + '3 - Socks' 0x002003B8 ( 2098104) 1496/ 1992 ConsoleWindowClass + '2 - Socks' 0x002B0384 ( 2818948) 1412/ 1692 ConsoleWindowClass + '1 - Socks' Killing 1684 Killing 1692 Killing 1992