The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

.NET 執行外部程式如何設定 Timeout 時間 (同步/非同步)

在 .NET 裡要執行外部程式我們都是用 System.Diagnostics.Process.Start 方法,不過我們在執行外部程式時經常會需要設定 Timeout 逾時時間,以確保原本的主程式能夠繼續運作下去。除此之外,我今天也會介紹「同步」與「非同步」的 Timeout 撰寫技巧。

使用 System.Diagnostics.Process.Start 方法執行一個外部程式非常容易,最簡單的範例如下:

Process.Start("calc");
以上這行程式是執行下去後就不管了,因為 .NET 預設是以「非同步」的方式執行外部程式,所以這行程式執行完後不會等待外部程式執行完畢,原本的主程式會繼續往下執行。如果你要等待外部程式執行完畢再繼續的話,可以用以下技巧:
Process p = Process.Start("calc");

// 無窮無盡的等待 calc (小算盤) 執行結束會才會���續執行
p.WaitForExit();

如果你需要限制外部程式最多執行 60 秒的話,可以參考以下程式。但請注意最後一行的 p.Kill(); 程式,如果沒有執行這行程式的話,原本被啟動的程式還是會繼續執行喔。

Process p = Process.Start("calc");

// 等待 calc (小算盤) 執行,若執行超過 60 秒會回傳 False
if (!p.WaitForExit(1000 * 60))
{
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine("程式執行超過 60 秒,請按任意鍵強制結束執行中的程式...");
    Console.ReadKey();

    // 將程式強制關閉
    p.Kill();
}

以上提到的都是屬於「同步式」程式執行等待與結束的方式,Process 類別還有提供 Process.Exited 事件可以在「非同步」的執行環境下 ( 例如 WinForm ) 靈活運用,讓你可以在程式結束時委派一個方法(method) 執行相關程式或傳送訊息。以下是個比 MSDN 範例程式還簡單的範例,比較特別的地方是 EnableRaisingEvents 屬性必須設定為 true 才會觸發事件,此屬性預設為 false。

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        // 用來判斷程式是否結束
        static bool m_IsTerminated = false;

        static void Main(string[] args)
        {
            Process p = Process.Start("calc");

            p.EnableRaisingEvents = true;
            p.Exited += delegate { m_IsTerminated = true; };

            while (!m_IsTerminated)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write(".");

                Thread.Sleep(1000);
            }

            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine("程式已結束");
        }
    }
}

最後,如果你想要用「非同步」的方式等待指定的時間讓程式超過執行時間自動關閉的話,可以利用之前提過的 ThreadPool 類別進行撰寫,讓程式可以在一個獨立的 Thread 中執行,並且在該 Thread 中等待程式執行完畢。

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        // 用來判斷程式是否結束
        static bool m_IsTerminated = false;

        static void Main(string[] args)
        {
            // 透過 ThreadPool 讓程式獨立的 Thread 中執行
            ThreadPool.QueueUserWorkItem(delegate
            {
                Process p = Process.Start("calc");

                if (!p.WaitForExit(1000 * 60))
                {
                    p.Kill();
                    m_IsTerminated = true;
                }
            });

            while (!m_IsTerminated)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write(".");

                Thread.Sleep(1000);
            }

            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine("程式已結束");
        }
    }
}

其他有關 Process 類別的說明請參考 MSDN 文件說明。