在Windows环境下,我们可以将Go语言编译为DLL并供.Net等其他语言的代码调用,需要用到Mingw64的gcc编译器(请注意,不是MinGW)。
一、环境信息
1、GoGo语言版本需要大于1.10,否则会报错:
-buildmode=shared not supported on windows/amd64
1
-buildmode=sharednotsupportedonwindows/amd64
具体可参见:https://golang.org/doc/go1.10#compiler
2、Mingw64Mingw64是gcc官方唯一支持的环境,可从http://www.mingw-w64.org/doku.php/download下载,安装时的架构选择x86_64即可,在安装完成后将Mingw64的bin目录(如D:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin)加入到PATH环境变量中,然后可以查看Go、Mingw64、gcc的版本:
查看Go、Mingw64及gcc版本
请注意,Mingw64的平台应和GOARCH的平台一致。例如,笔者的64位Win10下编译出的DLL文件为x64架构的:
注意Mingw架构
二、编写代码在代码中导出要导出的函数,以下的代码被抄得到处都是:
main.go
Go
package main
import "C"
import "fmt"
//export PrintBye
func PrintBye() {
fmt.Println("From DLL: Bye!")
}
//export Sum
func Sum(a int, b int) int {
return a + b;
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
packagemain
import"C"
import"fmt"
//export PrintBye
funcPrintBye(){
fmt.Println("From DLL: Bye!")
}
//export Sum
funcSum(aint,bint)int{
returna+b;
}
funcmain(){
// Need a main function to make CGO compile package as C shared library
}
其中://export的注释是必须的;main函数也是必须的,即使main函数是空的;如有必要,依然可以加入init函数。
三、编译为DLL
使用命令:
go build -buildmode=c-shared -o exportgo.dll main.go
1
gobuild-buildmode=c-shared-oexportgo.dllmain.go
即可直接将main.go编译为exportgo.dll。
我们可以使用《在Java中进行中文繁体简体转换,基于OpenCC(Open Chinese Convert)方案》中描述的dumpbin工具或Dependency Walker查看导出的DLL是否正确:
使用dumpbin查看DLL文件
我们会发现除了PrintBye和Sum函数外,Go语言中自带的大量package中的函数也被导出了,多达17000余个。
此外,还可以通过
-ldflags 指定链接器参数去除符号表及调试信息等。
此外,如果要编译为静态库,可以使用:
go build -buildmode=c-archive main.go
1
gobuild-buildmode=c-archivemain.go
执行完毕后会在当前目录生成main.a和main.h文件。
四、交叉编译
我们可以使用命令:
go tool dist list
1
gotooldistlist
查看当前支持的交叉编译环境(GOOS/GOARCH):
查看Go支持的交叉编译环境
然后使用命令:
set GOOS=linux
set GOARCH=386
set CGO_ENABLED=0
go build
1
2
3
4
setGOOS=linux
setGOARCH=386
setCGO_ENABLED=0
gobuild
配合对应的交叉编译链即可实现交叉编译。
五、.Net调用Go编译出的DLL
根据导出函数定义进行声明:
C#
[DllImport("exportgo.dll", EntryPoint = "PrintBye")]
static extern void PrintBye();
[DllImport("exportgo.dll", EntryPoint = "Sum")]
static extern int Sum(int a, int b);
1
2
3
4
5
[DllImport("exportgo.dll",EntryPoint="PrintBye")]
staticexternvoidPrintBye();
[DllImport("exportgo.dll",EntryPoint="Sum")]
staticexternintSum(inta,intb);
然后调用:
private void button_SaveSetting_Click(object sender, EventArgs e)
{
Console.WriteLine("Call go dll test");
PrintBye();
Console.WriteLine(Sum(33, 22));
}
1
2
3
4
5
6
7
privatevoidbutton_SaveSetting_Click(objectsender,EventArgse)
{
Console.WriteLine("Call go dll test");
PrintBye();
Console.WriteLine(Sum(33,22));
}
将DLL文件与.Net编译出的可执行文件放在一起执行,并观察stdout的输出:
.Net调用Go语言DLL测试
参考资料:
1、https://stackoverflow.com/questions/40573401/building-a-dll-with-go-1-7
2、https://my.oschina.net/bfbd/blog/1622767
3、https://blog.csdn.net/rtduq/article/details/80340461
4、https://blog.csdn.net/xulingyun20032003/article/details/81220098