我需要将字符串作为参数传递给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是否有可能获取字符串参数并正确使用它?
答案 0 :(得分:6)
要将字符串传递给syscall,您需要将指针传递给字符串的第一个字符。第一个问题是对DLL函数进行编码的字符串是什么。 Go字符串被编码为UTF-8 Unicode,因此,如果您的C函数需要其他功能,则必须先将其转换。这是一些常见情况:
假设您的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有效。其余的取决于当前的代码页。
如果您调用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)
}