Golang で winapi を叩く練習として GetWindowText を呼び出してみた

最初は AllenDang/w32 を検討していたが、手元の Windows 7 で動かなかった& なんか fork してる別ソース使わないとダメ(or 自分でソース直す)っぽかった、で面倒だったので、一度自分で書いてみることに。

ソース

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    user32, err := syscall.LoadDLL("user32.dll")
    if err != nil {
        panic(err)
    }
    defer user32.Release()

    procGetForegroundWindow, err := user32.FindProc("GetForegroundWindow")
    if err != nil {
        panic(err)
    }
    hwnd, _, _ := procGetForegroundWindow.Call()

    procGetWindowTextLength, err := user32.FindProc("GetWindowTextLengthW")
    if err != nil {
        panic(err)
    }
    textLength, _, _ := procGetWindowTextLength.Call(hwnd)
    textLength = textLength + 1

    procGetWindowText, err := user32.FindProc("GetWindowTextW")
    if err != nil {
        panic(err)
    }
    // LPTSTR が *uint16
    buf := make([]uint16, textLength)
    procGetWindowText.Call(hwnd, uintptr(unsafe.Pointer(&buf[0])), textLength)

    text := syscall.UTF16ToString(buf)
    fmt.Println(text)
}

解説

Golang 側で winapi を叩く仕組みがあるので、これに従う。

基本的には syscall モジュールを使って DLL ロードして、そっから Proc をロードして、んで Call するという流れ。

  • winapi の構造体や型の軍団が猛威を奮う
    • 自分で MSDN 見て書いてもいいが、 w32/typedef.go この辺を見るのが楽
  • []int の string 化は syscall.UTF16ToString(buf)

感想

  • winapi 側の型とか構造体とか作り込むのだるそう。w32/typedef.go まるごとパクろうかなぁ。ライセンスは BSD
  • まずは小さなツール作成(で使う Proc のみを最小限定義)するのがよさそうか。少しずつ作って慣れる作戦
  • あまり外部には頼りたくない……(w32 含めあまり栄えてない感じだし)

参考