最近因為工作的需要,要把一支 C# 程式運行在 Linux (Ubuntu) 平臺,所以接觸了 Mono,同時因為相容性問題必須修改dll,而使用 dnSpy軟體。
Linux Mono
Mono 是 Microsoft 支持的 C# 及 CLR 開源專案,它基於 ECMA 標準實現 Microsoft’s .NET Framework 的跨平台運行。需要安裝這個開源環境,才能在 Linux 上運行 C# 程式。
安裝步驟:
$ sudo apt install gnupg ca-certificates
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
$ echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
$ sudo apt update
安裝完成後,執行 C# 執行檔的指令:
$ sudo mono MyExe.exe
警告:libpng warning
因為這支 C# 程式有 UI 介面,使用了大量圖片,在 Linux 運行時,曾遇到下面警告:
"libpng warning: iCCP: known incorrect sRGB profile"
這是因為圖片嵌入的屬性,無法通過 libpng-1.6 以上版本的 ICC profiles 檢查,這個警告可以忽略。如果要消除,則要從原圖像中去掉 ICCP chunk,另存成新圖片。參考 Stackoverflow 上的解法:
$ pngcrush -ow -rem allb -reduce xxx.png
參考來源:
https://stackoverflow.com/questions/22745076/libpng-warning-iccp-known-incorrect-srgb-profile
System.EntryPointNotFoundException
後續執行時,又遇到以下例外訊息:
"System.EntryPointNotFoundException: GetPrivateProfileString assembly"
這是因為這支程式使用 Windows kernel32.dll 的函式庫,處理 INI 文件,從而取得一些程式運行的相關設定參數。然而 Linux 不支援 Windows kernel32.dll 。這時只好改用其他檔案存取方式,或是把參數 hard coding 在程式內。
DLL 編輯:dnSpy
運行過程中,遇到一些異常,是發生在 C# 程式的 DLL 檔案內,這時可以使用免安裝的 dnSpy 軟體,連結程式執行檔 exe 與 dll,對 dll 內容進行編輯與更新。簡易步驟:
- 開啟 dnSpy,將 MyExe.exe 拖曳到左側 Assembly Explorer
- 在 Assembly Explorer 展開 MyExe.exe,找到 Main(),dnSpy 會自動連結相關的 dll
- 找到你要修改的 dll 內容位置,右鍵選擇 Edit Method,就可修改程式碼並重新編譯
- 在上方工具列 File 清單內,選取 Save Module 儲存修改後的 DLL
下面這篇網誌有清楚的教學:
https://blog.darkthread.net/blog/dnspy/
dnSpy 官方 Git:
https://github.com/dnSpy/dnSpy
Mono 對 Series port 的支持
這支程式執行時,會從 USB COM port 讀取外接 Sensor 的資料,前面在 dll 內遇到的問題,就是 Serial port 在 Windows 跟 Linux 的命名不同,例如前者是 COM1,後者是 /dev/ttyUSB1 ,因此需要做對應的修改。
此外,Mono 並不支援事件型 (Event notification or Interrupt) 的 serial port 資料接收:
Mono官方原文:
只能使用 Thread + Read 組成 polling 的方式,讀取 Serial data:
new Thread(new ThreadStart(this.ThreadProc))
{
IsBackground = true
}.Start();
public void ThreadProc()
{
...
for(;;)
{
MyserialPort.Read()
}
Thread.Sleep(100);
...
}
SerialPort.Read 官方教學: