Golang:如何在不使用cgo的情况下调用Win32 API?

huangapple go评论86阅读模式
英文:

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

huangapple
  • 本文由 发表于 2015年11月14日 21:42:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/33709033.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定