運行環境: vs2013c++
框架: .net4.5算法
c編譯器:mingw 32位shell
首先咱們下載一個c編譯工具鏈編程
http://tdm-gcc.tdragon.net/downloadc#
選擇tmd gcc 32位編譯器下載windows
配置好後咱們就可使用該編譯器對c程序進行編程併發
嘗試寫個簡單的c代碼測試一下編譯框架
 異步
保存爲test.c函數
經過工具鏈的gcc程序進行編譯
經過相似gnu gcc的方式進行編譯
能夠正確運行出結果
測試c編譯器可用的狀況下咱們嘗試使用c#進行外部調用
在原先的項目中添加ExeExecute項目
要調用外部的exe程序咱們須要引入
using System.Diagnostics;
而要使用外部exe主要是掌握Process對象的使用
Process p = new Process();
而使用Process主要分爲三個步驟,第一步是設定啓動參數,第二步是啓動exe程序,第三步是捕抓程序的輸入輸出流進行控制
而後第一步的參數設置:
肯定編譯器對象爲gcc.exe
p.StartInfo.FileName = @"C:\Users\Administrator\Desktop\gcc-5.1.0\bin\gcc.exe";
gcc程序不在相同路徑下須要使用完整路徑
設定好程序路徑咱們還須要設定工做路徑,也就是源代碼以及生成程序代碼的路徑
p.StartInfo.WorkingDirectory = @"C:\Users\Administrator\Desktop\test\";
最後要設定編譯參數
p.StartInfo.Arguments = "test.c -o test.exe -m32 -g3 -static-libgcc";
採用靜態編譯,由於部分dll並無添加到系統環境變量中
最後爲了能捕抓程序的輸入輸出流,咱們不採用外部調用系統的shell,輸入輸出流重定向到程序中
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
第二步程序運行
p.Start();
第三部捕抓輸入輸出
編譯結果輸出信息重定向到c#程序的控制檯上
Console.Write(p.StandardOutput.ReadToEnd());
編譯正常除了警告外是不會有其餘輸出信息的
Console.WriteLine(p.ExitCode);
而程序退出碼則是判斷是否成功編譯的關鍵
p.WaitForExit();
p.Close();
最後必須退出外部exe調用的程序,否則會沒法控制一直運行在後臺致使內存溢出
根據上面的步驟來綜合編寫程序
很不幸運行程序過程當中系統提示部分dll找不到,而後我嘗試經過控制檯來測試,甚至直接修改環境變量導入dll到系統中仍沒法解決該問題
目前有兩個解決方案,as.exe路徑或者在程序運行目錄下放置必要的dll文件
爲了避免破壞編譯程序的結構,現採用複製到test目錄下編譯,雖然能夠經過程序來創建臨時路徑來訪問dll但會影響必定的速度,複製到目錄中共享雖然影響必定的可移動性,但仍是能更高效的使用,爲後面併發編譯作準備
接下來作測試,先測試正確編譯的狀況
程序不提醒任何錯誤,顯示編譯成功
修改源代碼製造語法錯誤
編譯會提示出錯信息
再修改源代碼
此次是能夠編譯成功,過程當中的警告會有所顯示
能成功編譯程序後咱們須要運行程序進行輸入輸出測試,這時候修改一下原程序
先作出能夠提供輸入輸出的程序進行編譯
根據ExitCode判斷編譯成功後能夠運行程序來進行測試
 
接下來跟上面的外部調用同樣,此次主要多開啓了輸入流重定向
p.StartInfo.RedirectStandardInput = true;
經過輸入流以及讀取輸出流判斷結果是否正確
這個程序並無作泛化出來來適配各類各樣的輸入輸出狀況
接下來泛化一下,作出類庫封裝編譯與測試功能
創建編譯測試類庫
該類庫主要包含編譯與測試兩部分
而後咱們定義一些類庫的接口
構造函數有兩個
默認不帶參數的構造方法,提供默認的gcc可執行路徑,以及c文件編譯測試的工具路徑
另一個是指定參數構建,能夠方便非相對路徑下的使用
下一步是編譯方法
編譯須要c源文件代碼,返回是否成功編譯
public bool Compile(string csrccode);
方法中須要先保存爲*.c來編譯
由於考慮到後面的併發編譯,這個文件名不得與其餘的線程重複,避免出現資源佔用的錯誤以及對結果的影響
而文件是跟線程共存亡的,線程結束該文件就無用了
因此文件名依賴於線程
string testID = Thread.CurrentThread.ManagedThreadId.ToString();
經過獲取線程id做爲文件名,能夠保證不會影響其餘進程的運行,若是經過加鎖的方式來併發反而會影響效率
 
