C# 結合 Golang 開發

1. 實現方式與語法形式

基本方式:將 Go 程序編譯成 DLL 供 C# 調用。html

1.1 Go代碼

注意:代碼中 export 的註釋是定義的入口描述不能省略git

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,從而造成如下編譯語句。github

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 環境時,會報如下錯誤:golang

"gcc": executable file not found in %PATH%c#

GCC下載:Windows 64位版本  || Windows 32位版本,也能夠從從雲盤下載
下載以後,解壓後確保 gcc 命令在搜索路徑(Path)中。
更多信息可參考:http://www.javashuo.com/article/p-yswharqc-w.htmlwindows

3. 操做系統 64 位與 32 的編譯

在 LiteIDE 中,能夠經過配置 win32.envwin64.env 來指定不一樣的 gcc 環境路徑達到生成指定系統的 DLL 文件。ui

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 的具體實現類實例來使用
具體接口與類實現代碼以下:.net

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. 相關參考

相關文章
相關標籤/搜索