golang 编译 dll 过程中需要用到 gcc,所以先安装 MinGW。
windows 64 位系统应下载 MinGW 的 64 位版本: https://sourceforge.net/projects/mingw-w64/
下载后运行 mingw-w64-install.exe,完成 MingGW 的安装。
首先撰写 golang 程序 exportgo.go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 } |
编译成 DLL 文件:
1 | go build -buildmode=c-shared -o exportgo.dll exportgo. go |
编译后得到 exportgo.dll 和 exportgo.h 两个文件。
参考 exportgo.h 文件中的函数定义,撰写 C# 文件 importgo.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System; using System.Runtime.InteropServices; namespace HelloWorld { class Hello { [DllImport( "exportgo.dll" , EntryPoint= "PrintBye" )] static extern void PrintBye(); [DllImport( "exportgo.dll" , EntryPoint= "Sum" )] static extern int Sum(int a, int b); static void Main() { Console.WriteLine( "Hello World!" ); PrintBye(); Console.WriteLine(Sum(33, 22)); } |
编译 CS 文件得到 exe
将 exe 和 dll 放在同一目录下,运行。
1 2 3 4 5 | >importgo.exe Hello World! From DLL: Bye! 55 |
golang 中的 string 参数在 C# 中可以如下引用:
1 2 3 4 5 6 7 8 9 10 11 12 | public struct GoString { public string Value { get; set; } public int Length { get; set; } public static implicit operator GoString(string s) { return new GoString() { Value = s, Length = s.Length }; } public static implicit operator string(GoString s) => s.Value; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // func.go package main import "C" import "fmt" //export Add func Add(a C. int , b C. int ) C. int { return a + b } //export Print func Print(s *C. char ) { /* 函数参数可以用 string, 但是用*C.char更通用一些。 由于string的数据结构,是可以被其它go程序调用的, 但其它语言(如 python)就不行了 */ print( "Hello " , C.GoString(s)) //这里不能用fmt包,会报错,调了很久... } func main() { } |
编译
go build -ldflags "-s -w" -buildmode=c-shared -o func.dll func.go
还是有点大的,880KB,纯C 编译的只有48KB,应该是没有包含全部的依赖吧,go是全包进来了
Go 调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import ( "fmt" "syscall" ) func main() { dll := syscall.NewLazyDLL( "func.dll" ) add := dll.NewProc( "Add" ) prt := dll.NewProc( "Print" ) r, err, msg := add.Call(32, 44) fmt.Println(r) fmt.Println(err) fmt.Println(msg) name := C.CString( "Andy" ) prt.Call(uintptr( unsafe .Pointer(name))) } |
1 2 3 4 5 | out : 76 0 The operation completed successfully. Hello Andy |
Python 调用
1 2 3 4 | from ctypes import CDLL, c_char_p dll = CDLL( "func.dll" ) dll.Add(32, 33) dll.Print(c_char_p(bytes( "Andy" , "utf8" ))) |
C++调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> #include <windows.h> using namespace std; typedef int (*pAdd)( int a, int b); typedef void (*pPrt)( char * s); int main( int argc, char *argv[]) { HMODULE dll= LoadLibraryA( "func.dll" ); pAdd add = (pAdd)GetProcAddress(dll, "Add" ); pPrt prt = (pPrt)GetProcAddress(dll, "Print" ); cout << add(321, 33) << endl; prt( "Andy" ); FreeLibrary(dll); return 0; } |