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

在 .NET 裡要執行外部程式我們都是用 [System.Diagnostics.Process.Start](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.start.aspx) 方法，不過我們在執行外部程式時經常會需要設定 Timeout 逾時時間，以確保原本的主程式能夠繼續運作下去。除此之外，我今天也會介紹「同步」與「非同步」的 Timeout 撰寫技巧。

使用 [System.Diagnostics.Process.Start](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.start.aspx) 方法執行一個外部程式非常容易，最簡單的範例如下：

```
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](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.aspx) 類別還有提供 [Process.Exited 事件](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.exited.aspx)可以在「非同步」的執行環境下 ( 例如 WinForm ) 靈活運用，讓你可以在程式結束時委派一個方法(method) 執行相關程式或傳送訊息。以下是個比 MSDN 範例程式還簡單的範例，比較特別的地方是 [EnableRaisingEvents](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.enableraisingevents.aspx) 屬性必須設定為 true 才會觸發事件，此屬性預設為 false。

```csharp
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 類別](http://msdn.microsoft.com/zh-tw/library/system.threading.threadpool.aspx)進行撰寫，讓程式可以在一個獨立的 Thread 中執行，並且在該 Thread 中等待程式執行完畢。

```csharp
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](http://msdn.microsoft.com/zh-tw/library/system.diagnostics.process.aspx) 類別的說明請參考 MSDN 文件說明。
