该syscall
软件包已弃用。假设我有以下代码,我想将其迁移到未弃用的代码:
someGoObject := &struct{int; float32}{5, 45.4}
syscall.Syscall6(someProc.Addr(), 1, uintptr(unsafe.Pointer(someGoObject)), 0, 0, 0, 0, 0)
其中someProc
是类型*syscall.LazyProc
(Windows)。
文档建议改用的sys subrepositorysyscall
不再提供类似于 的功能,syscall.Syscall
如果那里没有实现所需的功能,人们可能会尝试通过以下方式解决问题,并相信工作已经完成:
someProc.Call(uintptr(unsafe.Pointer(someGoObject)))
其中 nowsomeProc
属于*windows.LazyProc
.
但是,如果我们这样做,我们将不会得到syscall.Syscall
“(和朋友)的保证之一,因为LazyProc.Call()
它没有在 assembly 中实现:
(4) 在调用 syscall.Syscall 时将指针转换为 uintptr。
syscall 包中的 Syscall 函数将它们的 uintptr 参数直接传递给操作系统,然后操作系统可能会根据调用的细节将其中的一些重新解释为指针。也就是说,系统调用实现隐式地将某些参数从 uintptr 转换回指针。
如果必须将指针参数转换为 uintptr 以用作参数,则该转换必须出现在调用表达式本身中:
syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))
编译器在调用汇编中实现的函数的参数列表中处理转换为 uintptr 的指针,方法是安排引用的已分配对象(如果有)在调用完成之前保留并且不移动,即使仅从类型中它也是如此在通话期间似乎不再需要该对象。
为了让编译器识别这种模式,转换必须出现在参数列表中:
// INVALID: uintptr cannot be stored in variable // before implicit conversion back to Pointer during system call. u := uintptr(unsafe.Pointer(p)) syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))
(取自https://golang.org/pkg/unsafe/)
在我看来,唯一安全的方法是使用 C 分配内存。但是,这意味着我们需要复制数据并增加需要编写的代码量。
someObject := (*struct{int; float32})(C.calloc(1, unsafe.Sizeof(struct{int; float32}{}))) // Alloc
*someObject = struct{int; float32}{123, 456.789} // Fill with desired data
someProc.Call(uintptr(unsafe.Pointer(someObject))) // The actual call
C.free(unsafe.Pointer(someObject)) // Clean up
有没有更好的办法?
我认为处理这个问题的正确方法是使用mkwinsyscall工具。你可以像这样创建一个 Go 文件:
然后运行
go generate
,你会得到一个这样的结果文件(为简洁起见,删除了一些部分):如您所见,它会自动处理字符串转换、系统调用代码,甚至是错误处理。