【问题标题】:golang get char* as return value from dllgolang 获取 char* 作为 dll 的返回值
【发布时间】:2019-09-20 13:31:56
【问题描述】:

我正在使用golang调用像char* fn()这样的Dll函数,该dll不是我自己编写的,我无法更改它。这是我的代码:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    dll := syscall.MustLoadDLL("my.dll")
    fn := dll.MustFindProc("fn")

    r, _, _ := fn.Call()

    p := (*byte)(unsafe.Pointer(r))
    // define a slice to fill with the p string
    data := make([]byte, 0)

    // loop until find '\0'
    for *p != 0 {
        data = append(data, *p)        // append 1 byte
        r += unsafe.Sizeof(byte(0))    // move r to next byte
        p = (*byte)(unsafe.Pointer(r)) // get the byte value
    }
    name := string(data) // convert to Golang string
    fmt.Println(name)
}

我有一些问题:

  1. 有没有更好的方法来做到这一点?这样的dll函数有上百个,我得为所有函数编写循环。
  2. 对于像 100k+ 字节这样的超长字符串,append() 会导致性能问题吗?
  3. 已解决unsafe.Pointer(r) 导致 linter govet 显示警告possible misuse of unsafe.Pointer,但代码运行良好,如何避免此警告? 解决方案: 这可以通过在govet 命令行中添加-unsafeptr=false 来解决,对于vim-ale,添加let g:ale_go_govet_options = '-unsafeptr=false'

【问题讨论】:

  • 为什么不直接使用C.GoString
  • 考虑添加您的解决方案作为答案

标签: go unsafe-pointers


【解决方案1】:

将 uintptr 转换为 uppointer 是禁忌。 您必须阅读规则: https://golang.org/pkg/unsafe/#Pointer

但是有一种不应该产生警告的 hacky 方式:

//go:linkname gostringn     runtime.gostringn
func gostringn(p uintptr, l int) string

//go:linkname findnull     runtime.findnull
//go:nosplit
func findnull(s uintptr) int

// ....

name := gostringn(r, findnull(r))

函数需要指针,但我们从运行时将它们链接为 uintptr,因为它们具有相同的 sizeof。

理论上可能可行。但也被不屑一顾。

回到你的代码,正如 JimB 所说,你可以用一行来完成:

name := C.GoString((*C.char)(unsafe.Pointer(r)))

【讨论】:

    【解决方案2】:

    我通过跟踪go源码的os.Args得到如下解决方案,但是我是基于go1.17的。如果你在其他版本,可以阅读源码解决。

    func UintPtrToString(r uintptr) string {
        p := (*uint16)(unsafe.Pointer(r))
        if p == nil {
            return ""
        }
    
        n, end, add := 0, unsafe.Pointer(p), unsafe.Sizeof(*p)
        for *(*uint16)(end) != 0 {
            end = unsafe.Add(end, add)
            n++
        }
        return string(utf16.Decode(unsafe.Slice(p, n)))
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 2012-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多