调用 Syscall 函数时出现异常

我正在使用 Go 的系统调用包来调用用 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


炎炎设计
浏览 155回答 1
1回答

小唯快跑啊

那个设定所以,基本上,你得到的映射是int* buffer → buffer uintptrint argc→ unsafe.Pointer(&argsCount),其中&argsCount是一个指向intchar* argv[]→ unsafe.Pointer(&args),哪里args是[]stringconst char* fileName→unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))const char* key, const char* prefix, const char* version— 同上。存在的问题现在这有什么问题呢。uintptr禁止在函数之间传递包含活动 Go 对象地址的值。(稍后会详细介绍。)argsCount您将函数参数的地址传递给argc。如果将其解释为计数,则典型的商品平台/操作系统(例如 amd64/Windows)上的地址是一个极其巨大的值。我敢打赌,当函数尝试从中读取这么多元素时,它会崩溃,导致argv它读取进程未映射的内存。这里有两个问题:将切片值的地址作为参数传递,期望该切片的第一个元素的地址是错误的。这是因为切片值(目前,在您应该使用的“参考”Go 实现中)是具有struct3 个字段的:底层数据数组的地址、该数组中允许使用的元素数以及总数该数组中此类元素的计数append,在对切片值调用时,函数可以使用这些元素,而无需重新分配。当您拥有args []string并执行时&args,您将获得该结构的地址,而不是该切片的第一个元素的地址。要执行后者,请使用&args[0].在 Go 中,字符串(通常是这样,我们假设是这样)包含编码为 UTF-8 的字符。这通常不是 Windows 本机 C++ 代码在表示需要char *.我猜,你需要首先为 构建一个合适的东西argv,比如argv := make([]unsafe.Pointer, 0, len(args))for i, s := range args {     argv[i] = unsafe.Pointer(syscall.StringToUTF16Ptr(s)) }然后传递&argv[0]给被调用者。但请参阅下文syscall.StringToUTF16Ptr()。您正在做的将字符串数据传递给具有该类型的其余参数的准备工作const char*似乎是正确的,但前提是被调用者确实意味着它char是一个 16 位整数。换句话说,用于编译该库的源代码和工具链必须确保它char确实是wchar_t或WCHAR。如果是,那么你的做法应该没问题;否则就不是。你应该验证这一点。uintptr关于在表达式之间传递 s 的注意事项Go 具有垃圾收集功能,因此它的运行时必须知道指向所有当前活动对象的所有指针。只要存在一个变量,其中包含指向在程序运行时分配的内存块的指针,该块就不会被垃圾收集。值unsafe.Pointer算作对内存块的正确引用,但uintptr值则不然。这意味着当代码p := new(someType) u := uintptr(unsafe.Pointer(&p)) foo(u)returnp正在运行时,一旦分配了地址,GC 就可以自由地回收由 分配的对象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在单个表达式中——这是一个函数调用。因此,您不能将指向 Go 对象的指针作为uintptrs传递,除非它们包含从“C 端”获取的指针。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go