C# 结合 Golang 开发

aside section._1OhGeD · · 1015 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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.envwin64.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();

还有一种方式:

[DllImport("kernel32")]
private static extern IntPtr LoadLibraryA([MarshalAs(UnmanagedType.LPStr)] string fileName);
-- DllImport 会先在加载的里边找名称,可以预先加载。
LoadLibraryA((Environment.Is64BitOperatingSystem ? "x64" : "x86") + "/Test.dll");

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. 相关参考


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:aside section._1OhGeD

查看原文:C# 结合 Golang 开发

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1015 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传