CGO:在 LPCWSTR 和字符串之间转换
CGO: Convert between LPCWSTR and string
我正在为一个库编写 CGO 绑定,该库的所有字符串类型都使用 LPCWSTR
。我如何将 C.LPCWSTR
转换为 string
,反之亦然?
您应该能够 "cast" 将 LPCWSTR
作为 []uint16
,并使用 utf16
包解码字符
// take a C.wstr pointer, and convert it to a go slice
// `sz` is the length of the LPCWSTR
wstr := (*[1 << 30-1]uint16)(unsafe.Pointer(C.wstr))[:sz:sz]
runes := utf16.Decode(wstr)
goString := string(runes)
您通常不希望将 Go 指针传递到您的 C 代码中,因此当从字符串转换为 LPCWSTR 时,您需要在 C 中分配内存。从 Go 字符串转换的解决方案 s
可能看起来像:
func Encode(s string) C.LPCWSTR {
wstr := utf16.Encode([]rune(s))
p := C.calloc(C.size_t(len(wstr)+1), C.sizeof_uint16_t)
pp := (*[1 << 30]uint16)(p)
copy(pp[:], wstr)
return (C.LPCWSTR)(p)
}
可能还有一些 MFC 宏可以帮助与 cstrings 相互转换,您可以利用 C 中的简单包装函数。这样您就可以轻松地使用内置 C.CString
和 C.GoString
函数。
作为在 C 中分配内存的替代方法,可以调用 syscall.UTF16PtrFromString:
func Encode(s string) C.LPCWSTR {
ptr, _ := syscall.UTF16PtrFromString(s)
return C.LPCWSTR(unsafe.Pointer(ptr))
}
这假定您尝试调用的 API(假设是 win32 API,因为您使用的是 LPCWSTR)不制作任何字符串指针的副本。如果确实如此,那么在 GoLang 的代码范围合适的情况下,不分配 C 内存应该是安全的。
例如,按照下面的代码应该没问题:
func DoSomeWindowsStuff(arg string) {
CallWin32API(Encode(arg))
}
这里为字符串分配的内存应该一直存在到 CallWin32API() returns.
如果您确定输入不包含空字节,则可以进行编码
你自己:
import (
// #include <windows.h>
"C"
"unicode/utf16"
"unsafe"
)
func PtrFromString(s string) C.LPCWSTR {
r := []rune(s + "\x00")
e := utf16.Encode(r)
p := unsafe.Pointer(&e[0])
return (C.LPCWSTR)(p)
}
我正在为一个库编写 CGO 绑定,该库的所有字符串类型都使用 LPCWSTR
。我如何将 C.LPCWSTR
转换为 string
,反之亦然?
您应该能够 "cast" 将 LPCWSTR
作为 []uint16
,并使用 utf16
包解码字符
// take a C.wstr pointer, and convert it to a go slice
// `sz` is the length of the LPCWSTR
wstr := (*[1 << 30-1]uint16)(unsafe.Pointer(C.wstr))[:sz:sz]
runes := utf16.Decode(wstr)
goString := string(runes)
您通常不希望将 Go 指针传递到您的 C 代码中,因此当从字符串转换为 LPCWSTR 时,您需要在 C 中分配内存。从 Go 字符串转换的解决方案 s
可能看起来像:
func Encode(s string) C.LPCWSTR {
wstr := utf16.Encode([]rune(s))
p := C.calloc(C.size_t(len(wstr)+1), C.sizeof_uint16_t)
pp := (*[1 << 30]uint16)(p)
copy(pp[:], wstr)
return (C.LPCWSTR)(p)
}
可能还有一些 MFC 宏可以帮助与 cstrings 相互转换,您可以利用 C 中的简单包装函数。这样您就可以轻松地使用内置 C.CString
和 C.GoString
函数。
作为在 C 中分配内存的替代方法,可以调用 syscall.UTF16PtrFromString:
func Encode(s string) C.LPCWSTR {
ptr, _ := syscall.UTF16PtrFromString(s)
return C.LPCWSTR(unsafe.Pointer(ptr))
}
这假定您尝试调用的 API(假设是 win32 API,因为您使用的是 LPCWSTR)不制作任何字符串指针的副本。如果确实如此,那么在 GoLang 的代码范围合适的情况下,不分配 C 内存应该是安全的。
例如,按照下面的代码应该没问题:
func DoSomeWindowsStuff(arg string) {
CallWin32API(Encode(arg))
}
这里为字符串分配的内存应该一直存在到 CallWin32API() returns.
如果您确定输入不包含空字节,则可以进行编码 你自己:
import (
// #include <windows.h>
"C"
"unicode/utf16"
"unsafe"
)
func PtrFromString(s string) C.LPCWSTR {
r := []rune(s + "\x00")
e := utf16.Encode(r)
p := unsafe.Pointer(&e[0])
return (C.LPCWSTR)(p)
}