Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

various questions about Win32::Process

by nabbo (Novice)
on Jan 10, 2014 at 18:02 UTC ( #1070161=perlquestion: print w/ replies, xml ) Need Help??
nabbo has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

Like many others here, I'm trying to use Win32::Proc.

Here are my needs :

  • 1 - launch a process
  • 2 - get its PID
  • 3 - capture its std_out and std_err
  • 4 - watch it periodically, and react if the process is too long (the length is variable ; the reaction is not always to kill, but sometimes only warn, and let it run)
  • 5 - get its return code once done
  • 6 - being able to tell if the process has terminated by itself or was killed

There are various ways to launch a process and "monitor" it.

  • - I don't use fork and exec because it doesn't create a process, but a thread in the same perl process
  • - I don't use system because I want to do things while the process is running (basically, warn or kill if it's too long)
  • - I don't use Proc::Background because it apparently can't get std_out and std_err
  • - so for the moment, Win32::Process is what is closest to what I'm looking for :

Here is what I have so far :

#!/usr/bin/perl use warnings; use strict; use Win32::Process; use Win32::Process 'STILL_ACTIVE'; my $child_pid; my $child_proc; my $cmd = 'sleep.exe 20'; my $debug = 1; my $exitcode = 1; my $stdout = 'out.txt'; my $stderr = 'err.txt'; Win32::Process::Create($child_proc, $ENV{COMSPEC}, "/c $cmd > $stdout +2>$stderr", 0, 0, ".") || print Win32::FormatMessage( Win32::GetLastE +rror()); $child_pid = $child_proc->GetProcessID(); my ($min_time,$max_time,$kill_time) = (12,15,18); if($child_pid!=0) { print "Started child process id $child_pid\n"; my $i=0; while ( 'true' ) { $child_proc->Wait(1000); $i++; $child_proc->GetExitCode($exitcode); if ( $exitcode == STILL_ACTIVE ) { print "loop $i\n"; if($i==$max_time) { print "max time : this is quite long...\n"; } if($i==$kill_time) { print "kill time : kill $child_pid\n"; $child_proc->Kill(-1); } } else { print "### E = $^E ?=$? ###\n"; print " Process terminated on it's own rc=$exitcode \n"; if($i==$min_time) { print "min time : command was too short\n"; } exit $exitcode; } } } else { print "Can't start process...\n"; }

What is working :

  • 1 - process is launched
  • 3 - I can get std_out and std_err thanks to cmd /c but this prevents me from being able to get the real pid of my process being launched (sleep.exe in the example), and I can't kill it either ($child_proc->Kill(-1) will kill the cmd process, not the sleep.exe process)
  • 4 - I can watch if it's too long or too short, but I can't kill
  • 5 - I get the RC

What is not working :

  • 2 - I get the PID of cmd.exe, not sleep.exe. If I remove cmd, I can't get the std_out and std_err in a file. I have been trying to redirect the STDOUT and STDERR in perl, but any print in my script will be mixed with my programs output, which is not what I want.
  • 6 - I don't manage to tell whether the command ended by itself or was killed. Under unix, I can use fork() waitpid() and look into the $^E and $? variables. but here it's not working.

I have seen many threads about redirecting STDOUT and STDERR for the Win32::Process module, but never complete answer about the drawbacks (ability to kill and get real PID)

I'm using 32bits strawberry perl on a 64bits win2k8R2 box. My code needs to work on windows 2003/2008/2012. I can install some CPAN package by hand (the server is not connected to internet, whereas my workstation is)

I have tried several modules like Proc::Background Proc::Simple Proc::Reliable, with no luck.

Any help much appreciated. If some module can help, please tell me

Thank you very much for reading, please forgive my poor english.

Happy new year to you, perl Monks !

Comment on various questions about Win32::Process
Download Code
Re: various questions about Win32::Process ( system_detached Capture::Tiny )
by Anonymous Monk on Jan 10, 2014 at 18:38 UTC

    Try system_detached

    that is launch a detached process in the background with a new shell, have that process be a perl program that spawn whatever you're really spawning , capture STDIN/STDERR/... IPC::Run3/Capture:: Tiny... save wherever you want to save, and exit with the true programs exit value

    if it takes too long kill it

    Then get data from cache or tempfiles, perform any cleanup ... whatever

      I'm not very comfortable having a perl process spawning another perl process launching a command.
      Moreover, it does not solve all my problems (I'm not even sure it does solve any ?)

        Moreover, it does not solve all my problems (I'm not even sure it does solve any ?)

        So you tried it?

Re: various questions about Win32::Process
by Anonymous Monk on Jan 10, 2014 at 20:51 UTC
    A better overall strategy might be to arrange for the worker to set a timer ... and, if that timer goes off, to kill itself. In the world of computers, suicide often works cleaner than assassination. . . .
Re: various questions about Win32::Process
by nikosv (Hermit) on Jan 10, 2014 at 22:10 UTC
Re: various questions about Win32::Process
by BrowserUk (Pope) on Jan 10, 2014 at 22:11 UTC

    You could use Win32::Socketpair::winopen2_5()

    #! perl -slw use strict; use Win32::Socketpair qw[ winopen2_5 ]; ( $pid, $sock ) = winopen2_5( 'perl.exe', q[-E"say 'hello'; warn; die; +"] ); shutdown $sock, 1; print while <$sock>; close $sock; __END__ C:\test>winopen2_5 Warning: something's wrong at -e line 1. Died at -e line 1. hello

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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: various questions about Win32::Process
by eyepopslikeamosquito (Canon) on Jan 11, 2014 at 09:57 UTC

    Take a look at Win32::Job, a thin wrapper around Win32 Job objects. In the Win32::Job spawn() method, you can control stdin/stdout/stderr, while its run() method provides a simple way to run programs with a time limit. Another useful feature is that killing a job kills that process and all sub-processes spawned by it (similar to Unix process groups).

    Sample code can be found at Re: Timing Windows commands.

      This was the only module I had in mind, and did not take time to test it out before posting.

      The module is great, and solves easily most of my problems.

      Here is the code I wrote :

      use warnings; use strict; use Data::Dumper; use Win32::Job; my $cmd = "sleep 20"; my ($min_time,$max_time,$kill_time) = (12,15,30); my $i=1; my $max_sent = 0; my $job = Win32::Job->new; my $pid = $job->spawn(undef, $cmd, { stdin => 'NUL', stdout => 'stdou +t.log', stderr => 'stdout.log'}); print "PID = $pid\n"; my $ok = $job->watch("handler", 1); if($ok) { print "Done\n" ; } else { print "Problem !\n"; } my $status = $job->status; print Dumper($status); if($i<=$min_time) { print "MIN_TIME Too short...\n"; } sub handler { print "$pid still alive after $i sec\n"; $i++; if($i>$max_time and $max_sent==0) { print "MAX_TIME Too long...\n"; $max_sent = 1; } if($i>$kill_time) { print "KILL TIME : kill...\n"; return 1; } return 0; }

      Now... same issue as before. I don't manage to distinguish the fact that the process launched was killed externally, or finished with exit-code 1. Any idea about that ?

      Also, what if my perl process is killed (by the task manager for example) ? I tried :

      $SIG{INT} = sub { die "Caught a INT $!" }; $SIG{ABRT} = sub { die "Caught a SIGABRT $!" }; $SIG{FPE} = sub { die "Caught a SIGFPE $!" }; $SIG{ILL} = sub { die "Caught a SIGILL $!" }; $SIG{INT} = sub { die "Caught a SIGINT $!" }; $SIG{SEGV} = sub { die "Caught a SIGSEGV $!" }; $SIG{TERM} = sub { die "Caught a SIGTERM $!" };

      But none is actually called. Any idea ?

Re: various questions about Win32::Process
by bulk88 (Priest) on Jan 12, 2014 at 04:18 UTC
    Your problem is Win32::Process doesn't fully expose the C function CreateProcess, where you can specify the initial (since the child can switch them out to whatever it wants programatically if it wants) standard handles the child process will get. I modified your script to get cmd.exe's child's PID and changed away from sleep.exe to perl.exe.
    use warnings; use strict; use Win32::Process; use Win32::Process 'STILL_ACTIVE'; use Win32::Process::Info qw{WMI}; my $child_pid; my $real_child_pid; my $child_proc; my $cmd = 'perl -e"sleep 20"'; my $debug = 1; my $exitcode = 1; my $stdout = 'out.txt'; my $stderr = 'err.txt'; Win32::Process::Create($child_proc, $ENV{COMSPEC}, "/c $cmd > $stdout +2>$stderr", 0, 0, ".") || print Win32::FormatMessage( Win32::GetLastE +rror()); $child_pid = $child_proc->GetProcessID(); my $pi = Win32::Process::Info->new(); my ($min_time,$max_time,$kill_time) = (12,15,18); if($child_pid!=0) { print "Started child process id $child_pid\n"; my %subs = $pi->Subprocesses($child_pid); my $real_child_pid = $subs{$child_pid}[0]; print "Real child process id $real_child_pid\n"; #use Data::Dumper; #print Dumper(\%subs); my $i=0; while ( 'true' ) { $child_proc->Wait(1000); $i++; $child_proc->GetExitCode($exitcode); if ( $exitcode == STILL_ACTIVE ) { print "loop $i\n"; if($i==$max_time) { print "max time : this is quite long...\n"; } if($i==$kill_time) { print "kill time : kill $child_pid\n"; $child_proc->Kill(-1); } } else { print "### E = $^E ?=$? ###\n"; print " Process terminated on it's own rc=$exitcode \n"; if($i==$min_time) { print "min time : command was too short\n"; } exit $exitcode; } } } else { print "Can't start process...\n"; }
    What you want to use is IPC::Run3 or a Perl wrapper around CreateProcess that exposes http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331%28v=vs.85%29.aspx, specifically "HANDLE hStdOutput" (and according to grep.cpan.me, there is only 1, and its a personal module intended to never be used by the public (https://metacpan.org/release/DAVEROTH/Win32_AdminMisc_Source_980511), choice 3 is using Win32::API and calling CreateProcess yourself with that struct.

      Thanks for the tip. I have tried this version, and indeed, I can recover the real PID of my process and kill it with

      Win32::Process::KillProcess($real_child_pid, "-1");

      (Note that for some reason, the standard

      kill -9 , $real_child_pid

      is not working. But I don't care much as long as I can kill the process, one way or another

      Any idea about knowing if the process was killed or terminated on its own ?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1070161]
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (10)
As of 2014-09-22 12:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (191 votes), past polls