Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
257 views
in Technique[技术] by (71.8m points)

python - How to fully stop a .bat file (and child processes) started with System.Diagnostic.Process.Start

Context

I'm using a .Net System.Diagnostic.Process object from Matlab to run, without any popping-up console window, some external processing for which I redirect, in real time, stdout/stderr to the matlab console (or any matlab progress-gui in the future).

The program to run is a .bat file, that itself runs a python script (preprocessing.py) that finally internally calls an executable (processing.exe).

So far everything works great including redirection of stdout/stderr, except that when trying to stop execution on matlab side (with ctrl+c), I can see from the task manager that the call to Process.Kill() does not stop nor the python engine (python.exe) and nor the executable call by the python script (processing.exe). My matlab script is then blocked in Process.WaitForExit() until I manually kill python.exe or processing.exe from the task manager.

Why the Kill() method on the initial System.Diagnostic.Process ran on .bat file does not kill the other child processes and makes me locked in WaitForExit ? ... I can force-kill processing.exe but it's dirty (I'm not supposed to know anything about possible child processes started).

The matlab code

Basically my matlab code looks like this:

function [] = main(guicallbacks)
%[
    % Init process
    process = System.Diagnostic.Process();
    process.StartInfo.Filename = 'Processing.bat';
    process.StartInfo.CreateNoWindow = true;
    process.UseShellExecute = false;
    process.RedirectStandardOutput = true;
    process.RedirectStandardError = true;

    % Attach to process events
    l = event.listener(process, 'OutputDataReceived', @onStdOut); l.Recursive = true;
    l = event.listener(process, 'ErrorDataReceived', @onStdErr); l.Recursive = true;

    % Start process
    Start(process);
    cuo = onCleanup(@()stopProcess(process));
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();

    % Wait for process
    canceled = false;
    while (~double(process.HasExited))
    
        % Check for cancellation (progress gui, etc...)
        canceled = guicallbacks.IsCancelPending();
        if (canceled), break; end

        % Wait
        pause(1.0);

    end

    % Check stop reason
    if (canceled), error('System:OperationCanceledException', 'User cancelation'); end
    if (double(process.ExitCode) ~= 0), error('Process failed (ExitCode = %i)', double(process.ExitCode)); end
%]
end
function [] = onStopProcess(process)
%[
    if (double(process.HasExited())), return; end

    process.Kill();
    process.WaitForExit();
%]
end
function [] = onStdOut(varargin)
%[
    cprintf('blue', char(varargin{2}.Data));
%]
end
function [] = onStdErr(varargin)
%[
    cprintf('red', char(varargin{2}.Data));
%]

The .bat file

Basically, execept for setting a few environment variables the .bat file to call looks like this:

python.exe -u preprocessing.py

The python script

The python script call final processing.exe executable this way:

subprocess.call("processsing.exe");
question from:https://stackoverflow.com/questions/66067979/how-to-fully-stop-a-bat-file-and-child-processes-started-with-system-diagnost

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here below is adaptation in matlab for the solution pointed out by @rahnema1 to fully kill the process and its children (recursively):

function [] = onStopProcess(process)
%[
    if (double(process.HasExited())), return; end

    killProcessTree(process.Id);
    process.WaitForExit();
%]
end

function [] = killProcessTree(processId)
%[
    % https://stackoverflow.com/a/10402906/684399
    NET.addAssembly('System.Management');
    mos = System.Management.ManagementObjectSearcher(sprintf('Select * From Win32_Process Where ParentProcessID=%i', processId));
    mos = mos.Get();
    mos = mos.GetEnumerator();
    while(mos.MoveNext())
        pd = mos.Current.Properties.Item('ProcessID');
        killProcessTree(double(pd.Value));
    end
    try
        endOfLeafProcess = System.Diagnostics.Process.GetProcessById(processId);
        endOfLeafProcess.Kill();
    catch
    end    
%]
end

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...