Голанг получить символ * в качестве возвращаемого значения из DLL - PullRequest
0 голосов
/ 20 сентября 2019

Я использую golang для вызова функции Dll, например char* fn(), 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, но код работает нормально, как избежать этого предупреждения? Решение: Эту проблему можно решить, добавив -unsafeptr=false к govet командной строке, для vim-ale , добавьте let g:ale_go_govet_options = '-unsafeptr=false'.

enter image description here

1 Ответ

2 голосов
/ 21 сентября 2019

Кастинг uintptr как upointer - это харам.Вы должны прочитать правила: https://golang.org/pkg/unsafe/#Pointer

Но есть хакерский способ, который не должен выдавать предупреждение:

//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, потому что ониимеют одинаковый размер.

Может работать в теории.Но также осуждается.

Возвращаясь к своему коду, как сказал JimB, вы можете сделать это одной строкой с:

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