2016/09/26

Recent entries from same category

  1. Go言語で Lisp 処理系を作った。
  2. Go 言語のスライス挿入ベンチマーク
  3. Go 言語で変数のシャドウイングを避けたいなら shadow を使おう。
  4. Go 言語の struct の実体を引数で(なるべく)渡せない様にするテクニック
  5. Oracle Cloud Function でしりとりした。

golang で簡単に DLL を呼び出す方法は syscall.NewLazyDLL を使う事です。

package main

var (
    times = 0
    dll   = syscall.NewLazyDLL("mydll.dll")
    proc  = dll.NewProc("MyFunc")
)

func main() {
    i := int32(123)
    proc.Call(uintptr(unsafe.Pointer(&i)))
}

ですが Call の引数は全て uintptr なので間違いが起きやすくなります。

※余談ですが cgo だと Go のポインタをC側に渡す事は出来ない(panicする)のですが、DLL の場合は許されています。

そこで golang には mksyscall_windows.go というツールが付属していいます。例えば Windows の API、FormatMessage を呼び出したいとします。

FormatMessage function (Windows)
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx

MSDN のサイトを見ながら golang の型に合うよう、以下の様に記述します。この例ではファイル名は syscall_windows.go であるとします。

package main

//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go

//sys   FormatMessage(flags uint32, source syscall.Handle, messageID uint32, languageID uint32, buffer *byte, bufferSize uint32, arguments uintptr) (numChars uint32, err error) = kernel32.FormatMessageW

そしてコンソールから go generate を実行すれば以下の zsyscall_windows.go というファイルが生成されます。

// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT

package main

import (
    "syscall"
    "unsafe"

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

var _ unsafe.Pointer

// Do the interface allocations only once for common
// Errno values.
var (
    errERROR_IO_PENDING error = syscall.Errno(ERROR_IO_PENDING)
)

// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
    switch e {
    case 0:
        return nil
    case ERROR_IO_PENDING:
        return errERROR_IO_PENDING
    }
    // TODO: add more here, after collecting data on the common
    // error values see on Windows. (perhaps when running
    // all.bat?)
    return e
}

var (
    modkernel32 = windows.NewLazySystemDLL("kernel32.dll")

    procFormatMessageW = modkernel32.NewProc("FormatMessageW")
)

func FormatMessage(flags uint32, source syscall.Handle, messageID uint32, languageID uint32, buffer *byte, bufferSize uint32, arguments uintptr) (numChars uint32, err error) {
    r0, _, e1 := syscall.Syscall9(procFormatMessageW.Addr(), 7uintptr(flags), uintptr(source), uintptr(messageID), uintptr(languageID), uintptr(unsafe.Pointer(buffer)), uintptr(bufferSize), uintptr(arguments), 00)
    numChars = uint32(r0)
    if numChars == 0 {
        if e1 != 0 {
            err = errnoErr(e1)
        } else {
            err = syscall.EINVAL
        }
    }
    return
}

エディタや IDE からも補完できるので渡す引数の型が分かりやすくなります。

DLLの呼び出し

あとはプログラムから FormatMessage を呼び出すだけになりますが、Windows の API はプログラムを落とす事もできる危険な物もあります。internal パッケージを使って外部のパッケージからは見えない様にしておきましょう。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太, 稲葉貴洋
技術評論社 大型本 / ¥899 (2016年09月09日)
 
発送可能時間:

Posted at by | Edit


blog comments powered by Disqus