如何正确调用netapi32!NetSessionEnum()函数?

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

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()`释放该指针。

这里发生了什么:

  1. 变量pinfo是指向类型为SESSION_INFO_10的值的指针。

  2. 你取得了该变量中保存的值(一个指针)所占用的内存块的地址,并将其传递给NetSessionEnum()

  3. 该函数自己分配了缓冲区,并将其地址写入你传递给函数的地址所指向的内存块。

    由于你传递了pinfo变量的地址,缓冲区的地址最终被写入变量pinfo中。

  4. 然后,你使用存储在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:

  1. The variable pinfo is a pointer to a value of type SESSION_INFO_10.

  2. 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().

  3. 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 variable pinfo.

  4. You then use that address stored in pinfo to access the memory allocated by NetSessionEnum().

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).

huangapple
  • 本文由 发表于 2016年3月9日 15:27:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/35885460.html
匿名

发表评论

匿名网友

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

确定