ひょんなことから golang で dll を動的に呼び出さなくてならなくて、引数として構造体のポインタを渡さなくちゃならなかったんだけど、調べてもよくわからんかったのでメモしておく。
dll 呼び出し側は以下のようなC言語の実装になっているとする。
#include "stdio.h" typedef struct struct1 { int x; int y; int ret; char name[8]; } struct1; int add0(int x, int y) { int ret; ret = x + y; return ret; } void add1(int x, int y, int *ret) { *ret = x + y; return; } void add2(struct1 *args) { args->ret = args->x + args->y; sprintf(args->name, "struct1"); return; }
golang から dll を呼び出すためにまず dll をLoadDLL
でロードする必要がある。そのあと、関数を FindProc
で golang の変数にバインドして Call
メソッドでその関数を呼び出すんだが、渡す引数をすべて uintptr
にしなければならない。Goの型を uintptr
にするには変数のポインタを unsafe.Pointer
で一度ラップしてからそれをさらに uintptr
でラップすることでできる。ただ、配列や構造体は変数のポインタを渡してもちゃんと渡されなくて、第一要素のポインタを渡す必要があるようだ。
例えば var a [10]int
のような場合は uintptr(unsafe.Pointer(&a))
ではなく uintptr(unsafe.Pointer(&a[0])
とする必要がある。構造体の場合の例では
var b struct { x int y int }
このような構造体があったとしたら、uintptr(unsafe.Pointer(&b.x))
とすることでポインタが渡る。(ただ、複雑な構造体の場合は単純には渡せない可能性があり、その場合はどうするのかはまだ解明不足)
上記を踏まえたうえで、先の dll 内の関数を呼び出す golang のコードは以下になる。
package main import ( "C" "fmt" "log" "syscall" ) import "unsafe" type struct1 struct { x int32 y int32 ret int32 name [8]uint8 } func main() { dll, err := syscall.LoadDLL("a.dll") if err != nil { log.Fatal("LoadDLL: ", err) } defer dll.Release() add0, err := dll.FindProc("add0") if err != nil { log.Fatal("FindProc(add2): ", err) } add1, err := dll.FindProc("add1") if err != nil { log.Fatal("FindProc(add2): ", err) } add2, err := dll.FindProc("add2") if err != nil { log.Fatal("FindProc(add2): ", err) } ret0, _, _ := add0.Call(1, 2) fmt.Println("add0:") fmt.Println(" ", ret0) var ret1 int add1.Call(1, 2, uintptr(unsafe.Pointer(&ret1))) fmt.Println("add1:") fmt.Println(" ", ret1) var ret2 struct1 ret2.x = 1 ret2.y = 2 add2.Call(uintptr(unsafe.Pointer(&ret2.x))) fmt.Println("add2:") fmt.Println(" ", ret2.x) fmt.Println(" ", ret2.y) fmt.Println(" ", ret2.ret) fmt.Println(" ", string(ret2.name[:])) }
以上