欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# 结合 Golang 开发

程序员文章站 2022-04-14 21:50:46
1. 实现方式与语法形式 基本方式:将 Go 程序编译成 DLL 供 C 调用。 1.1 Go代码 注意:代码中 export 的注释是定义的入口描述不能省略 在 LiteIDE 中将编译配置的 自定义值为 ,从而形成以下编译语句。 1.2 C 代码 2. Windows 下编译依赖的环境 生成 D ......

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)中。
更多信息可参考:

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

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