0

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");
CitizenInsane
  • 4,755
  • 1
  • 25
  • 56
  • 1
    [This](https://stackoverflow.com/questions/5901679/kill-process-tree-programmatically-in-c-sharp) may help. – rahnema1 Feb 05 '21 at 17:47
  • 1
    @rahnema1 Thanks you very much for this pointer. Adapting accepted answer for enumerating .net objects in matlab, this worked like a charm – CitizenInsane Feb 05 '21 at 20:50

1 Answers1

0

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
CitizenInsane
  • 4,755
  • 1
  • 25
  • 56