2015年4月21日 星期二

[C#]老師,外部執行檔不理我

這篇文章的出現是有歷史共業在的
隨著時光飛逝科技進步
一間公司經營久了系統多了人也老了
便開始出現了所謂的"舊系統"跟"新系統"
舊系統只要還堪用也舉不出什麼致命的缺陷
在人力資源不足的狀況下往往就會讓他繼續活下去
而新系統開發時在資源再利用的思維模式作祟下
就會有引用舊系統功能的需求出現
"寫成一個service大家用阿"
傻瓜,連dll都拿不到了還指望service
最後各退一步就是呼叫你的執行檔給你參數你output資料給我

賀,前言說了這麼多開始進入正題
協調好資料交換的格式之後,得到了一個精美的exe執行檔
裡面做什麼事你完全不知道,他會給你什麼結果你也無法預期
會跳error已經是很好的狀況了
最糟的是他什麼都不給你就一直hang住

同理心,執行檔會hang住肯定是出現了什麼他也沒預料到的特殊狀況才這樣
天助自助者,我們自己給的timeout設定不就得了
一般來說C#呼叫執行檔取output你會這麼寫
            string ret = string.Empty;
            using (Process proc = new Process())
            {
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.RedirectStandardOutput = true;
                //塞入你的參數
                proc.StartInfo.Arguments = parameter;
                //你的執行檔
                proc.StartInfo.FileName = exeFile;
                proc.Start();
                ret = proc.StandardOutput.ReadToEnd();
                proc.WaitForExit();
            }
在各種無法預期的不可抗力下
你很有機會在ReadToEnd()那邊等上一輩子

設定timeout的寫法

            string ret = string.Empty;
            using (Process proc = new Process())
            {
                proc.StartInfo.UseShellExecute = false;
                proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.Arguments = parameter;
                proc.StartInfo.FileName = exeFile;

                StringBuilder output = new StringBuilder();

                using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
                {
                    proc.OutputDataReceived += (sender, e) =>
                    {
                        if (e.Data == null)
                        {
                            outputWaitHandle.Set();
                        }
                        else
                        {
                            output.AppendLine(e.Data);
                        }
                    };

                    proc.Start();
                    proc.BeginOutputReadLine();






                    //人生苦短不該花費在等待上,設定一個timeout的秒數吧
                    int waitTimeSecond = 20;

                    if (proc.WaitForExit(waitTimeSecond * 1000) &&
                        outputWaitHandle.WaitOne(waitTimeSecond * 1000)
                    {
                        ret = output.ToString();
                    }
                    // Timed out.
                    else
                    {
                        ret = "老師,外部執行檔不理我";
                    }
                }
            }