为了账号安全,请及时绑定邮箱和手机立即绑定

将包含 *byte 的结构传递给 Syscall 并在执行后读取其内容

将包含 *byte 的结构传递给 Syscall 并在执行后读取其内容

Go
守候你守候我 2023-07-31 16:23:43
我用来syscall.Syscall(...)调用 dll 中的 C 方法。这是 C 方法签名:SENSEI_API HSENSEI SENSEI_open(const char* sensigrafo, const char* options, SENSEI_ERR* se);这是SENSEI_ERR结构:typedef struct{    int code;    char* error_string;} SENSEI_ERR;在我的 GO 程序中,我声明了一个结构:type senseiErr struct {    code         int    error_string *byte}并尝试调用该方法:    var nargs uintptr = 3    var err senseiErr    ret, _, callErr := syscall.Syscall(uintptr(senseiOpen),        nargs,        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("en"))),        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))),        uintptr(unsafe.Pointer(&err)),    )正如您可能已经猜到的,该方法用错误的代码和文本SENSEI_open填充参数。SENSEI_ERR现在我需要阅读该错误的内容。err.code实际上有正确的值。关于err.error_string我不知道。我是 GO 新手,有一些问题:由于 C 结构体有字段char* error_string,error_string *byte我的 GO 结构体是否正确?我应该使用[]byte还是其他东西?如何读取该error_string字段的内容?fmt.Println(err.error_string)打印内存地址fmt.Println(*err.error_string)始终打印“101”
查看完整描述

1 回答

?
倚天杖

TA贡献1828条经验 获得超3个赞

1)我怀疑这cost char*是否意味着UTF16编码。所以你所需要的只是获取原始数据:


sensigrafo := "en\000" // \000 = 0 = null termination, \0 does not valid

options := "\000"


...


uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&sensigrafo))

uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&options))


// *(*unsafe.Pointer) are accessing the first field of string header:

type string struct {

    data *byte

    len int

}


// same with slices

// but for them there's less ugly way:

sensigrafo := []byte("en\000")

options := []byte("\000")


uintptr(unsafe.Pointer(&sensigrafo[0]))

uintptr(unsafe.Pointer(&options[0]))

2) Cint和 Golangint可能有不同的 sizeof,所以这需要 cgo 声明 ( C.int) 或手动匹配随机选择(如果你不想使用 cgo,也可以尝试 int32、int64)


type senseiErr struct {

    code         C.int /* Golang's int32/int64 */

    error_string *byte // pointer types are same as C's void* or Golang's unsafe.Pointer

}

错误的偏移量可能会导致 error_string 为空或指向随机地址。


3)要读取内容,您必须使用与 C 相同的方法(读取数据直到 null 终止字节,考虑到 *byte 指向字符串的第一个元素),但我建议使用已经实现的运行时函数:


//go:linkname gostringn runtime.gostringn

func gostringn(p *byte, l int) string


//go:linkname findnull runtime.findnull

//go:nosplit

func findnull(s *byte) int


...


error_string := gostringn(err.error_string, findnull(err.error_string))


// or cgo one:


type senseiErr struct {

    code         C.int

    error_string *C.char

}


...


error_string := C.GoString(err.error_string)


查看完整回答
反对 回复 2023-07-31
  • 1 回答
  • 0 关注
  • 71 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信