【问题标题】:How to elegantly migrate from uses of `syscall.Syscall()`?如何优雅地从 `syscall.Syscall()` 的使用中迁移?
【发布时间】:2020-01-18 15:13:54
【问题描述】:

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)))

现在someProc 的类型是*windows.LazyProc

但是,如果我们这样做,我们将无法获得syscall.Syscall(和朋友)的保证之一,因为LazyProc.Call() is not implemented in 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

有没有更好的办法?

【问题讨论】:

    标签: go


    【解决方案1】:

    我认为处理这个问题的正确方法是使用mkwinsyscall 工具。 你可以像这样创建一个 Go 文件:

    package main
    //go:generate mkwinsyscall -output zmsi.go msi.go
    //sys MsiOpenDatabase(dbPath string, persist int, db *windows.Handle) (e error) = msi.MsiOpenDatabaseW
    

    然后运行go generate,你会得到一个这样的结果文件(删除了一些部分 为简洁起见):

    func MsiOpenDatabase(dbPath string, persist int, db *windows.Handle) (e error) {
        var _p0 *uint16
        _p0, e = syscall.UTF16PtrFromString(dbPath)
        if e != nil {
            return
        }
        return _MsiOpenDatabase(_p0, persist, db)
    }
    
    func _MsiOpenDatabase(dbPath *uint16, persist int, db *windows.Handle) (e error) {
        r0, _, _ := syscall.Syscall(procMsiOpenDatabaseW.Addr(), 3, uintptr(unsafe.Pointer(dbPath)), uintptr(persist), uintptr(unsafe.Pointer(db)))
        if r0 != 0 {
            e = syscall.Errno(r0)
        }
        return
    }
    

    如您所见,它还会自动处理字符串转换 作为 Syscall 代码,甚至是错误处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-07
      • 1970-01-01
      • 2021-01-08
      • 2011-05-23
      • 1970-01-01
      相关资源
      最近更新 更多