Golang:如何在没有cgo的情况下调用win32 API?

enu*_*ami 6 windows dll winapi go

我试图调用GetUserNameExsecur32.dll这样的:

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)
Run Code Online (Sandbox Code Playgroud)

这段代码编译得很好,但GetUserNameEx.Call()会失败.我不知道为什么我不能得到UserName.谁能帮助我?

icz*_*cza 6

size是一个输入输出参数.进行调用时,必须将其设置为buffer(arr)的大小.它的类型也是PULONG,所以在Go使用uint32.Windows PULONG类型是指向a ULONG(具有范围0..4294967295)的指针.见.

Call()返回3个值:

func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
Run Code Online (Sandbox Code Playgroud)

存储退回lastErr并打印它.你会这样做,你会发现错误:

_, _, lastErr := GetUserNameEx.Call(
    3, uintptr(unsafe.Pointer(&arr[0])), uintptr(unsafe.Pointer(&size)))

fmt.Println(lastErr)
Run Code Online (Sandbox Code Playgroud)

打印:

More data is available.
Run Code Online (Sandbox Code Playgroud)

这意味着可以获得的数据多于适合您传递的缓冲区的数据 - 或者更确切地说 - 使用输入输出参数指定的大小size(您0作为传递参数传递size).

工作代码(注意由于unicode而除以2 '\0',对于大小计算的终止字节/字符减去1 ):

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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下lastErr将是:

The operation completed successfully.
Run Code Online (Sandbox Code Playgroud)

要正确处理错误:

返回的错误始终为非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
}
// No error, proceed to print/use arr
Run Code Online (Sandbox Code Playgroud)