程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 【.net 深呼吸】啟動一個進程並實時獲取狀態信息

【.net 深呼吸】啟動一個進程並實時獲取狀態信息

編輯:關於.NET

地球人和火星人都知道,Process類既可以獲取正在運行的進程,也可以啟動一個新的進程。在79.77%應用場合,我們只需要讓目標進程順利啟動就完事了,至於它執行了啥,有沒有出錯,啥時候退出就不管了。

但是,在某些情況下,啟動新進程後,還希望能向目標進程傳送數據,或者實時讀取來自新進程的信息。比如,啟動一個安裝程序,安裝程序會向標准流寫入安裝進度,然後調用方可以從標准流中讀取進度,以達到實時監控安裝進度的目的。

Process類公開三個標准流屬性:

StandardInput——輸入流。類型是Writer,為啥是writer呢,因為這個標准流是相對於被啟動的進程而言的,流動方向是從調用方流向目標進程,所以是寫入數據,即將內容發送到目標進程。

StandardOutput——輸出流。即目標進程對外輸出的內容,流動方向是從目標進程流向調用方,因此,對調用者來說,是讀取,故其類型為Reader。

StandardError——和輸入流差不多,只是它專用於輸出錯誤。錯誤信息是目標進程輸出的,所以,對調用者來說還是讀取者。

綜上所述,只要啟動新進程後,從StandardOutput屬性在得到一個StreamReader對象,然後建立一個循環,不斷地從流中讀取內容,就能夠實時獲得最新狀態了。

 

其實,還有更好辦的方法,Process類有個BeginOutputReadLine方法,調用後,會自動異步讀取數據,一旦收到目標進程傳回的數據,就會引發OutputDataReceived事。所以,我們在代碼中只要處理這個事件就可以接收實時信息了。

 

咱們來做個例子吧。假設我弄一個程序,只負責在後台安裝,每處理完一個進度,就會向標准流寫入進度信息,這樣調用者就能實時監控安裝進度了。

首先完成被調用的項目,項目類型為Windows應用程序項目。

不管它,反正就是一個標准的.exe文件,這個項目我是先建個空白項目,然後手動設置的。

 

每個可執行程序都必須至少有一個Main方法。主要的處理代碼都在這裡完成,只要Main方法執行完,進程就會退出了。

        static void Main()
        {
            StreamWriter writer = null;
            Stream outStream = Console.OpenStandardOutput();
            writer = new StreamWriter(outStream);

            int n = 0;
            while (n <= 100)
            {
                Thread.Sleep(30);
                writer.WriteLine(n);
                writer.Flush();
                n++;
            }
            writer.Close();
            Environment.ExitCode = 0;
        }

這裡用到了Console類,別以為它只能耍控制台應用程序,其實Console類還包括標准輸入輸出的操作。要調用OpenStandardOutput方法獲取標准輸出流,然後就可以寫入內容了。

由於Process類的StandarOutput屬性或OutputDataReceived事件,都是用字符串來傳遞的,所以上面代碼中,咱們也用StreamWriter來寫數據。

不過要注意一定,每寫一回都要記得Flush一次,這樣寫入的內容才會讓調用方及時收到。如果不Flush的話,寫入的內容會放在緩沖區中,直接流關閉或執行Flush時才會真正發送到標准流上,所以,每寫完一次都調用一下Flush方法,確保調用方能夠實時收到信息。

最後那一行Environment.ExitCode = 0 表示進程退出時返回退出碼0,即正常退出。因為我這個Main是返回void的,所以要用Enviroment類的ExitCode來設置。當然了,你還可以把Main方法改為返回int類型的值,然後直接 return 0 就行了。

 

好,被調用進程項目完成,現在做調用者項目,它是一個WPF項目。在這個時代,寫Windows桌面應用都應優先用WPF,因為WPF是牛逼層面的東東。

XAML代碼就不貼了,直接講核心代碼。

我用了個進度條來實時顯示進度,而Process類的OutputDataReceived事件是異步引發的,要在事件處理中更新進度條,需要借助Dispatcher來代理調用。

不過,在這個例子中並不需要,因為有一個很NX的類,專門用來處理進度的,就是Progress<T>,這個類可以綁定一個回調的委托,用它來更新UI是不需要Dispatcher來調度的,只要Progress<T>實例是在UI線程上創建的即可(忘了說明這句,多謝網友在評論中補充)。

            IProgress<int> progress;

            progress = new Progress<int>(p =>
            {
                pb.Value = p;
            });

在聲明變量的時候,應用 IProcess<T> 接口來聲明,T是表示進度的類型,Progress類是顯示實現了IProgress接口的,為了能夠直接調用Report方法報告進度,應當用IProgress接口來聲明變量。

 

下面代碼啟動剛剛寫的那個進程,並監視狀態信息。

            Process p = new Process();
            p.StartInfo.FileName = "..\\..\\..\\Demo\\bin\\Debug\\Demo.exe";
            p.StartInfo.UseShellExecute = false; //必須為false
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
            p.EnableRaisingEvents = true; //必須為true

            p.OutputDataReceived += OnDataReceived;
            p.Exited += OnExited;
            p.Start();
            p.BeginOutputReadLine();

實例化Process後,要設置StartInfo相關參數,FileName是要執行進程的exe文件。

注意:

UseShellExecute必須為false,不然無法在代碼中讀標准。

RedirectStandardOutput必須為true,這樣我們才能在代碼中訪問標准流。

EnableRaisingEvents必須為true,這樣才會引發OutputDataReceived和Exited

事件,否則事件不會發生。

CreateNoWindow表示不顯示目標程序的窗口,這個你自己看著辦,這裡我不讓它顯示窗口,因為這個程序本來就沒有窗口。

一定要在Start方法之後調用BeginOutputReadLine方法,一定要在 Start 和 BeginOutputReadLine方法調用前處理OutputDataReceived事件。為啥,自己想吧,這太簡單了,不脫褲子怎麼拉屎,除非你不穿褲子。

 

在OnDataReceived方法中讀出數據,並轉化為int類型,因為剛才上面的那個項目中,是把一個int值寫入流的,所以這裡讀出來的值是可以轉換為int類型的。

        private void OnDataReceived(object sender, DataReceivedEventArgs e)
        {
            int p = Convert.ToInt32(e.Data);
            progress.Report(p);
        }

直接調用IProgress<T>的Report方法就能報告進度了。

本來,是可以調用 System.Diagnostics.Process.WaitForExit()方法來等待進程執行完的,但是,由於這個方法是同步調用的,它會讓UI線程塞車,導致UI無法即時響應,體驗不好。所以改為處理Exited事件,這個事件會在進程退出後異步調用,不會讓UI線程塞車,所以處理它較好。

 

現在,運行例子,會看以下效果。

 

示例代碼下載地址 http://files.cnblogs.com/files/tcjiaan/processInstSample.zip

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved