go编译dll文件并调用
qingheluo2021-12-06清河洛641
首先,go编译dll的过程需要用到gcc,所以要先安装gcc现在我们开始编写要编译成dll的go程序package main
# 包名必须为main
import "C"
# 必须引入C(大写)包,C是虚拟包,可以调用C语言函数
//export MyFunc
func MyFunc(a int , b int) int{
return a+b
}
# 需要导出来的函数上面必须要写//export [函数名]格式的注释且与函数主体之间不能有其他语句
# 编写要导出函数的逻辑
# 函数名并不一定首字母大写,但是习惯上首字母会大写
func m...
首先,go编译dll的过程需要用到gcc,所以要先安装gcc
现在我们开始编写要编译成dll的go程序
package main
# 包名必须为main
import "C"
# 必须引入C(大写)包,C是虚拟包,可以调用C语言函数
//export MyFunc
func MyFunc(a int , b int) int{
return a+b
}
# 需要导出来的函数上面必须要写//export [函数名]格式的注释且与函数主体之间不能有其他语句
# 编写要导出函数的逻辑
# 函数名并不一定首字母大写,但是习惯上首字母会大写
func main(){}
# 必须要有一个main主函数,可以为空但必须要有
以上内容为一个最为简单的示例,下面我们使用go build命令编译成dll
go build -ldflags "-s -w" -buildmode=c-shared -o dll_filename go_filename
-ldflags选项非必须,但是加上会使编译成的dll体积减小,所以一般会使用该选项
以上命令会生成一个dll文件和同名的.h头文件,该头文件不用管,可以删除
调用dll文件
Go语言
import (
"syscall"
"fmt"
)
# 引入syscall包来调用dll文件
func main(){
dll := syscall.NewLazyDLL("test.dll")
jiafa := dll.NewProc("Add")
demo,err,msg := jiafa.Call(3,5)
fmt.Println(demo)
fmt.Println(err)
fmt.Println(msg)
}
NewLazyDLL()函数引入dll文件
NewProc()函数注册dll文件中的函数
Call()函数调用函数
Call()函数返回3个值,第一个为函数返回值,第二个为异常,第三个为运行函数的信息
Python语言
import ctypes
# 引入ctypes包来调用dll文件
dll = ctypes.CDLL(r"E:/code/golang/demo/test.dll")
# 引入指定的dll文件,这里必须为绝对路径
demo = dll.chu(66, 33)
# 运行dll文件中的函数
print(demo)
使用GO编译成dll我们调用了"C"库从而将数据类型都转换为C类型,所以我们编写GO程序时和使用GO或Python调用dll时传入和返回的数据类型都是C类型,必须对数据类型进行转换
数据类型的对应关系
python | ctypes | C | go | C模块 |
int | c_byte | char | int8 | C.char |
int | c_ubyte | unsigned char | uint8或byte | C.uchar |
int | c_short | short | int16 | C.short |
int | c_ushort | unsigned short | uint16 | C.ushort |
int | c_int | int | int | C.int |
int | c_uint | unsigned int | uint | C.uint |
int | c_long | long | int64 | C.long |
int | c_ulong | unsigned long | uint64 | C.ulong |
int | c_longlong | __int64 或 long long | int64 | C.longlong |
int | c_ulonglong | unsigned __int64 或 unsigned long long | uint64 | C.ulonglong |
float | c_float | float | float32 | C.float |
float | c_double | double | float64 | C.double |
float | c_longdouble | long double | float64 | C.double |
bool | c_bool | _Bool | bool | C._Bool |
单ASCII字符 | c_char | char | 单字ASCII字符 | C.char |
单Unicode字符 | c_wchar | wchar_t | 单rune | C.wchar_t |
字节串 | c_char_p | char* | 字符串 | *C.char |
Unicode字符串 | c_wchar_p | wchar_t* | Unicode字符串 | *C.wchar_t |
在python调用dll时,每个函数运行之前要指定该函数的返回值类型:dll.FunName.restype = ctypes_type
from ctypes import *
dll = CDLL("E:/demo/demo.dll")
dll.buer.restype = c_bool
# 表示该dll文件中的buer函数返回值为一个布尔值
dll.zfc.restype = c_char_p
# 表示该dll文件中的zfc函数返回值为一个字符串
下面我们举几个常用的python使用示例:
布尔值
函数源码:
func buer(val C._Bool) C._Bool{
flag := bool(val)
return C._Bool(flag)
}
Python调用:
dll.buer.restype = c_bool
res = dll.buer(c_bool(True))
传入一个C类型的布尔值
返回值无需转化,python会自动转化为布尔值
数字
函数源码:
func num(val C.long) C.long{
jia :=5
msg := val + C.long(jia)
msg = msg + 3
return msg
}
数字类型如果为常量则无需转换,但是如果是变量的话必须进行转换
以上代码中第一次相加时变量val必须进行转换
第二次相加时数字3为一个固定的数字,无需转换直接相加
对于float类型也是有该特性
Python调用:
dll.num.restype = c_long
demo = dll.num(10)
传入数字时可以使用c_long(val),也可以直接传入具体数值
字符串
函数源码:
func zfc(val *C.char) *C.char{
str := C.GoString(val)
str = "测试:"+str+str
return C.CString(str)
}
要对字符串进行操作,必须先使用C.GoString()函数转换为go语言的字符串
对字符串操作完成后使用C.CString()函数转换为C语言的字符串并返回
Python调用:
dll.st.restype = c_char_p
demo = dll.st(c_char_p("字符串".encode("utf-8")))
print(demo.decode("utf-8"))
传入的字符串必须转换为C语言的字符串,且转换函数c_char_p()接收的值需要进行编码
返回的值为经过解码即可获得对应的值