您现在的位置是:网站首页> Go语言

GO语言调用DLL,填平所有的坑,最详尽攻略

摘要

由于业务需要,购买了别人写好的一个DLL模块,于是磨难开始了,经历多天的折磨,终于完美解决。


首先DLL如果是32位的,编译前运行 


set GOARCH=386

第一个DLL函数,第一个参数,要求传入一个指针,直接指向[]byte类型,注意,是直接指向;第2个参数为[]byte长度;第三个参数为一个直接指向string类型指针;返回一个整数,标识调用成功或失败。


最折磨我的就是直接指向某种类型的指针传递问题,查了N多资料,都是类似下面这样:



p:= unsafe.Pointer(&dat)

g:=dll32.NewProc("XXX")

r, _, _ :=g.Call(uintptr(p),uintptr(cdat),uintptr(pk))

我开始也这样用,怎么弄都不对,然后我用OD载入调试,发现传进去的东西根本不是DLL想要的。

这样传进去的数据会被2层指针指向,ptrA->ptrB->[]byte,传进去的是ptrA,所以导致无法正常调用。那么问题来了,怎么才能传进去的指针直接指向数据,达到类似ptrA->[]byte这样的效果呢?


问题的重点就在这里,研究了好几天uintptr发现不是它的问题,问题出在


unsafe.Pointer

它上面,它会在指针外面再包一层指针,怎么解决呢?我只能考虑先把指针转成整数再传进去,结果



p:= *((*int32)(unsafe.Pointer(&dat)))

r, _, _ :=g.Call(uintptr(p),uintptr(cdat),uintptr(pk))

这样成功了。下面传递整数指针就简单多了


cdat:=len(dat)

这样即可,再后面传递字符串指针,指针获取方式和byte一样即可。但是问题又来了,执行不成功,继续OD,发现有问题,问题在于GO语言字符串后面在内存中没有结尾标志。那GO自己怎么判断字符串结尾呢?我想应该是每个字符串GO都同时记录了长度吧,不过不确定,有明白的大神请告知,这个问题我就只能这样,先把字符串转换成byte,然后在byte最后加0,类似这样


keystr:=[]byte{49,50,51,0}

pk:= *((*int32)(unsafe.Pointer(&keystr)))

这个问题就解决了,这个字符串就变成windows识别的了。返回值整数,直接就能用,这点我很奇怪,不知道为什么,比如这里,可以直接


r, _, _ =g.Call(uintptr(p),uintptr(cdat),uintptr(pk))

if r!=1 {

按理说,返回的是个指向整数的指针,应该*r才对,不懂,大神告知。

然后现在所有传递参数的问题解决了,后面问题又来了,第2个函数,调用后返回值是指向字符串的指针,这个指针指向的内容字符串当然是0结尾的windows格式了,GO依然无法正确读取。怎么办呢,只能自己写了个函数处理这个问题



//根据DLL返回的指针,逐个取出字节,到0字节时判断为字符串结尾,返回字节数组转成的字符串

func prttostr(vcode uintptr) string {

   var vbyte []byte

   for i:=0;i<10;i++{

      sbyte:=*((*byte)(unsafe.Pointer(vcode)))

      if sbyte==0{

         break

      }

      vbyte=append(vbyte,sbyte)

      vcode += 1

   }

   return string(vbyte)

}

原理就是操作指针一直向后移动,发现0了就停止,这样问题解决了,虽然所有的资料和大神都告诉我,不要轻易操作指针,但是这种情况下,不这么弄,怎么弄呢?谁能告诉我。

无论如何问题终于解决了,也许解决的不够完美,我想应该有更简单的办法,但是我不知道。



Top