簡體   English   中英

在進程中調用 robocopy 的批處理腳本不會終止

[英]Batch script calling robocopy in Process won't terminate

如果從另一個線程甚至另一個程序調用process.Kill() ,如果批處理腳本使用 robocopy.exe,則該進程永遠不會從WaitForExit()退出,直到它完成,就好像它沒有被殺死一樣。

Robocopy.exe 從批處理腳本中調用。 每個其他腳本或程序都如您所願地結束。

    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(); 

批處理腳本如下所示:

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

我有一種感覺,它與輸出處理程序有關。 我嘗試在Kill()調用之后和之前使用process.CancelErrorReadprocess.CancelOutputRead() ,但沒有運氣。

奇怪的是,如果你使用process.WaitForExit(timeout)重載,它會在來自另一個線程的Kill()之后立即返回 true。 然而,這是在撒謊。 該過程仍在運行! 如果你再次嘗試process.WaitForExit() ,根據 MSDN 文檔,它仍然會等待進程完成,盡管HasExited說 true。

為確保異步事件處理已完成,請在從該重載接收到 true 后調用不帶任何參數的 WaitForExit() 重載。

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

您正在成功終止批處理器 (cmd.exe),但這樣做不會終止 robocopy,這是一個單獨的進程。

似乎並沒有被記錄,但是,當我們看看.NET源代碼,事實證明,在Process.WaitForExit()方法不只是等待的過程中退出,同時也等待最終的文件在標准輸出和標准錯誤流上。 在這種情況下,這意味着即使批處理器被殺死,它也會等待 robocopy 完成。

(帶有超時的Process.WaitForExit的重載沒有這個額外的邏輯。)

我認為這構成了 .NET 框架中的一個錯誤。 至少,它應該被記錄在案。

作為一種解決方法,您可以使用.HasExited和/或帶有超時的WaitForExit版本來確定進程是否已退出。 當然,在您的場景中,您可能更願意等待孫子進程,在這種情況下,您的代碼已經按預期運行。

我遇到了同樣的問題。 就我而言,從 RoboCopy 參數列表中刪除/mt開關似乎可以解決該問題。

跟進Harry Johnston 的有用回答后,我發現當您避免RedirectStandardOutput = true時,該過程會正常完成。 如果這不是一個可接受的解決方案,我發現使用 robocopy 的/LOG:"C:\\logs\\robocopy.txt"開關將其標准輸出發送到外部日志文件也有效(盡管您無法獲取文件/進程對象本身的目錄日志輸出)。

看起來現在在應用程序不知道終止 Robocopy.exe 的情況下執行此操作的唯一方法是在殺死腳本本身之前先殺死腳本進程的子進程:

在 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.
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM