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

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

…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. Hope you got the idea.

Please read the manual page carefully 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).