C# 使用 Process.Start 執行外部程式


C# 執行外部程式的方式並不難,也很簡單,首先引入
using System.Diagnostics;
然後使用 Process.Start("OtherProgram.exe")方式就可以呼叫並執行。



但是,某些情況下會導致程式無法被執行(或是無法正常執行),
很多原因在於『執行程式的起始位置』,由於被呼叫的程式執行時,
它所啟動的路徑其實是在呼叫程式的路徑下,
如果被呼叫程式需要讀取與程式相同路徑下的文件檔案(或是INI),可能會導致找不到的問題。

為了測試這樣的現象,我準備了2支程式,
一支是被呼叫端的程式 HelloConsole.exe
另外一支就是呼叫端程式 CallOtherProgram.exe

HelloConsole.exe 這支程式在啟動時會讀取相同路徑下的 Doc.txt 並將內容顯示出來,如果有參數也會把參數列出來。而這支程式我固定放在 D:\TEST\ 下,以方便測試,如果不是在這路徑下,那呼叫端的程式碼裡面的路徑也要變更。

HelloConsole.exe

using System;
using System.IO;

namespace HelloConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("This is HelloConsole.exe");
            Console.WriteLine(" ");
            Console.WriteLine("Environment.CurrentDirectory is ");
            Console.WriteLine(Environment.CurrentDirectory);
            Console.WriteLine(" ");
            Console.WriteLine("AppDomain.CurrentDomain.BaseDirectory is ");
            Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);
            Console.WriteLine(" ");

            if (args.Length > 0)
            {
                Console.WriteLine("You passed arguments is :");
                foreach(string arg in args)
                {
                    Console.WriteLine(arg);
                }
            }

            string fileName = "Doc.txt";

            if (File.Exists(fileName))
            {
                Console.WriteLine("Retrieve Doc.txt content ...");
                using (StreamReader reader = new StreamReader(fileName))
                {
                    string content = reader.ReadToEnd();
                    Console.WriteLine("===== Start of content =====");
                    Console.WriteLine(content);
                    Console.WriteLine("====== End of content ======");
                }
            }
            else
            {
                Console.WriteLine(" >> Doc.txt can not be read because it not exists !!");
            }
            Console.WriteLine(" ");
            Console.Write("Press Enter to Close Program....");
            Console.ReadLine();
        }
    }
}


另外 Doc.txt 內容:
1.This is a document file with program and in same folder
2.Use to test program can read text normal.

注意上面程式碼開啟 Doc.txt 的方式是不帶絕對路徑的方式,意思是希望以程式當下啟動路徑下直接開啟 Doc.txt。(下面會解釋呼叫會發生的問題)

我們先單獨執行 HelloConsole.exe 看看是甚麼樣子

從畫面可以發現執行後 Environment.CurrentDirectory 這樣的環境變數確實是在程式存在的地方(D:\TEST),因此讀取/開啟 Doc.txt 時可以正常讀取。


接下來我們看看呼叫端程式 CallOtherProgram.exe ,這支程式會提供 4 種呼叫方式來測試呼叫的結果

  1. 簡單呼叫方式
  2. 簡單呼叫方式(帶參數)
  3. 標準呼叫方式(提供環境設定)
  4. 標準呼叫方式(提供環境設定/帶參數)


CallOtherProgram.exe

using System;
using System.Diagnostics;
using System.IO;

namespace CallOtherProgram
{
    class Program
    {
        static void Main(string[] args)
        {
            string fileName = "D:\\TEST\\HelloConsole.exe";
            Console.WriteLine("This Program use to test call other program by different method ,");
            Console.WriteLine("It will call " + fileName + " to run and watch it execute result");
            Console.WriteLine(" ");
            Console.WriteLine("1.Default call(no change path)");
            Console.WriteLine("2.Default call with argument string");
            Console.WriteLine("3.Change execution environment path");
            Console.WriteLine("4.Change execution environment path with argument string");
            Console.Write("\r\n >> ");
            var key = Console.ReadKey(true);
            if (key.KeyChar == '1')
            {
                //第一種
                Process.Start(fileName);
            }
            else if(key.KeyChar == '2')
            {
                //第二種
                Console.WriteLine("\r\n Input string (separated by space cahracter and Enter to be end): ");
                Console.Write("\r\n >> ");
                string argStr = Console.ReadLine();
                Process.Start(fileName, argStr);
            }
            else if (key.KeyChar == '3')
            {
                //第三種
                string path = Path.GetDirectoryName(fileName);
                string file = Path.GetFileName(fileName);
                var startInfo = new ProcessStartInfo();
                startInfo.WorkingDirectory = path;
                startInfo.FileName = file;
                Process.Start(startInfo);
            }
            else if (key.KeyChar == '4')
            {
                //第四種
                Console.WriteLine("\r\n Input string (separated by space cahracter and Enter to be end): ");
                Console.Write("\r\n >> ");
                string argStr = Console.ReadLine();

                string path = Path.GetDirectoryName(fileName);
                string file = Path.GetFileName(fileName);
                var startInfo = new ProcessStartInfo();
                startInfo.WorkingDirectory = path;
                startInfo.FileName = file;
                startInfo.Arguments = argStr;
                Process.Start(startInfo);
            }
            Console.WriteLine(" ");
            Console.WriteLine("Press Enter to Close ....");
            Console.ReadLine();
        }
    }
}


※其實上面一堆 Console.WriteLine 是可以簡化啦,只是懶惰了點都是搞複製/貼上所以才這麼多.....

執行結果:
第一種,基本用法 Process.Start(fileName),不帶參數,直接呼叫執行檔。
可以看到,被呼叫的 HelloConsole.exe 無法讀到跟它放在一起的 Doc.txt,從觀察中不難發現 HelloConsole.exe 的 Environment.CurrentDirectory 卻出現了 D:\MYAPP ??

也就是說 HelloConsole.exe 的執行環境變成呼叫者 CallOtherProgram.exe 的執行環境了。

第二種,基本用法的延伸,附帶參數 Process.Start(fileName, argStr) ,看得出來狀況跟第一種相同,無法讀取 Doc.txt ,但卻可以正確接收到參數傳入。

第三種,使用 ProcessStartInfo 傳入相關的參數值給 Process.Start
Process.Start(startInfo),利用這種方式要求 Process 建立自己的環境位置,
有了 startInfo.WorkingDirectory = path 這樣的設置,可以讓 HelloConsole.exe 在指定的路徑下被執行,因此可以正確讓  HelloConsole.exe 找到 Doc.txt 並顯示。

第四種,把第三種呼叫加入傳遞參數,當然這參數不適合使用第二種方法呼叫,而是把參數設定在 startInfo.Arguments 裡面,再以 Process.Start(startInfo) 方式執行,這樣 HelloConsole.exe  可以正確讀取 Doc.txt ,並且可以接收參數。


當然,最好處理方式是在 HelloConsole.exe 裡面讀取 Doc.txt 指定位置,當然可以使用 AppDomain.CurrentDomain.BaseDirectory 來強制取得執行程式路徑,只是實際使用時,可能也有需要在呼叫端的路徑下處理檔案。

只能說,程式寫法很自由,使用上要很小心,先確認開發的程式可能的執行方式,包含被呼叫執行(像是 Windows 工作排程就是一種呼叫端程式)的狀況下,也能正確執行,才是上上策。







留言

這個網誌中的熱門文章

【研究】列印的條碼為什麼很難刷(掃描)

統一發票列印小程式