Go调用DLL

来源:互联网 发布:java识别屏幕数字 编辑:程序博客网 时间:2024/06/11 05:48

Go的标准包syscall提供了调用DLL所需的API。本例用到的API如下(仅列出重要部分):

// LoadDLL 和 MustLoadDLL 根据DLL名字 name 来加载DLL,返回值为 syscall.DLL 结构体指针。// LoadDLL 返回 error 表示错误。MustLoadDLL 错误时调用 panicfunc LoadDLL(name string) (*DLL, error)func MustLoadDLL(name string) *DLL// syscall.DLL 结构体表示 DLL 抽象type DLL struct {    // ...}// FindProc 和 MustFindProc 根据函数名字 name 加载 DLL 中的函数// 返回值为 syscall.Proc 结构体指针。// FindProc 返回 eror 表示错误。MustFindProc 错误时调用 panicfunc (d *DLL) FindProc(name string) (proc *Proc, err error)func (d *DLL) MustFindProc(name string) *Proc// syscall.Proc 结构体表示 DLL 中的函数type Proc struct {    // ...}// Call 方法调用 syscall.Proc 表示的函数,参数为 uintptr 切片,返回值为 r1, r2 和 error// C 函数只能有一个返回值,因此r2用不到。// error表示错误,该错误永远不为nil,它是由GetLastError()构成的func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)// 将字符串转换为byte指针func StringBytePtr(s string) *byte

下面是一个完整的例子:

首先实现一个DLL,定义了两个函数,一个从Go接收参数,一个返回参数给Go:

// test.h#ifndef TEST_H#define TEST_H#ifdef TEST_DLL_EXPORT#define TEST_API __declspec(dllexport)#else#define TEST_API __declspec(dllimport)#endifTEST_API void greet(char *name);TEST_API char *name();#endif
// test.c#define TEST_DLL_EXPORT#include "test.h"#include <stdio.h>#include <stdlib.h>#include <string.h>void greet(char *name){    printf("Hello, %s!\n", name);}char *name(){    char buf[] = "Gopher";    char *n = malloc(strlen(buf) + 1);    strcpy(n, buf);    n[strlen(buf)] = '\0';    return n;}

下面是Go调用DLL的代码:

package mainimport (    "fmt"    "syscall"    "unsafe")func main() {    dll := syscall.MustLoadDLL("test.dll")    procGreet := dll.MustFindProc("greet")    procGreet.Call(uintptr(unsafe.Pointer(syscall.StringBytePtr("Cynhard"))))    procName := dll.MustFindProc("name")    r, _, _ := procName.Call()    // 获取C返回的指针。    // 注意C返回的r为char*,对应的Go类型为*byte    p := (*byte)(unsafe.Pointer(r))    // 定义一个[]byte切片,用来存储C返回的字符串    data := make([]byte, 0)    // 遍历C返回的char指针,直到 '\0' 为止    for *p != 0 {        data = append(data, *p)  // 将得到的byte追加到末尾        r += unsafe.Sizeof(byte(0))  // 移动指针,指向下一个char        p = (*byte)(unsafe.Pointer(r))  // 获取指针的值,此时指针已经指向下一个char    }    name := string(data)  // 将data转换为字符串    fmt.Printf("Hello, %s!\n", name)}

注意从C中返回的指针是由malloc动态分配的,Go中不会对此指针进行引用计数,不会被垃圾回收,因此会造成内存泄漏。解决方法是在DLL中提供释放资源的接口,在Go中调用此接口。

注:本例中用到的低级编程知识请见:Go低级编程

原创粉丝点击