Go 程序編譯成 DLL 供 C# 調用。
C# 結合 Golang 開發
1. 實現方式與語法形式
基本方式:將 Go 程序編譯成 DLL 供 C# 調用。
1.1 Go代碼
注意:代碼中 export 的注釋是定義的入口描述不能省略
package main import "C" import "fmt" func main() { fmt.Println(Test()) } var _count = 0 //Test : //export Test func Test() int { _count++ return _count }
在 LiteIDE 中將編譯配置的 BUILDARGS
自定義值為 --buildmode=c-shared -o Test.dll
,從而形成以下編譯語句。
go build --buildmode=c-shared -o Test.dll
1.2 C# 代碼
[DllImport("Test.dll", EntryPoint = "Test")] extern static int Test();
2. Windows 下編譯依賴的環境
生成 DLL 依賴於 gcc,沒有 gcc 環境時,會報以下錯誤:
"gcc": executable file not found in %PATH%
GCC下載:Windows 64位版本 || Windows 32位版本,也可以從從雲盤下載。
下載之后,解壓后確保 gcc 命令在搜索路徑(Path)中。
更多信息可參考:https://www.cnblogs.com/ghj1976/p/3540257.html
3. 操作系統 64 位與 32 的編譯
在 LiteIDE 中,可以通過配置 win32.env
和 win64.env
來指定不同的 gcc 環境路徑達到生成指定系統的 DLL 文件。
4. c# 中操作系統 64 位與 32 的適配
在 c# 中判斷操作系統是否 64 位,可以使用以下語句。
bool is64 = Environment.Is64BitOperatingSystem;
為了在不同的操作系統下,加載不同的 DLL,采取以下步驟來進行組織。
(1)將 32 位的版本命名為 Test32.dll,將 64 位的版本命名為 Test64.dll
(2)定義 ITest 接口,將 DLL 將要調用的方法定義為接口方法
(3)分別為ITest接口實現 Test32 與 Test64 類,在類中加載相應的 DLL
(4)通過判斷操作系統類型,實例化一個 ITest 的具體實現類實例來使用
具體接口與類實現代碼如下:
public interface ITest { int Test(); } public class Test32 : ITest { class TestDLL { const string DLL_NAME = "Test32.dll"; [DllImport(DLL_NAME, EntryPoint = "Test")] public extern static int Test(); } public int Test() { return TestDLL.Test(); } } public class Test64 : ITest { class TestDLL { const string DLL_NAME = "Test64.dll"; [DllImport(DLL_NAME, EntryPoint = "Test")] public extern static int Test(); } public int Test() { return TestDLL.Test(); } }
實例化與調用:
ITest test = Environment.Is64BitOperatingSystem ? (ITest)new Test64() : (ITest)new Test32(); int result = test.Test();
5. 其它一些問題
5.1 字符串轉換
- 傳入字符串,C#: byte[] -> GO: *C.char
- 接收字符串,GO: string -> C#: GoString struct
GO 定義示例
//Hello : //export Hello func Hello(name *C.char) string { return fmt.Sprintf("hello %s", C.GoString(name)) }
C# GoString struct 定義
public struct GoString { public IntPtr p; public int n; public GoString(IntPtr n1, int n2) { p = n1; n = n2; } }
C# DllImport 聲明
[DllImport(DLL_NAME, EntryPoint = "Hello", CallingConvention = CallingConvention.Cdecl)] public extern static GoString Hello(byte[] name);
C# GoString struct 轉 String
public string GoStringToCSharpString(GoString goString) { byte[] bytes = new byte[goString.n]; for (int i = 0; i < goString.n; i++) { bytes[i] = Marshal.ReadByte(goString.p, i); } string result = Encoding.UTF8.GetString(bytes); return result; }
C# 調用示例
GoString goResult = test.Hello(Encoding.UTF8.GetBytes("張三")); Debug.WriteLine(GoStringToCSharpString(goResult));
5.2 調試
CallingConvention
在聲明中加入 CallingConvention = CallingConvention.Cdecl
避免未知異常。
[DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl)]
程序崩潰甚至異常提示都沒有,可在加載 DLL 之前:
Environment.SetEnvironmentVariable("GODEBUG", "cgocheck=0");
6. 相關參考
- GO 生成 DLL,C# 調用的一個完整小示例:
https://github.com/Baozisoftware/go-dll - 字符串處理相關的一個問答
https://stackoverflow.com/questions/48208098/using-generated-golang-dll-to-return-string-or-c-char