Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

How do I cleanly kill a spawned process on Win32.

by DrWhy (Chaplain)
on Feb 26, 2009 at 02:57 UTC ( [id://746427]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings PMonks,

I need a cleaner method than Win32::Process::Kill() to terminate a (non-perl) process I have spawned out of a perl script. It's a server that I want to start up, do some tests on and then cleanly shut down. I'm doing all of the management of this process with Win32::Process. The problem with this module's Kill() method is that it ultimately maps to win32's TerminateProcess(). This is bad because this blows the process out of the water, so to speak, without letting it clean up after itself. According to all the documentation I've read this can lead to possible problems with dll's used by the process, because it doesn't let the process properly disconnect from the dll's it is using. In the case of our server it also doesn't allow for flushing of the log buffer so we miss some diagnostic information.

It looks like the solution would be use the win32 GenerateConsoleCtrlEvent() to send a ctrl-c event to the process, but there doesn't seem to be a good way within the Perl world to access this function in the context I'm dealing with. Win32::Console does seem to implement some kind of access to this function, but the documentation there seems to indicate that it can only be used to send events to yourself (Not 100% sure of this as the documentation is rather limited).

This node seems to point to a way of using GenerateConsoleCtrlEvent using Win32::API, but this seems kludgy at best.

Does anyone know of a less kludgy solution to this problem?

--DrWhy

"If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

  • Comment on How do I cleanly kill a spawned process on Win32.

Replies are listed 'Best First'.
Re: How do I cleanly kill a spawned process on Win32.
by BrowserUk (Patriarch) on Feb 26, 2009 at 08:59 UTC

    Use perl's kill with

    1. a signal value of 2/'INT'

      and perl will call GenerateConsoleCtrlEvent() with CTRL_C_EVENT.

    2. a signal value of 15/'TERM' or 21/'BREAK'

      and perl will call GenerateConsoleCtrlEvent() with CTRL_BREAK_EVENT.

    If the receiving process is another Perl script, it can trap these events using the appropriate %SIG handlers.

    If is is a non-perl program it will need to use SetConsoleCtrlHandler() to install handlers to respond to the generated events and exit cleanly.


    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.
      Ha. That's exactly what I thought I saw from poking through Perl's source code. Is this documented anywhere?

      FWIW, the server I'm testing already has a SetConsoleCtrlHandler defined, which is why I started this whole exercise in the first place.

      --DrWhy

      "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

      The program does not need to call SetConsoleCtrlHandler() if the only concern is shutdown via DllMain. The default action of GenerateConsoleCtrlEvent() is to call ExitProcess() which calls DllMain with DLL_PROCESS_DETACH, however it does not get called with DLL_THREAD_DETACH.

      The idea way would be to have a thread of the server waiting on an Event, then fire the Event when closedown is needed.
        The program does not need to call SetConsoleCtrlHandler() if the only concern is shutdown via DllMain.

        If you are going to allow the default handler to call ExitProcess(), then I see no advantage over calling TerminateProcess(). You wouldn't even be able to choose what exit value is returned.

        If the OP is hoping to be able to perform some kind of clean up and controlled shutdown, he would need to install his own handler.

        The idea way would be to have a thread of the server waiting on an Event, then fire the Event when closedown is needed.

        You'd have to expand on that a little. What would be the advantage in having a separate thread?

        SetConsoleCtrlHandler() can already handle all of these events:

        1. CTRL_C_EVENT:
        2. CTRL_CLOSE_EVENT:
        3. CTRL_BREAK_EVENT:
        4. CTRL_LOGOFF_EVENT:
        5. CTRL_SHUTDOWN_EVENT:

        Are you suggesting that the OP should spawn a separate thread that installs handlers and then goes to sleep waiting for them to occur? If so, how would you suggest that it then "interupt" the main thread when they do?

        Or are you suggesting creating some kind of user event that this extra thread would wait on? If so, how would the Perl script generate that event? And what would the extra thread do with it when it arrived?


        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.

        I agree that, all things considered, an Event will be the most appropriate way to handle this.

        In my experience, “murder is always messy.”   :-D   More to the point, “it's never exactly the same way twice.” So, it's very difficult to test.

        You might have one thread in the application that does nothing but wait for the closing whistle. It starts-up the app, waits for the whistle, quickly notifies the workers, waits briefly for them to respond, and then closes down the app as neatly as it can manage.

Re: How do I cleanly kill a spawned process on Win32.
by weismat (Friar) on Feb 26, 2009 at 07:16 UTC
      I looked at the example you mention, and a little more poking around in WMI docs on the Web. I can't tell for sure, but I strongly suspect that the Win32::OLE method degenerates to TerminateProcess(), so that wouldn't solve the problem.

      I've looked at the source code for Perl 5.8.9, and I think I found the answer to your suggestion about perl's builtin kill(). I'm far from an expert so this is only a guess. In win32.c there's a function my_kill() that seems to be the core of the implementation of perl's builtin kill() on Windows platforms. It looks like they are trying to emulate some of the more common signals from *nix land so that if you were to kill 2, $pid it would call GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid), kill 9, $pid would map to TerminateProcess(pid), (0, 1, SIGBREAK and SIGTERM are also handled specially). So this could potentially work for me, by just doing a kill 2, $pid, but I'm not sure since I'm not that familiar with Perl's guts and I haven't seen any documentation to confirm this behavior.

      --DrWhy

      "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

Re: How do I cleanly kill a spawned process on Win32.
by ikegami (Patriarch) on Feb 26, 2009 at 03:21 UTC
    Shouldn't your server run as a service? And that also provides a solution, since services can be stopped cleanly.
      The server works both ways, and we have to test it in both modes.

      --DrWhy

      "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

Re: How do I cleanly kill a spawned process on Win32.
by Anonymous Monk on Apr 20, 2009 at 02:27 UTC
    The kill method does not send signals on windows. See http://perldoc.perl.org/perlport.html. I spent a long time determining how to start a process on windows and then send a ctrl-c "signal". The secret is perl shares the console with the application that was started. Enjoy!
    #!/usr/bin/perl use strict; use warnings; use Win32; use Win32::Process; use Win32::Process 'STILL_ACTIVE'; use Win32::Process::Info qw{NT}; use Win32::Console; my $exe = $ARGV[0]; my $params = $ARGV[1]; my $appWorkingDir = $ARGV[2]; my $signalPollIntervalMillisec = $ARGV[3]; my $signalKillFile = $ARGV[4]; my $signalShutdownFile = $ARGV[5]; # Never die, only when child process terminates $SIG{INT} = 'IGNORE'; $SIG{TERM} = 'IGNORE'; my $ProcessObj; my $success = Win32::Process::Create($ProcessObj,$exe,$params,0,NORMAL +_PRIORITY_CLASS,$appWorkingDir); if ( $success ) { my $pid = $ProcessObj->GetProcessID(); while ( 'true' ) { $ProcessObj->Wait($signalPollIntervalMillisec); $ProcessObj->GetExitCode($exitcode); if ( $exitcode == STILL_ACTIVE ) { # Take action base on some flag if ( -e $signalKillFile ) { kill -9, $pid; exit -3; } elsif ( -e $signalShutdownFile ) { my $CONSOLE = Win32::Console->new(); $CONSOLE->GenerateCtrlEvent(CTRL_C_EVENT); } } else { # Process terminated on it's own exit $exitcode; } } }

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2025-03-16 11:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    When you first encountered Perl, which feature amazed you the most?










    Results (54 votes). Check out past polls.