注册 登录

清河洛

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()接收的值需要进行编码
    返回的值为经过解码即可获得对应的值


网址导航