将字符串传递给Syscall(uintptr)

时间:2018-08-20 06:26:11

标签: go dll

我需要将字符串作为参数传递给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) 

但是它不起作用。 Syscall作为参数uintptr。 我不能使用C包,因为它不能在Windows上构建。

在C中,它的工作方式如下:

strcpy(acIn, "device = STM32F407IE");

JLINKARM_ExecCommand(acIn, &acOut[0], sizeof(acOut));

那么C DLL是否有可能获取字符串参数并正确使用它?

2 个答案:

答案 0 :(得分:6)

要将字符串传递给syscall,您需要将指针传递给字符串的第一个字符。第一个问题是对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并在结尾处附加期望的零。

答案 1 :(得分:1)

syscall软件包is deprecated

已弃用:此软件包已被锁定。来电者应使用相应的 打包到golang.org/x/sys存储库中。那也是 应该应用新系统或版本所需的更新。

因此,您应该使用golang.org/x/sys/windows软件包或类似的软件包。 但是,您可能会注意到Syscall函数和类似函数不是 在golang.org/x/sys包中可用。相反,您将使用 LazyProc.Call。示例:

package main

import (
   "golang.org/x/sys/windows"
   "unsafe"
)

func Pointer(s string) (uintptr, error) {
   p, e := windows.UTF16PtrFromString(s)
   if e != nil {
      return uintptr(0), e
   }
   return uintptr(unsafe.Pointer(p)), nil
}

func InstallProduct(path_s string, cmd_s string) error {
   path_p, e := Pointer(path_s)
   if e != nil {
      return e
   }
   cmd_p, e := Pointer(cmd_s)
   if e != nil {
      return e
   }
   o := windows.NewLazyDLL("msi.dll").NewProc("MsiInstallProductW")
   o.Call(path_p, cmd_p)
   return nil
}

func main() {
   s := `Action=Admin TargetDir=D:\Rust`
   InstallProduct("rust-1.48.0-x86_64-pc-windows-gnu.msi", s)
}