VB.NET啟動並監測外部程式

來源:互聯網
上載者:User
程式

 你再也不需要使用Win32應用編程介面或者VB的Shell函數來啟動外部應用程式了。因為你可以使用.NET架構中的System.Diagnostics.Process類來進行這個操作,從而進一步簡化代碼。

  雖然.NET使很多事情變得更加複雜,但是啟動外部應用程式不在此列。在傳統VB程式中,你可以使用Shell函數啟動一個應用程式。當你傳送一個資料檔案名的時候,VB在相應應用程式中開啟這個資料檔案。你可以使用一個任選的windowstyle參數控制所啟動的應用程式的視窗方式。例如,在VB6中,下面這行代碼將啟動預設的文字編輯器(通常是記事本)並開啟檔案"c:\somepath\somefile.txt":

returnID = Shell("c:\somepath\somefile.txt", vbNormalFocus)

  通過Microsoft.VisualBasic.Comaptibility網域名稱空間,在VB.NET中仍然能夠使用Shell功能,並且它已經被做了一些改動,但在.NET架構中它並不是啟動應用程式的最好的方法,因為Shell函數有一些嚴格的限制條件,其中之一就是只能非同步地啟動程式;在啟動應用程式之後,你自己的程式才繼續運行。所以你不能直接使用它來啟動一個程式,並且只能等到這個程式退出,你才能返回到你自己的程式中。為了在傳統VB中做到這點,你必須求助於Windows API,而這需要對視窗控制代碼、過程識別號、枚舉最進階視窗等有所瞭解。

  使用.NET,就能使這個操作變得很簡單。你可以使用System.Diagnostics網域名稱空間中的Process類來啟動外部程式。你可以簡單的使用共用的Process.Start方法啟動一個新的過程,把一個可執行檔名或者可執行應用程式的擴充關聯檔案名稱作為參數傳輸給它。例如,下面的代碼啟動"c:\somepath\somefile.txt"檔案。

System.Diagnostics.Process.Start ("c:\somepath\somefile.txt")

  Start方法有一個超載的版本,能返回一個Process對象,所以你可以獲得對啟動的過程的引用,並可用於多種用途:

Dim myProcess As Process = System.Diagnostics.Process.Start
("c:\somepath\somefile.txt")
MessageBox.Show(myProcess.ProcessName)

  初看起來,你看上去好象喪失了控制視窗風格的能力(還記得Shell函數的第二個參數嗎?),但是事實情況並非如此。在很多情況下,你不需要明確地設定視窗風格,因為預設情況是在一個帶有焦點的正常視窗(ProcessWindowStyle.Normal)中啟動過程。但是如果你想使用一個不同的視窗風格時,可以使用超載的Process.Start方法接收一個ProcessStartInfo對象參數而不是一個簡單的字串。為了使用它,首先要建立一個ProcessStartInfo對象,然後設定進程初置值。兩個超載方法讓你設定一個檔案名稱或者一個檔案名稱和一組命令列參數。並且ProcessStartInfo對象還有一個WindowStyle屬性,由System.Diagnostics.Process.WindowStyle枚舉的值組成。所以你可以調用Process.Start方法並傳送一個ProcessStartInfo對象來控制啟動的視窗的風格。

Dim psInfo As New _
System.Diagnostics.ProcessStartInfo _
("c:\somepath\somefile.txt")
psInfo.WindowStyle = _
System.Diagnostics.ProcessWindowStyle.Normal
Dim myProcess As Process = _
System.Diagnostics.Process.Start(psInfo)

  由於Process類有一個StartInfo屬性,它是一個ProcessStartInfo對象,所以另一種產生相同結果的方法是建立一個Process對象並設定它的StartInfo屬性。在預建立的Process對象的時候,你可以僅僅調用它的Start方法,而不需使用Process類的共用Start方法。

Dim myProcess As System.Diagnostics.Process = _
new System.Diagnostics.Process()
myProcess.StartInfo.FileName = _
"c:\somepath\somefile.txt"
myProcess.StartInfo.WindowStyle = _
System.Diagnostics.ProcessWindowStyle.Normal
myProcess.Start

 

  在設計期間設定Process參數

  .NET架構出廠時已經帶有在設計期間封裝這些代碼的Process組件。你可以在工具列的Components欄目中找到它。為了使用它,把一個Process組件拖到你的表單上,然後在屬性視窗展開StartInfo屬性,如下圖(圖1)所示設定StartInfo的值。





