關於.Net中Process的使用方法和各類用途彙總(二):用Process啓動cmd.exe完成將cs編譯成dll

 

  上一章博客我爲你們介紹了Process類的全部基本使用方法,這一章博客我來爲你們作一個小擴展,來熟悉一下Process類的實際使用,廢話很少說咱們開始演示。c#

  先看看咱們的軟件要設計成的佈局吧。多線程

  

  首先咱們須要給定會使用到的dll,記得vs中的引用那一項嗎?咱們雖然不須要將這裏面的引用所有導入進來,可是咱們須要將咱們使用過的dll所有導入進來,否則編譯時會提示找不到類方法之類的。異步

  

  可能有些同窗不知道怎麼查看一個類或者方法所在的dll,其實只用對着那個方法或者類按下f12就能在打開的文檔的正上方看到所在的dll了,包括dll所在的路徑都寫的很是清楚。async

  

  我後續更新是想完成導入工程,讀取出來工程內的cs文件和資源,選擇須要編譯的cs文件和資源,而後點擊編譯,完成編譯,不過功能比較多,我會慢慢的更新,沒更新一個版本會在本博客的下方附上地址。佈局

  

  介紹就先到此爲止,咱們開始講解代碼的實現過程,因爲整個工程的代碼大部分和咱們須要講解的內容無關,因此我只挑出核心功能在這裏介紹一下,其餘的內容請自行下載工程查看。先貼上代碼,我感受代碼中的註釋已經寫的很是明白的,不過我仍是找些註釋解釋的不是很明白的地方詳細講解下。ui

  

      /// <summary>
        /// 異步執行編譯
        /// </summary>
        private async void Compiled()
        {
            //注意:咱們下面這幾個變量提早拿出來是由於ui都式不能跨線程訪問,異步操做也會被認爲是多線程。
            
            //dll的輸出路徑
            string working = path_textBox.Text;
            //dll的名字
            string name = name_textBox.Text;
            //vs的路徑,這裏咱們使用的是vs的一個批處理文件進行dll的編譯,它會自動幫咱們配置好環境,雖然咱們直接使用.net的csc.exe也能編譯,可是你看個人代碼裏用的c#6.0的各類新語法都是須要編譯器支持的,僅僅使用.net的csc.exe是沒法完成編譯的
            string vs_Path = vs_Path_textBox.Text + "\\Common7\\Tools\\VsMSBuildCmd.bat";

            //這個也不算新語法了,這個和上面的async是一對,你給一個方法標記上async表示這個方法是異步的,可是注意await等待的代碼之外的代碼是同步執行的,也就是說是主線程執行的,必須將須要執行的代碼放在Task.Run()中,纔會異步執行,我一開始就是有這個誤區一直疑惑異步爲啥還會卡。
            await Task.Run(() =>
            {
                //建立一個ProcessStartInfo,設置初始信息
                System.Diagnostics.ProcessStartInfo start = new System.Diagnostics.ProcessStartInfo("cmd.exe");
                //讓應用能夠接受輸入,這裏咱們用於向控制檯輸入命令
                start.RedirectStandardInput = true;
                //讓應用能夠輸出數據,這裏咱們是打算讀取在編譯完畢後讀取控制檯的輸出數據判斷是否編譯成功的,可是暫時沒法完成此功能
                start.RedirectStandardOutput = true;
                //這個屬性設置成true可讓打開的控制檯無窗口,以達到咱們想要的效果
                start.CreateNoWindow = true;
                //這個屬性也不知道幹什麼用的,可是你想對控制檯進行控制,這個屬性必須設置爲false,否則會拋出異常告訴你,必需要將此屬性設置爲false
                start.UseShellExecute = false;
                
                //終於開始正題了,就像上一章博客介紹的那樣,咱們經過ProcessStartInfo初始化一個Process對象
                System.Diagnostics.Process process = System.Diagnostics.Process.Start(start);
                
                //如下就是像控制檯輸入命令的過程了,想從控制檯讀取數據,就直接調用StandardOutput.ReadLine();就能讀取一行了,不過要注意了,你調用了此方法後,控制檯會一直等待一個輸入,因此很容易就一直卡在那裏不執行下面的代碼
                
                //GetLetter方法是我本身定義個獲取路徑中的盤符加上:用的,用此方法獲取到vs所在的盤,以後直接將命令輸給cmd,就能進入vs所在的磁盤分區了,不明白的本身去補控制檯命令
                process.StandardInput.WriteLine(GetLetter(vs_Path));
                //用cd命令進入VsMSBuildCmd.bat批處理文件所在的路徑
                process.StandardInput.WriteLine("cd " + System.IO.Path.GetDirectoryName(vs_Path));
                //獲取VsMSBuildCmd.bat文件的名字,並輸入給cmd,執行這個批處理文件的命令
                process.StandardInput.WriteLine(System.IO.Path.GetFileName(vs_Path));
                
                //批處理文件執行完畢後,咱們的控制檯環境就被搭建好了,咱們將路徑再轉向dll的輸出目錄
                process.StandardInput.WriteLine(GetLetter(working));
                process.StandardInput.WriteLine("cd " + working);

                //將會使用到的dll以","爲分割符拼接起來。這裏我說明一下會使用到的dll什麼意思,假設咱們的類調用第三方dll之類的時候,固然你調用瞭如System.Windows.MessageBox();之類的方法也須要引用對應的dll。
          //不知道用的方法或者類是在哪一個dll?對着你的方法或者類按下f12在代開的文檔的最上方寫着呢。
//StringBuilder是個高效的字符串拼接類,可是相應的功能沒有string多,在拼接完全部字符串後,直接ToString就能獲得字符串 StringBuilder dll = new StringBuilder(); foreach (string item in DllPath) { dll.Append(item); dll.Append(","); } //將須要編譯的cs文件以空格爲分隔符拼接起來,這裏用空格,dll那裏用,這是語法,別問我爲何。 StringBuilder cs = new StringBuilder(); foreach (string item in CsPath) { cs.Append(item); cs.Append(" "); } //若是沒有使用其餘dll的狀況下 if (dll.ToString() == "") { //因爲沒有使用dll的狀況。 //我一一解釋這些命令都是幹嗎用的,csc是用來編譯咱們cs文件的應用程序,/t:library表明咱們要將cs文件編譯成dll,固然也能編譯成exe之類的,以後跟上空格再加上全部須要編譯的cs文件。再以後用/out:指定輸出的名字 process.StandardInput.WriteLine($"csc /t:library {cs.ToString()} /out:{name}"); } else { //使用了其餘dll的狀況下,其餘的都跟上面相同。主要式添加了/r:,/r:後面跟上全部的dll,用","分割 process.StandardInput.WriteLine($"csc /r:{dll.ToString()} /t:library {cs.ToString()} /out:{name}"); } //全部命令都執行完畢了,接下來久給一個exit的命令退出控制檯吧 process.StandardInput.WriteLine("exit"); //等待控制檯關閉 process.WaitForExit(); //釋放Process對象的全部資源 process.Close(); //執行事件,這裏有個新語法,?.表示CompiledEndEvent不爲null的狀況下就出發CompiledEndEvent事件 CompiledEndEvent?.Invoke(); }); }

 

  

string vs_Path = vs_Path_textBox.Text + "\\Common7\\Tools\\VsMSBuildCmd.bat";

 

  咱們先說明一下VsMSBuildCmd.bat這個批處理文件吧,vs2013和vs2015我均已證明VsMSBuildCmd.bat所在路徑是vs安裝路徑下的一指定路徑下的,所以我這裏久乾脆拼接了一下字符串。若是低版本vs路徑有所不一樣各位能夠看着修改。VsMSBuildCmd.bat文件幫咱們作了一些編譯操做,不執行這個文件,而是直接執行.net目錄下的csc.exe的話,咱們是沒法編譯c#的一些新語法的。有一點各位要明白,c#版本和.net版本幾乎沒有太大關係,也不能說徹底不要緊,c#新版本的語法實際上是編譯器維護的,因此咱們僅僅用csc.exe是沒法識別那些新語法的。spa

  

  再簡單介紹一些async和await,這個是c#5.0增長的兩個關鍵字,他讓咱們編寫異步方法變得異常方法,經過上面的代碼你也能夠看到這個方法是能夠混合異步和同步操做的,咱們能夠在方法中使用await等待一個費時的操做,在這個操做執行完畢後纔會繼續執行下面的方法,且不堵塞線程。僅僅只有Task.Run();中的方法是異步執行的,這樣極大的方便了咱們不少的操做。用法我已經告訴你們了,怎麼去利用各自看着用吧。對了,別在Unity中使用,Unity中用的是c#4.0的語法和.Net2.0的版本,沒法支持這兩個關鍵字。.net

 

  其餘的沒啥好講的了,註釋寫的已經很是清楚了。我就給各位講講本代碼中使用到的c#6.0的新語法吧。線程

  首先是"$"運算符,這個運算符是爲了簡化string.Format();方法而被設計出來的,我給個$運算符和string.Format的例子一對比,你就會簡單明瞭的明吧$幹什麼用的,怎麼用了。設計

string a = "", b = "", c = "", d = "";
string s = $"{a}到底{b}{c}呀{d}";
string s2 = string.Format("{0]到底{1}{2}呀{3}", a, b, c, d);

 

  看着上面的例子,兩種拼接字符串的方式,用$運算符能夠下降咱們很多工做量,並且閱讀性也增長了。他們的輸出結果天然也是相同的。

 

  而後式?.運算符,中文名叫什麼來着忘記了,不過也不用在乎這種事。這個運算符依然是簡化咱們的工做用的,看下面的代碼,這樣很容易就能明白。

LL kk = null;

//咱們之前的寫法
if (kk != null)
    kk.S();
//而用?.運算符,這個運算符意思就是若是kk不爲null的話就執行kk.S();跟上面那兩行代碼一個功能
kk?.S();

 

  說到?.運算符了,就不得不提提??運算符,?.運算符表示不爲null時執行,??運算符則表示爲null時執行。看段代碼:

LL kk = null;

//咱們之前的寫法
if (kk == null)
    kk = new LL();
//而用?.運算符
kk = kk ?? new LL();

 

 

  好了,感謝閱讀本篇博客,但願各位也能有所收穫。Process類和ProcessStartInfo類還有不少功能,你們能夠本身去多多研究研究,有機會的話我會在後續章節中爲各位繼續分享。

 

附上整個工程源碼,工程是vs2015寫成的WPF程序,使用了大量c#6.0的新語法,低版本的打開會提示大量的錯誤,vs2013貌似能正常編譯,沒試過,再低版本的都沒法使用。不過只用將工程中的幾個不兼容的小錯誤修改一下就能正常編譯了。

若是你只是想要運行程序的話,直接在工程目錄中的bin\Release目錄下找到DLL編譯器.exe便可拷貝走使用。

工程還有不少問題,好比沒法獲取編譯結果,不會儲存最近一次的操做信息登,這些問題我都會後續修復,並在此頁面更新,每修改一版我都會寫上版本。

1.0 beta版:http://files.cnblogs.com/files/menghuijinxi/DLL%E7%BC%96%E8%AF%91%E5%99%A8.zip

 

文章原創,歡迎轉載,請標明出處。

相關文章
相關標籤/搜索