我正在使用Go的syscall包来调用用C++编写的DLL。
C++方法签名如下所示。
init(int* buffer, int argc, char* argv[], const char* fileName, const char* key, const char* prefix, const char* version)
这是我在Go中调用上述方法的函数。
func init(
buffer uintptr,
argsCount int,
args []string,
fileName string,
key string,
prefix string,
version string
) uintptr {
// libHandle is handle to the loaded DLL
methodAddress := getProcAddress(libHandle, "init")
status, _, err := syscall.Syscall9(
methodAddress,
7,
buffer,
uintptr(unsafe.Pointer(&argsCount)),
uintptr(unsafe.Pointer(&args)),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(key))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(prefix))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(version))),
0,
0)
fmt.Println(err.Error())
return status
}当我调用这个方法时,我会得到这个错误,但对此我没有任何想法。
Exception 0xc0000005 0x0 0x0 0x7fffe30bdb33
PC=0x7fffe30bdb33
syscall.Syscall9(0x7fffe32db600, 0x7, 0x97db50, 0xc00007ff10,
0xc00007ff70, 0xc000054180, 0xc0000541a0, 0xc0000541c0, 0xc0000541e0,
0x0, ...)
c:/go/src/runtime/syscall_windows.go:210 +0xf3
main.main()
E:/Path/test.go:157 +0x2be
rax 0x81fbf0
rbx 0x1
rcx 0x7ff804ad1310
rdi 0x7ff10
rsi 0xc00007ff70
rbp 0x0
rsp 0x81fbc0
r8 0x0
r9 0x7ff804ad0000
r10 0xc00007ff00
r11 0x81fbf0
r12 0x7ff10
r13 0xc00007ff70
r14 0xc000054180
r15 0x97db50
rip 0x7fffe30bdb33
rflags 0x10257
cs 0x33
fs 0x53
gs 0x2b发布于 2019-06-25 16:58:04
设置
所以,基本上,你得到的映射
int* buffer→buffer uintptrint argc→unsafe.Pointer(&argsCount),其中&argsCount是指向int的指针char* argv[]→unsafe.Pointer(&args),args是[]stringconst char* fileName→unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))
const char* key,const char* prefix,const char* version -和上面一样。问题
这是怎么回事。
uintptr值。(稍后将对此进行详细介绍)argsCount函数参数的地址传递给argc。一个典型的商品平台/操作系统上的地址,如as 64/Windows,如果被解释为计数的话,是一个非常巨大的价值。
我敢打赌,当函数尝试从argv-causing读取许多元素时,它会崩溃,从而读取进程未映射的内存。1. Passing an address of a slice value as an argument expecting the address of the first element of that slice is wrong. --这是因为一个片值(目前,在所谓的"reference“Go实现中)是一个包含三个字段的struct:底层数据数组的地址、允许使用的数组中元素的数量以及该数组中这些元素的总数,这些元素在对片值调用时可以不被append函数重新分配而使用。
当您拥有args []string并执行&args时,您将得到该结构的地址,而不是该切片的第一个元素的地址。
要执行后一项操作,请使用&args[0]。
2. In Go, a string (typically, and let's assume that's the case) contains characters encoded as UTF-8. This is typically not what a Windows-native C++ code expects to deal with when it says it wants a `char *`. --我猜,您首先需要为argv构造一个合适的东西,比如
:= ([]unsafe.Pointer,0,len( args ))用于i,s :=范围args{ argvi = unsafe.Pointer(syscall.StringToUTF16Ptr(s)) }
然后把&argv[0]传给被叫人。
但是关于syscall.StringToUTF16Ptr(),请看下面。
const char*类型的其余参数所做的准备似乎是正确的,但前提是被调用方真正意味着其char是一个16位整数。
换句话说,编译该库所用的源代码和工具链必须确保char确实是WCHAR。
如果是的话,你所做的应该是好的,否则就不会。你应该核实一下。关于跨表达式传递uintptr的说明
Go具有垃圾收集功能,因此它的运行时必须知道指向所有当前活动对象的所有指针。只要有一个变量包含指向程序运行时分配的内存块的指针,该块就不会被垃圾回收。
unsafe.Pointer值可算作对内存块的正确引用,但uintptr值不是。
p := new(someType)
u := uintptr(unsafe.Pointer(&p))
foo(u)
return正在运行时,GC可以在p分配其地址后立即收回p分配的对象--仅仅因为p是对该对象的唯一引用,而u不是。
现在考虑一下,参考Go实现中的GC与程序代码同时运行。这意味着当foo运行时,GC也可以运行,并且它可以从someType的脚下直接扫描foo的实例。
作为此一般规则的一个例外,Go编译器确保发生在同一语言表达式中的所有uintptr(unsafe.Pointer)类型转换都不受GC的影响。所以,以我们前面的例子来说,这是可以做的。
foo(uintptr(unsafe.Pointer(new(someType)))
return和
p := new(someType)
v := unsafe.Pointer(&p)
foo(uintptr(v))
return因为到uintptr的类型转换发生在一个表达式中--这是一个函数调用。
因此,除非指针包含从"C端“获得的指针,否则不能传递指针以uintptrs的形式传递对象。
https://stackoverflow.com/questions/56755969
复制相似问题