圖1:你可以添加一個Process組件到一個表單中,讓你在設計期間設定屬性而不是在運行期間設定屬性。

  監視啟動過程

  到目前為止,你看到的啟動過程還是使用一種非同步方式;就象傳統VB Shell函數一樣。換句話說,在啟動這個過程之後,父程式中的代碼才能繼續執行。你需要一些監視被啟動的過程的方法,並弄清楚它們什麼時候退出或者是否仍在運行。根據你的應用程式的具體情況,你可能需要使用不同的方式來處理這個問題。

   啟動過程,停止你的程式直到它退出。

   啟動過程,監視它,並只有當它結束時才做某些事情,同時讓你的程式正常地運行。

   啟動過程,給它一些輸入,讓它處理這些輸入,然後強迫它退出。

   啟動過程,並且只要啟動的過程正在運行或者運行期間沒有出現問題,就執行某些操作。如果過程退出或者停止,你需要作出某些動作。

   啟動過程,並給它一些特殊的輸入,並/或取得進一步處理產生的輸出結果。例如,你可能想啟動一個命令視窗,以編程方式在這個視窗中輸入一些內容,然後取得並處理輸出結果。


  啟動一個過程並等到它退出

  等待一個啟動的過程結束的最簡單的方法時調用Process.WaitForExit方法。這導致正在啟動的過程停止執行直到啟動過的過程退出。然而不幸的是,當你直接從一個Windows表單中使用這個方法的時候,它還能導致表單停止對系統事件的響應,比如Paint。

  所以一般來說你不會想從一個按鈕中使用WaitForExit方法來啟動一個外部程式(雖然使用WaitForExit方法非常適於從一個沒有可視使用者介面的應用程式中啟動另一個過程,例如從一個ASP.NET應用程式伺服器中調用控制台應用程式)。圖二中所示的樣本表單有一個名為"Launch and WaitForExit"的按鈕,讓你在從一個表單中使用這個方法時能看到會發生什麼情況。


圖2:樣本表單讓你測試並實驗各個的啟動過程的方法。

Private Sub btnWaitForExit_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnWaitForExit.Click

'建立一個新過程
Dim myProcess As Process = _
System.Diagnostics.Process.Start("sample.txt")

' 等待直到它退出
myProcess.WaitForExit()

' 顯示結果
MessageBox.Show("Notepad was closed at: " & _
myProcess.ExitTime & "." & _
System.Environment.NewLine & "Exit Code: " & _
myProcess.ExitCode)
myProcess.Close()
End Sub


  前面的例子說明一個有趣的情況。即使啟動的過程結束後,你仍然有存取碼中的Process對象的能力;然而這種情況下,大多數Process屬性是停用,因為過程本身不再存在。你仍然可以讀取ExitCode和ExitTime屬性,這兩個屬性分別返回整數和日期時間型的值。DOS命令設定了一個退出碼,讓你知道是否出現錯誤。.NET和其他的Windows應用程式可以通過使用main方法的傳回值設定這個值。預設情況下,這個值等於零。對於DOS命令,一個非零ExitCode值要麼表明出現一個錯誤,要麼表明命令過程被異常中止。

  啟動不可視的過程

  在一個可見的視窗中,你不必啟動一個過程;有時你僅僅想運行一個過程並取得輸出值。下面的例子把當前的目錄轉換為系統目錄,然後運行一個DOS的dir命令,這個命令帶有"* .com"參數,列出目錄中所有帶有.com副檔名的檔案。在Windows XP中,命令shell解譯器把"&&"運算子認做一個命令分隔字元,所以你可以在一行中放置多個命令。">>"運算子把輸出值重新導向到一個制定檔案中。在這種情況下,代碼把dia顯示的結果匯入Application.StartupPath屬性指定的路徑中的"dirOutput.txt"檔案。

Dim myProcess As Process = New Process()
Dim s As String
Dim outfile As String = Application.StartupPath & _
"\dirOutput.txt"
'取得系統路徑
Dim sysFolder As String = _
System.Environment.GetFolderPath _
(Environment.SpecialFolder.System)
'設定檔案名稱和命令列參數
myProcess.StartInfo.FileName = "cmd.exe"
myProcess.StartInfo.Arguments = "/C cd " & _
sysFolder & " && dir *.com >> " & Chr(34) & _
outfile & Chr(34) & " && exit"
'在一個隱藏視窗中啟動過程
myProcess.StartInfo.WindowStyle = _
ProcessWindowStyle.Hidden
myProcess.StartInfo.CreateNoWindow = True
myProcess.Start()

'如果過程在1秒中不能完成,那麼銷毀它
myProcess.WaitForExit(1000)
If Not myProcess.HasExited Then
myProcess.Kill()
End If

'顯示退出時間和退出碼
MessageBox.Show("The 'dir' command window was " & _]
"closed at: " & myProcess.ExitTime & "." & _
System.Environment.NewLine & "Exit Code: " & _
myProcess.ExitCode)
myProcess.Close()


  前面的代碼返回一個零(0)值的ExitCode。如果想看看非零值ExitCode的例子,可以在系統目錄上附加一個"X"或其他字元,這樣能使它非法。這導致出現一個錯誤,ExitCode值將不同。因為一個帶有錯誤的過程可能會一直運行下去,代碼在返回到控制啟動的程式之前,使用一個超載WaitForExit方法來等待幾毫秒時間。上面的代碼等待一秒鐘,然後調用Kill方法結束啟動的過程,強迫過程退出。請查看一下你的應用程式啟動目錄中的dirOutput.txt是否存在。

