英文:
Golang: how can I call win32 API without cgo?
问题
我正在尝试调用secur32.dll
中的GetUserNameEx
函数,代码如下:
dll, err := syscall.LoadDLL("secur32.dll")
if err != nil {
log.Fatal(err)
}
defer dll.Release()
GetUserNameEx, err := dll.FindProc("GetUserNameExW")
if err != nil {
log.Fatal(err)
}
arr := make([]uint8, 256)
var size uint
GetUserNameEx.Call(3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(arr)
fmt.Println(size)
这段代码可以正常编译,但是GetUserNameEx.Call()
会失败。我不知道为什么无法获取UserName
。有人可以帮帮我吗?
英文:
I'm trying to call GetUserNameEx
from secur32.dll
like this:
dll, err := syscall.LoadDLL("secur32.dll")
if err != nil {
log.Fatal(err)
}
defer dll.Release()
GetUserNameEx, err := dll.FindProc("GetUserNameExW")
if err != nil {
log.Fatal(err)
}
arr := make([]uint8, 256)
var size uint
GetUserNameEx.Call(3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(arr)
fmt.Println(size)
This code compiles fine, but GetUserNameEx.Call()
will fail. I don't know why I cannot get UserName
. Could anyone help me?
答案1
得分: 6
size
是一个输入输出参数。在调用时,你需要将它设置为缓冲区(arr
)的大小。它的类型是PULONG
,在Go语言中使用uint32
。Windows的PULONG
类型是指向ULONG
的指针(取值范围为0到4294967295)。参见来源。
另外,Call()
函数返回3个值:
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
存储返回的lastErr
并打印它。如果你这样做了,你会更早地发现错误:
_, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(lastErr)
打印结果为:
More data is available.
这意味着有更多的数据可用,超过了你传递的缓冲区的容量,或者说超过了你通过输入输出参数size
指定的大小(你传递的是0
作为size
)。
以下是可工作的代码(注意由于Unicode的缘故,需要除以2,减去1是为了计算终止的'\0'
字节/字符的大小):
arr := make([]uint8, 256)
var size uint32 = uint32(len(arr)) / 2 - 1
_, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(lastErr)
fmt.Println(string(arr))
fmt.Println(arr)
fmt.Println(size)
在这种情况下,lastErr
将是:
The operation completed successfully.
要正确处理错误:
返回的错误始终是非
nil
的,由GetLastError
的结果构造而成。在查询错误之前,调用者必须检查主要的返回值,以决定是否发生了错误(根据调用的具体函数的语义)。错误将保证包含syscall.Errno
。
示例:
r1, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
if r1 == 0 {
fmt.Println("ERROR:", lastErr.Error())
return
}
// 没有错误,继续打印/使用arr
英文:
size
is an in-out parameter. When you make the call, you have to set it to the size of the buffer (arr
). Also its type is PULONG
, so in Go use uint32
. Windows PULONG
type is a pointer to a ULONG
(which has range 0..4294967295
). See source.
Also Call()
returns 3 values:
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
Store the returned lastErr
and print it. Would you have done so, you would find the error earlier:
_, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(lastErr)
Prints:
More data is available.
This means more data is available than what fits into the buffer you pass - or rather - by the size you indicated with the in-out parameter size
(you passed 0
as the size
).
Working code (note the division by 2 due to unicode and minus 1 for the terminating '\0'
byte / character for the size calculation):
arr := make([]uint8, 256)
var size uint32 = uint32(len(arr)) / 2 - 1
_, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
fmt.Println(lastErr)
fmt.Println(string(arr))
fmt.Println(arr)
fmt.Println(size)
In this case lastErr
will be:
The operation completed successfully.
To properly handle error:
> The returned error is always non-nil
, constructed from the result of GetLastError
. Callers must inspect the primary return value to decide whether an error occurred (according to the semantics of the specific function being called) before consulting the error. The error will be guaranteed to contain syscall.Errno
.
Example:
r1, _, lastErr := GetUserNameEx.Call(
3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))
if r1 == 0 {
fmt.Println("ERROR:", lastErr.Error())
return
}
// No error, proceed to print/use arr
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论