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)中。
更多信息可参考:
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 - 字符串处理相关的一个问答
上一篇: 分治策略---求最大子数组
下一篇: python基础语法