探測一個過程什麼時候退出

  在VB6中,你可以調用Win32 API的GetModuleUsage()函數來判定過程什麼時候結束。在.NET中,相應的操作是在啟動過程後不斷的迴圈,檢查Process.HasExited屬性,並且調用Application.DoEvents方法處理你的應用程式中其他的事件,直到過程結束。

Do While Not myProcess.HasExited
Application.DoEvents
Loop


  但是Process類給了你一個更簡潔的方法來判斷過程什麼時候退出--它可以產生一個Exited事件。為了使這種情況出現,你需要設定Process.EnableRaisingEvents屬性為True(預設情況下屬性值為False),並建立一個事件控制代碼。例如:

'允許過程產生事件
myProcess.EnableRaisingEvents = True

'添加一個Exited事件控制代碼
AddHandler myProcess.Exited, _
AddressOf Me.ProcessExited

'開始過程
myProcess.Start()

'事件處理常式
Friend Sub ProcessExited(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim myProcess As Process = DirectCast( _
sender, Process)
MessageBox.Show("The process exited, raising " & _
"the Exited event at: " & myProcess.ExitTime & _
"." & System.Environment.NewLine & _
"Exit Code: " & myProcess.ExitCode)
myProcess.Close()
End Sub


  使用這兩種方法潛在的問題就是如果啟動的過程掛起或者從不退出,你的應用程式就會一直停止。解決辦法就是添加一個定時器,周期性的檢查啟動的程式是否有響應。

  控制過程輸入輸出

  有時候,你可能不僅僅想使用簡單的命令列,而是想把更複雜的輸入資訊直接發送到啟動的過程中。前面例子中的把輸出匯入到檔案中的方法,並不總是最好的選擇。在許多情況下,把輸出直接導回你的應用程式可能更有效。對於使用StdIn、StdOut和StdErr的程式,比如控制台應用程式,你可以覆蓋預設方法,提供一個StreamWriter來輸入,並提供一個StreamReaders來讀取StdOut和StdErr輸出值。當你啟動過程的時候,你需要設定ProcessStartInfo對象的RedirectStandardInput、RedirectStandardOutput和RedirectStandardError屬性為True。然後,在啟動過程之後,使用Process對象的StandardInput、StandardOutput和StandardError屬性來把輸入輸出資料流分配到StreamReader和StreamWriter對象。

  警告:預設情況下,架構使用Win32 ShellExecute函數,在內部啟動過程;但是當你想再分配輸入輸出資料流的時候,你必須在啟動過程之前設定ProcessStartInfo.UseShellExecute屬性為False。注意當你那麼做的時候,你必須要麼指定到檔案的完全路徑,要麼檔案位置必須在環境路徑中。例如,下面的代碼建立一個不可見的視窗,取得系統目錄中.com檔案的目錄列表,然後在一個訊息框中顯示結果。

Dim myProcess As Process = New Process()
Dim s As String
myProcess.StartInfo.FileName = "cmd.exe"
myProcess.StartInfo.UseShellExecute = False
myProcess.StartInfo.CreateNoWindow = True
myProcess.StartInfo.RedirectStandardInput = True
myProcess.StartInfo.RedirectStandardOutput = True
myProcess.StartInfo.RedirectStandardError = True
myProcess.Start()
Dim sIn As StreamWriter = myProcess.StandardInput
sIn.AutoFlush = True

Dim sOut As StreamReader = myProcess.StandardOutput
Dim sErr As StreamReader = myProcess.StandardError
sIn.Write("dir c:\windows\system32\*.com" & _
System.Environment.NewLine)
sIn.Write("exit" & System.Environment.NewLine)
s = sOut.ReadToEnd()
If Not myProcess.HasExited Then
myProcess.Kill()
End If

MessageBox.Show("The 'dir' command window was " & _
closed at: " & myProcess.ExitTime & "." & _
System.Environment.NewLine & "Exit Code: " & _
myProcess.ExitCode)

sIn.Close()
sOut.Close()
sErr.Close()
myProcess.Close()
MessageBox.Show(s)


  對於不使用StdIn的程式,你可以使用SendKeys方法來輸入按鍵事件。例如,下面這些代碼啟動記事本並輸入一些文本。

973 448:

1 2 3
Dim myProcess As Process = New Process()
myProcess.StartInfo.FileName = "notepad"
myProcess.StartInfo.WindowStyle = _
ProcessWindowStyle.Normal
myProcess.EnableRaisingEvents = True
AddHandler myProcess.Exited, _
AddressOf Me.SendKeysTestExited
myProcess.Start()

myProcess.WaitForInputIdle(1000)
If myProcess.Responding Then
System.Windows.Forms.SendKeys.SendWait( _
"This text was entered using the " & _
"System.Windows.Forms.SendKeys method.")
Else
myProcess.Kill()
End If

  你可以使用SendKeys方法發送任何鍵入值,包括Alt、Ctrl和Shift鍵;所以,你可以使用它來儲存或載入檔案、退出或者執行其他菜單驅動的命令。然而、SendKeys方法只發送鍵入值到使用中視窗(就是有焦點的那個視窗),所以如果一個應用程式在這個過程中失去焦點,那麼可能會出現問題。



相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。