英文:
How do I properly call netapi32!NetSessionEnum()?
问题
我一直在尝试使用netapi32.dll进行编程,但结果不尽如人意。
以下部分按预期工作:
type SERVER_INFO_101 struct {
PlatformID uint32
Name *uint16
VersionMajor uint32
VersionMinor uint32
Type uint32
Comment *uint16
}
func NetServerGetInfo() {
info := &SERVER_INFO_101{}
ret, _, err := procNetServerGetInfo.Call(0, 101, uintptr(unsafe.Pointer(&info)))
if ret != 0 {
log.Fatal(err)
}
spew.Dump(info)
}
然而,我不确定为什么在unsafe.Pointer
内部还需要使用&
。
以下部分不起作用,我无法找出原因。没有抛出任何错误代码。结构体和变量都没有被填充。
type SESSION_INFO_10 struct {
Cname *uint16
Username *uint16
Time uint32
IdleTime uint32
}
func NetSessionEnum() {
info := &SESSION_INFO_10{}
var prefmaxlen int32 = -1
var entriesread uint32
var totalentries uint32
var resumehandle uint32
x, y, z := procNetSessionEnum.Call(0, 0, 0, 10, uintptr(unsafe.Pointer(info)), uintptr(prefmaxlen), uintptr(unsafe.Pointer(&entriesread)), uintptr(unsafe.Pointer(&totalentries)), uintptr(unsafe.Pointer(&resumehandle)))
fmt.Println(x, y, z)
fmt.Println(entriesread, totalentries)
spew.Dump(info)
}
英文:
I'v been trying to play with netapi32.dll, but I'm having mixed results.
The following works as expected
type SERVER_INFO_101 struct {
PlatformID uint32
Name *uint16
VersionMajor uint32
VersionMinor uint32
Type uint32
Comment *uint16
}
func NetServerGetInfo() {
info := &SERVER_INFO_101{}
ret, _, err := procNetServerGetInfo.Call(0, 101, uintptr(unsafe.Pointer(&info)))
if ret != 0 {
log.Fatal(err)
}
spew.Dump(info)
}
However, I'm not sure why info has to have & inside the unsafe.Pointer also.
The following does not work, and I can't seem to find out why. No error codes get thrown. Neither the struct or variables gets filled out.
type SESSION_INFO_10 struct {
Cname *uint16
Username *uint16
Time uint32
IdleTime uint32
}
func NetSessionEnum() {
info := &SESSION_INFO_10{}
var prefmaxlen int32 = -1
var entriesread uint32
var totalentries uint32
var resumehandle uint32
x, y, z := procNetSessionEnum.Call(0, 0, 0, 10, uintptr(unsafe.Pointer(info)), uintptr(prefmaxlen), uintptr(unsafe.Pointer(&entriesread)), uintptr(unsafe.Pointer(&totalentries)), uintptr(unsafe.Pointer(&resumehandle)))
fmt.Println(x, y, z)
fmt.Println(entriesread, totalentries)
spew.Dump(info)
}
答案1
得分: 1
因为你不应该在那里传递指向你的内存块的指针,这是因为手册中的引用:
这个缓冲区是由系统分配的,必须使用NetApiBufferFree函数释放。
那个指针的类型是误导性的,但你应该在那里传递一个指向指针的指针,类似这样:
func NetSessionEnum() {
var pinfo *SESSION_INFO_10
var prefmaxlen int32 = -1
var entriesread uint32
var totalentries uint32
var resumehandle uint32
x, y, z := procNetSessionEnum.Call(0, 0, 0, 10,
uintptr(unsafe.Pointer(&pinfo)), uintptr(prefmaxlen),
uintptr(unsafe.Pointer(&entriesread)),
uintptr(unsafe.Pointer(&totalentries)),
uintptr(unsafe.Pointer(&resumehandle)))
fmt.Println(x, y, z)
fmt.Println(entriesread, totalentries)
spew.Dump(info)
}
// 现在使用`*pinfo.Cname`等
// 别忘了以后调用`NetApiBufferFree()`释放该指针。
这里发生了什么:
-
变量
pinfo
是指向类型为SESSION_INFO_10
的值的指针。 -
你取得了该变量中保存的值(一个指针)所占用的内存块的地址,并将其传递给
NetSessionEnum()
。 -
该函数自己分配了缓冲区,并将其地址写入你传递给函数的地址所指向的内存块。
由于你传递了
pinfo
变量的地址,缓冲区的地址最终被写入变量pinfo
中。 -
然后,你使用存储在
pinfo
中的地址来访问NetSessionEnum()
分配的内存。
这被称为“双重间接”,在Win32 API的许多地方都使用到。请阅读手册页面并研究其中包含的代码示例。
**更新:**事实证明,原始代码还存在更多问题,所以我花时间提供了完整的解决方案- 这里是代码片段(在Windows XP 32位、Windows 2003 R2 64位和Windows 8.1 64位上使用Go 1.6 amd64和i386进行了测试)。
英文:
…because you're not supposed to pass a pointer to your memory block there—to cite the manual:
> This buffer is allocated by the system and must be freed using the NetApiBufferFree function.
The type of that pointer is misleading but you're supposed to pass a pointer to a pointer there, something like this:
func NetSessionEnum() {
var pinfo *SESSION_INFO_10
var prefmaxlen int32 = -1
var entriesread uint32
var totalentries uint32
var resumehandle uint32
x, y, z := procNetSessionEnum.Call(0, 0, 0, 10,
uintptr(unsafe.Pointer(&pinfo)), uintptr(prefmaxlen),
uintptr(unsafe.Pointer(&entriesread)),
uintptr(unsafe.Pointer(&totalentries)),
uintptr(unsafe.Pointer(&resumehandle)))
fmt.Println(x, y, z)
fmt.Println(entriesread, totalentries)
spew.Dump(info)
}
// Now use `*pinfo.Cname` etc
// Don't forget to later call `NetApiBufferFree()` on that pointer.
What happens here:
-
The variable
pinfo
is a pointer to a value of typeSESSION_INFO_10
. -
You take the address of the memory block occupied by the value kept in that variable (which is a pointer) and pass it to
NetSessionEnum()
. -
That function allocates the buffer by itself and writes its address to the memory block pointed to by the address you have passed to the function.
Since you've passed an address of the
pinfo
variable, the address of the buffer ends up being written into the variablepinfo
. -
You then use that address stored in
pinfo
to access the memory allocated byNetSessionEnum()
.
That's called "double indirection" and is used in quite many places of Win32 API. Please read the manual page and study the code example it includes.
Update: as it turned out, there were more problems with the original code so I've took time to provide full solution—here is the gist (tested with Go 1.6 amd64 and i386 on Windows XP 32-bit, Windows 2003 R2 64-bit and Windows 8.1 64-bit).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论