標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

【解決編】UNCパスの補完機能の強化のためにコンピューター名一覧を出したいんだが、違うそうじゃない

記事を書いたら、自己解決する法則でもあるのかなぁ。

// +build run

package main

import (
    "fmt"
    "unsafe"

    "golang.org/x/sys/windows"
)

const RESOURCE_CONNECTED = 1
const RESOURCE_CONTEXT = 5
const RESOURCE_GLOBALNET = 2
const RESOURCE_REMEMBERED = 3
const RESOURCETYPE_ANY = 0
const RESOURCETYPE_DISK = 1
const RESOURCETYPE_PRINT = 2
const RESOURCEDISPLAYTYPE_NETWORK = 6
const RESOURCEUSAGE_CONNECTABLE = 1
const RESOURCEUSAGE_CONTAINER = 2
const RESOURCEUSAGE_ATTACHED = 16
const RESOURCEUSAGE_ALL = 19
const ERROR_NO_MORE_ITEMS = 259

var mpr = windows.NewLazySystemDLL("mpr.dll")
var procWNetOpenEnum = mpr.NewProc("WNetOpenEnumW")
var procWNetEnumResource = mpr.NewProc("WNetEnumResourceW")
var procWNetCloseEnum = mpr.NewProc("WNetCloseEnum")

type netresourceT struct {
    Scope       uint32
    Type        uint32
    DisplayType uint32
    Usage       uint32
    LocalName   *uint16
    RemoteName  *uint16
    Comment     *uint16
    Provider    *uint16
}

func u2str(u *uint16) string {
    if u == nil {
        return ""
    }
    buffer := make([]uint16, 0, 100)
    for *u != 0 {
        buffer = append(buffer, *u)
        u = (*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(u)) + 2))
    }
    return windows.UTF16ToString(buffer)
}

func WNetEnum(nr *netresourceT, handler func(localName string, remoteName string)) error {
    var handle uintptr

    // localNameW,err := windows.UTF16PtrFromString("localhost")
    // if err != nil {
    // return err
    // }

    rc, _, err := procWNetOpenEnum.Call(
        RESOURCE_GLOBALNET,
        RESOURCETYPE_ANY,
        0,
        uintptr(unsafe.Pointer(nr)),
        uintptr(unsafe.Pointer(&handle)))
    if rc != windows.NO_ERROR {
        return fmt.Errorf("NetOpenEnum: %s", err)
    }
    defer procWNetCloseEnum.Call(handle)
    for {
        var buffer [16*1024]byte
        count := int32(-1)
        size := len(buffer)
        rc, _, err := procWNetEnumResource.Call(
            handle,
            uintptr(unsafe.Pointer(&count)),
            uintptr(unsafe.Pointer(&buffer[0])),
            uintptr(unsafe.Pointer(&size)))

        if rc == windows.NO_ERROR {
            println("open")
            for i := int32(0) ; i < count ; i++ {
                var p *netresourceT
                p = (*netresourceT)(unsafe.Pointer(&buffer[ uintptr(i)*unsafe.Sizeof(*p) ]))
                handler(u2str(p.LocalName), u2str(p.RemoteName))
                WNetEnum(p,handler)
            }
            println("close")
        } else if rc == ERROR_NO_MORE_ITEMS {
            return nil
        } else {
            return fmt.Errorf("NetEnumResource: %s", err)
        }
    }
}

func main() {
    err := WNetEnum(nil,func(localName, remoteName string) {
        println(localName, " -> ", remoteName)
    })
    if err != nil {
        println(err.Error())
    }
}

// https://msdn.microsoft.com/ja-jp/library/cc447030.aspx
// http://eternalwindows.jp/security/share/share06.html

要は再帰的に呼ばないとダメというわけですね

$ go run eachnetdrive_run.go
open
  ->  Microsoft Terminal Services
  ->  Microsoft Windows Network
open
  ->  MICROSOFTACCOUNT
  ->  WORKGROUP
open
  ->  \\ATERM-71C395
  ->  \\DESKTOP-LGGUCRA
open
  ->  \\DESKTOP-LGGUCRA\tmp
close
close
close
  ->  Web Client Network
close