Batch script calling robocopy in Process won't terminate

五迷三道 提交于 2021-02-17 04:41:20

问题


If process.Kill() is called from another thread or even another program, the process never comes out of WaitForExit() if the batch script used robocopy.exe until it is finished as if it wasn't killed.

Robocopy.exe is called from the batch script. Every other script or program ends as you'd expect.

    ProcessStartInfo startInfo = new ProcessStartInfo();
    startInfo.FileName = "batch.bat";
    startInfo.UseShellExecute = false;
    startInfo.CreateNoWindow = true;
    startInfo.RedirectStandardOutput = true;
    startInfo.OutputDataReceived += CaptureHandler;
    startInfo.RedirectStandardError = true;
    startInfo.ErrorDataReceived += CaptureHandler;
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.WaitForExit(); 

The batch script looks like:

@echo off
call "robocopy.exe" "somedir" "somedest" /mir /fp /ndl /njh /njs /ns

I have a feeling it has to do with the output handlers. I tried using process.CancelErrorRead and process.CancelOutputRead() as well after the Kill() call and before, no luck.

Oddly, if you use process.WaitForExit(timeout) overload, it will return true immediately after Kill() from the other thread. However, it's lying. The process is still running! If you try process.WaitForExit() again, as per the MSDN doc, it will still wait for the process to finish despite HasExited saying true.

To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter after receiving a true from this overload.

https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx


回答1:


You are successfully killing the batch processor (cmd.exe) but doing so won't kill robocopy, which is a separate process.

It doesn't seem to be documented, but when we look at the .NET source code it turns out that the Process.WaitForExit() method doesn't just wait for the process to exit, it also waits for end-of-file on the standard output and standard error streams. In this scenario, that means that it waits for robocopy to finish even after the batch processor has been killed.

(The overload of Process.WaitForExit with a timeout does not have this extra logic.)

I think this constitutes a bug in the .NET framework. At the very least, it should be documented.

As a workaround, you can use .HasExited and/or the version of WaitForExit with a timeout to determine whether the process has exited or not. Of course, in your scenario you might prefer to wait for grandchild processes, in which case your code is already behaving as desired.




回答2:


I ran into the same problem. In my case, dropping the /mt switch from the RoboCopy argument list seemed to fix the issue.




回答3:


Having followed up on Harry Johnston's helpful answer, I found that the process completes normally when you avoid RedirectStandardOutput = true. If this isn't an acceptable solution I found that using robocopy's /LOG:"C:\logs\robocopy.txt" switch to send its standard output to an external log file also works (although you lose the ability to get the file/directory log output from the process object itself).




回答4:


Looks like right now the only way to do this without the application knowing to terminate Robocopy.exe specifically is to do kill the children of the script process before killing the script itself:

Kill process tree programmatically in C#

    /// <summary>
    /// Kill a process, and all of its children, grandchildren, etc.
    /// </summary>
    /// <param name="pid">Process ID.</param>
    private static void KillProcessAndChildren(int pid)
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher
          ("Select * From Win32_Process Where ParentProcessID=" + pid);
        ManagementObjectCollection moc = searcher.Get();
        foreach (ManagementObject mo in moc)
        {
            KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
        }
        try
        {
            Process proc = Process.GetProcessById(pid);
            proc.Kill();
        }
        catch (ArgumentException)
        {
            // Process already exited.
        }
    }


来源:https://stackoverflow.com/questions/44712364/batch-script-calling-robocopy-in-process-wont-terminate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!