Reputation: 156
I need to pass string as param to C DLL in golang, i want to get something like this:
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)
but it doesn't work. Syscall take as param uintptr. I can't use C package because it won't build on windows.
In C it works like:
strcpy(acIn, "device = STM32F407IE");
JLINKARM_ExecCommand(acIn, &acOut[0], sizeof(acOut));
So it is any chance that C DLL can get string param and use it properly?
Upvotes: 3
Views: 4681
Reputation: 1
A good way to handle this, is to use the mkwinsyscall tool. You can create a Go file like this:
package main
//go:generate mkwinsyscall -output zmsi.go msi.go
//sys MsiInstallProduct(path string, command string) (e error) = msi.MsiInstallProductW
Then run go generate
, and you get a result file like this (some parts removed
for brevity):
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
}
As you can see, it automatically takes care of the string conversion, as well as the Syscall code, and even the error handling.
Upvotes: 3
Reputation: 5582
To pass a string to syscall you need to pass a pointer to the first character of the string. The first question is what string encoding your DLL function expects. Go strings are encoded as UTF-8 unicode so if your C function expects something else you have to convert them first. Here are some common cases:
Say your C function expects a zero-terminated ASCII string, then you can do the following:
s := "some string"
b := append([]byte(s), 0)
syscall.Syscall(uintptr(proc), 1, uintptr(unsafe.Pointer(&b[0])), 0, 0)
First convert your string to a byte array and add the zero that C expects at the end. Then pass the pointer to the first byte character.
To be safe you should also make sure that you actually pass in only valid ASCII strings and no invalid characters. Usually only characters in the range [0..127] are valid for general ASCII. The rest depends on the current codepage.
If you call into a Windows DLL you want to usually use their UTF-16 version of that function, e.g. SendMessageW, and thus need to convert your string to UTF-16. Luckily there is a wrapper function for that under Windows, so you can just do the following:
s := "some string"
s16, err := syscall.UTF16PtrFromString(s)
if err == nil {
syscall.Syscall(uintptr(proc), 1, uintptr(unsafe.Pointer(s16)), 0, 0)
}
This will convert to UTF-16 and append the expected zero at the end for you.
Upvotes: 11