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

将字符串传递给 Syscall(uintptr)

将字符串传递给 Syscall(uintptr)

Go
qq_花开花谢_0 2023-03-21 16:54:35
我需要将字符串作为参数传递给 golang 中的 C DLL,我想得到这样的东西:  proc, e = syscall.GetProcAddress(h, "JLINKARM_ExecCommand") //One of the functions    vals := []string{"device = STM32F429ZI"}    start := uintptr(unsafe.Pointer(&vals[0]))    asd, _, _ = syscall.Syscall6(uintptr(proc), 3, start, 0, 0, 0, 0, 0) 但它不起作用。系统调用将 uintptr 作为参数。我不能使用 C 包,因为它不能在 Windows 上构建。在 C 中,它的工作方式如下:strcpy(acIn, "device = STM32F407IE");JLINKARM_ExecCommand(acIn, &acOut[0], sizeof(acOut));那么 C DLL 是否有机会获取字符串参数并正确使用它?
查看完整描述

2 回答

?
翻过高山走不出你

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

要将字符串传递给系统调用,您需要传递指向字符串第一个字符的指针。第一个问题是您的 DLL 函数需要什么字符串编码。Go 字符串被编码为 UTF-8 unicode,因此如果您的 C 函数需要其他内容,您必须先转换它们。以下是一些常见的情况:


1)ASCII字符串

假设您的 C 函数需要一个以零结尾的 ASCII 字符串,那么您可以执行以下操作:


s := "some string"

b := append([]byte(s), 0)

syscall.Syscall(uintptr(proc), 1, uintptr(unsafe.Pointer(&b[0])), 0, 0)

首先将您的字符串转换为字节数组,并在末尾添加 C 期望的零。然后将指针传递给第一个字节字符。


为了安全起见,您还应该确保您实际上只传入有效的 ASCII 字符串,没有无效字符。通常只有 [0..127] 范围内的字符对通用 ASCII 有效。其余取决于当前代码页。


2) Windows 上的 UTF-16

如果您调用 Windows DLL,您通常希望使用该函数的 UTF-16 版本,例如SendMessageW,因此需要将您的字符串转换为 UTF-16。幸运的是,在 Windows 下有一个包装函数,因此您只需执行以下操作:


s := "some string"

s16, err := syscall.UTF16PtrFromString(s)

if err == nil {

    syscall.Syscall(uintptr(proc), 1, uintptr(unsafe.Pointer(s16)), 0, 0)

}

这将转换为 UTF-16 并在末尾为您附加预期的零。


查看完整回答
反对 回复 2023-03-21
?
青春有我

TA贡献1784条经验 获得超8个赞

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

package main

//go:generate mkwinsyscall -output zmsi.go msi.go

//sys MsiInstallProduct(path string, command string) (e error) = msi.MsiInstallProductW

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


func MsiInstallProduct(path string, command string) (e error) {

    var _p0 *uint16

    _p0, e = syscall.UTF16PtrFromString(path)

    if e != nil {

        return

    }

    var _p1 *uint16

    _p1, e = syscall.UTF16PtrFromString(command)

    if e != nil {

        return

    }

    return _MsiInstallProduct(_p0, _p1)

}


func _MsiInstallProduct(path *uint16, command *uint16) (e error) {

    r0, _, _ := syscall.Syscall(procMsiInstallProductW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(command)), 0)

    if r0 != 0 {

        e = syscall.Errno(r0)

    }

    return

}

如您所见,它会自动处理字符串转换、系统调用代码,甚至错误处理。


查看完整回答
反对 回复 2023-03-21
  • 2 回答
  • 0 关注
  • 115 浏览
慕课专栏
更多

添加回答

举报

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