Go调用C API并设置回调函数

Go调用 C API并设置回调函数

一听这名字,就些许骚气,耗费大半天饱读度娘,试过一个个坑,终于哭尽肝来!

环境坑(编译器GoLand)

  • 工程文件必须在GOPATH环境变量目录下
    Go调用C API并设置回调函数_第1张图片
    否则,编译无输出,一位写Go的胸得说的。
  • 记住"Run kind"模式设置为“Package”模式
    Go调用C API并设置回调函数_第2张图片
    切记不要选"file"模式,因为“Export” Go方法,和Go中得C语言会单独编译为小文件模块,要不会出现不可以思议得编译错误,百思不得其姐。

Go编写回调

//export showMsg
func showMsg(msg *C.char,len int)  {
	fmt.Println("show msg in go:",C.GoString(msg))
}
//export showMsg

这个不是注释,写C/C++的人儿注意了,这是Go方法导出为C方法的金钥匙!

Go中写真正的C回调

Go调用C API并设置回调函数_第3张图片
以下这个不是注释,而且和import "C"不能有空行。

/*
.....
*/

这里的showMsg(s,len)就是上一步export的Go方法。

动态调用C动态库Dll,设置回调

func init() {
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	dllfile := fmt.Sprintf(`%s\test.dll`, dir)
	lib := syscall.NewLazyDLL(dllfile)
	setcallback = lib.NewProc("setCallback")
}

func main() {
	fmt.Print("start\n")
	C.sayHello()
	_, _, err := setcallback.Call((uintptr)(unsafe.Pointer(C.showMsg_cgo)))
	if err != syscall.Errno(0) {
		fmt.Println(`调用出错,错误原因:%v`, err)
	}
	select
	{}
}

C/C++动态库导出函数和代码

#ifdef DLL1_EXPORTS
#define DLL1_API extern "C" __declspec(dllexport)
#else
#define DLL1_API __declspec(dllimport)
#endif

typedef  void ShowInfo(char *str, int len);

DLL1_API void setCallback(ShowInfo * cb);
#include "Dll1.h"
#include 
#include 

using namespace std::chrono;

ShowInfo *  g_showInfo = nullptr;
int			g_cnt      = 0;

DLL1_API void setCallback(ShowInfo * cb)
{
	g_showInfo = cb;
	if (g_cnt == 0) {
		std::thread([&]() {
			while (1) {
				if (g_showInfo) {
					char buf[255] = { 0 };
					sprintf_s(buf, "say hello in C %d", ++g_cnt);
					g_showInfo(buf, strlen(buf));
				}
			}
		}).detach();
	}
}

结果截图

Go调用C API并设置回调函数_第4张图片

Go完整代码

  • main.go
package main
/*
void showMsg_cgo(char *s,int len);
*/
import "C"
import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"unsafe"
)
var (
	setcallback   *syscall.LazyProc // 初始化,path为库配置文件config.xml路径
)

//export showMsg
func showMsg(msg *C.char,len int)  {
	fmt.Println("show msg in go:",C.GoString(msg))
}

func init() {
	dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	dllfile := fmt.Sprintf(`%s\test.dll`, dir)
	lib := syscall.NewLazyDLL(dllfile)
	setcallback = lib.NewProc("setCallback")
}

func main() {
	fmt.Print("start\n")
	C.sayHello()
	_, _, err := setcallback.Call((uintptr)(unsafe.Pointer(C.showMsg_cgo)))
	if err != syscall.Errno(0) {
		fmt.Println(`调用出错,错误原因:%v`, err)
	}
	select
	{}
}

  • bridge.go
package main
/*
#include 

void showMsg_cgo(char *s ,int len)
{
	showMsg(s,len);
}
*/
import "C"

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