寫入文件,結合上面掌握的一些代碼,來編寫,這時候一些調試性代碼能夠關閉,不用在控制檯輸出影響效率
接下來就要運行程序來判斷輸入輸出流
先運行程序
運行等待輸入輸出操做
析構時候須要作關閉操做避免後臺運行沒有正常關閉致使內存異常
輸出,結果配對,不匹配返回false,匹配返回true
輸入部分,判斷程序是否已經退出了(退出不關閉依舊能夠正常輸入流)
結束判斷,若是程序還在運行中證實缺乏輸入或者輸出,不能徹底匹配測試,返回錯誤,關閉程序
雖然嘗試使用c++相似的輸入輸出流的重載,可是返回對象時候不能返回引用對象,要是new對象,可能我寫法上有問題致使沒法很好地重載,否則能夠連續作輸入輸出判斷,錯誤經過異常拋出的操做,如今暫時不成功
編寫完成就開始測試
引用類庫
對象new
測試一下簡單的hello world
判斷代碼,判斷輸出一次hello world!再輸入一次hello world!
結果是錯誤的由於我多輸入了一次無效的操做
註釋掉無效的操做後
運行成功
#include<stdio.h>
int main(){
int a,b;
printf("input two number:\n");
scanf("%d %d",&a,&b);
printf("res:%d",a + b);
return 0;
}
第二次我用輸出輸入輸出的方式,而後驗證的時候在讀取第一個輸入的時候沒法讀取,用read方法也好,readline也好,readtoend也好,都是不能讀取數據,只會阻塞等待,因此程序只能不斷輸入,再一次性輸出,這會致使系統只能有一次流完整讀取操做。
運行
卡在第一次讀取輸出流中
這意味着程序沒法輸入輸出按次判斷,只能完整輸入判斷輸出
修改只有一次寫入一次讀取
此時能夠看到正確的結果
因爲原先的猜測沒法成立,如今簡化代碼單純完成輸入輸出檢測不進行順序檢測
最後經過public bool RunTest(string exein,string exeout)進行判斷
一樣是剛纔那份代碼
運行一下能夠經過
接下來有一句容易致使超時的語句要進行處理
exeRun.StandardOutput.ReadToEnd();
由於readtoend可能因爲死循環,等待輸出等緣由阻塞或者卡死,不做超時處理會有大量的死掉的進程,並且還沒法給客戶端及時的信息反饋
這時候咱們就要單獨對這個語句作一個超時處理
先作一個時間處理
ManualResetEvent timeEvent = new ManualResetEvent(false);
創建委託,完成任務着set一下事件
而後主進程會異步調用改委託
proc.BeginInvoke(null, null, null);
bool flag = timeEvent.WaitOne(time, false);
而後線程在等待時間內不斷檢測是否改變timeEvent標誌
改變了的話裏面返回true
不然會超時後爲false
若是超時常規退出不是行的了
只能kill掉死掉的進程
爲了實現超時時間的可控性,爲runtest函數重載
增長一個timeout參數能夠應對算法較複雜的程序,類內默認初始值爲5000ms
而後進行測試肯定可用,準備用於下一個綜合實驗
經過該實驗我掌握了exe的外部調用,以及對輸入輸出流的讀寫控制,以及gcc編譯器工具在windows下的編譯使用的方法,以及不斷修改到最後作出可超時檢測的c編譯器調用與程序測試的程序,因爲技術知識缺乏,暫時未能解決對輸入輸出的步驟控制