Golang与DLL交互

Golang与DLL交互

在系统级编程中,经常需要使用C/C++来编写模块给Go调用,目前官方支持这种调用,但是需要CGO做支撑。
这里研究了Golang与DLL进行数据交换的几种方式,(重点是:指针,结构体,回掉函数)并做以测试。

依赖条件:

  • C接口形式的DLL(这里使用了VS2015编写)
  • CGO依赖的GCC(这里使用Qt5.9自带的Minigw)

1. DLL代码

#include "stdafx.h"
#include 

#ifndef TM_ROBOT_API
#define TM_ROBOT_API extern "C" __declspec(dllexport) 
#else
#define TM_ROBOT_API extern "C" __declspec(dllimport) 
#endif

TM_ROBOT_API void fun1(int* pVal) {
    char buff[128];
    sprintf(buff, "fun1, val: %d -> 999\n", *pVal);
    OutputDebugStringA(buff);
    *pVal = 999;    
}

struct MyStruct
{
    int nVal1;
    float nVal2;
};

typedef void(*CB_MY)(float nVal, float fVal);

TM_ROBOT_API void fun2(MyStruct* pVal) {
    char buff[128];
    sprintf(buff, "fun2, val: 1->%d, 2->%.4f -> 1,2\n", pVal->nVal1, pVal->nVal2);
    OutputDebugStringA(buff);
    pVal->nVal1 = 1;
    pVal->nVal2 = 2.2;
}

TM_ROBOT_API void fun3(CB_MY pFun) {
    char buff[128];
    sprintf(buff, "fun3, val: %08X -> call it 10s\n", pFun);
    OutputDebugStringA(buff);
    for (int i=80;i<100;i++)
    {
        pFun(i*3.3, i*1.1);
        Sleep(10);
    }
}

注意,这里的调用约定为extern "C" __declspec(dllexport)

2. Golang代码

package main

/*
// 依赖GCC
typedef struct _MyStruct
{
    int nVal1;
    float nVal2;
}MyStruct;

*/
import "C"
import (
    "fmt"
    "syscall"
    "unsafe"
)

func cb_my(val1 float32, val2 float32) int32 {
    fmt.Println("cb_my:", val1, val2)
    return 0
}

func main() {
    Handle := syscall.NewLazyDLL("gocTest.dll")
    Fun3 := Handle.NewProc("fun3")
    Fun2 := Handle.NewProc("fun2")
    Fun1 := Handle.NewProc("fun1")
    var aa int32 = 10 // var aa C.int = 10
    Fun1.Call(uintptr(unsafe.Pointer(&aa)))
    fmt.Println("########", aa)
    var arg1 C.MyStruct // 使用Go的Struct会有问题
    arg1.nVal1 = 11
    arg1.nVal2 = 22.22
    Fun2.Call(uintptr(unsafe.Pointer(&arg1)))
    fmt.Println("************", arg1)
    var fn = syscall.NewCallbackCDecl(cb_my) // 注意调用约定
    Fun3.Call(fn)
    fmt.Println("============")
}

3. 总结

  1. fun1做简单的测试就能通过,如果时float等其他类型,尽量使用CGO模块类型。
  2. fun2如果使用Golang的结构体,除了int32之外的参数都有问题,需要CGO模块类型。
  3. fun3特别注意NewCallbackCDecl/NewCallback与回掉函数的约定,否则会出很多异常。

其他高阶混合编程,这里不做涉及。

参考

https://studygolang.com/articles/1424
http://lib.csdn.net/article/go/33766

类型转换对照表:

char -->  C.char -->  byte
signed char -->  C.schar -->  int8
unsigned char -->  C.uchar -->  uint8
short int -->  C.short -->  int16
short unsigned int -->  C.ushort -->  uint16
int -->  C.int -->  int
unsigned int -->  C.uint -->  uint32
long int -->  C.long -->  int32 or int64
long unsigned int -->  C.ulong -->  uint32 or uint64
long long int -->  C.longlong -->  int64
long long unsigned int -->  C.ulonglong -->  uint64
float -->  C.float -->  float32
double -->  C.double -->  float64
wchar_t -->  C.wchar_t  -->  
void * -> unsafe.Pointer

你可能感兴趣的:(Go语言,C/C++)