golang 交叉编译C++ dll配置文件的实现
调用c++ 报%1 is not a valid Win32 application
开发环境:
win64位
C++ dll 为32位
首先遇到的坑就是环境的配置,调用了一个之前C++写的dll,一直报错:
%1 is not a valid Win32 application.
就怀疑是否是dll出了问题,使用C++调用,正常运行,排除dll的问题,然后怀疑是否是golang调用方法问题,这里简要说一下golang调用dll的三种方法,使用了C语言编写了一个最基本的dll,只包含helloword函数
void helloword(void) { printf("Hello, World!\n"); }
第一种,直接使用syscall库调用,注意在用完后释放
const HELLODLLPATH = `C:\gopath\src\helloword_dll\libhelloword.dll`
func Hello1() { helloDll, e := syscall.LoadLibrary(HELLODLLPATH) if e != nil { panic(e) } helloword, e := syscall.GetProcAddress(helloDll, "helloword") if e != nil { panic(e) } _, _, er := syscall.Syscall(helloword, 0, 0, 0, 0) if er != 0 { panic(er) } e = syscall.FreeLibrary(helloDll) if e != nil { panic(e) } }
第二种,使用syscall.NewLazyDLL()来调用,这种方式最为简洁,后js面主要使用这种方式
func Helloword2() { helloDll := syscall.NewLazyDLL(HELLODLLPATH) hello := helloDll.Newproc("helloword") hello.Call() }
第三种,使用syscall.MustLoadDLL(),和第二种类似
func Helloword3() { helloDll := syscall.MustLoadDLL(HELLODLLPATH) hello := helloDll.MustFindProc("helloword") _, _, _ = hello.Call() }
介绍完golang这三种调用dll的方式后,回到遇到的问题,采用这三种方式测试golang调用dll是否有问题,结果三种方式均成功调用,线索中断,这个时候就只能怀疑最开始的dll和golang的是否匹配,通过查询资料js,发现之前的dll是32位,而golang的环境为amd64,此时通过goland设置编译环境为386,并且把cgo打开
SET GOARCH=386 SET CGO_ENABLED=1
终于,成功调用!
第二个问题,就是调用dll的参数传递,这里用syscall.NewLazyDLL()来调用,目标dll有一个识别图像的方法,总共四个参数,第一个是一个id,前面调用返回的,第二个是图像数据指针,第三个图像长度,第四个识别方式,返回是C语言字符串,具体调用如下
func IntPtr(n int) uintptr { return uintptr(n) } func BytesPtr(s []byte) uinwww.devze.comtptr { repythonturn uintptr(unsafe.Pointer(&s[0])) } func CnnDll2() { cnnDll := syscall.NewLazyDLL(CNNDLLPATH) //载入 CNN_init := cnnDll.NewProc("CNN_init") bin := OpenBin() id, _, _ := CNN_init.Call(BytesPtr(bin), IntPtr(len(bin)), 0, 0, IntPtr(js2), IntPtr(5)) CNN_recognition := cnnDll.NewProc("CNN_recognition") img := OpenImg() //识别 ret, _, _ := CNN_recognition.Call(id, BytesPtr(img), IntPtr(len(img)), IntPtr(2)) var recode []byte for i := 0; true; i++ { tmp := *(*byte)(unsafe.Pointer(ret + uintptr(i))) if tmp != 0 { recode = append(recode, tmp) } else { break } } fmt.Println("#ret", recode, string(recode)) }
这里返回的字符串是C格式的,和golang的类型底层内存模型不同,所以就是按照C的方式,移动指针,直到找到'\0',字符串结束,但是总觉得这种方式并非最好的方法,通过查询资料,找到了自认为比较科学的转换方式,使用CGO的类型,如下
import "C" reStr := C.GoString((*C.char)(unsafe.Pointer(ret)))
C.GoString()的参数必须是 *C.char ,这里使用了cgo,正如前面所述,需开启cgo环境
到此这篇关于golang 交叉编译C++ dll配置文件的实现的文章就介绍到这了,更多相关golang 交叉编译C++ dll配置内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论