Golang编写dll给C++调用

最近楼主学习了go语言,发现确实很方便,而且运行速度也不慢,官方自带的库也很好用,但是就是没有图形界面,所以就想使用C++写gui,然后网络什么的调用golang,于是开始尝试使用C++调用golang的dll

楼主系统是win10 64位的,使用MINGW编译C++,gcc版本64位的4.8.2,golang版本是64位的1.10.1,如果生成dll有问题可以升级下go版本

首先写一个main.go文件,包含json解析,http请求等C++写起来麻烦的方法,main函数中测试了golang结构体内存布局,发现和c++是一样的

详细我在注释里面都有写

package main

//
import "C"
import (
	"encoding/json"
	"fmt"
	"github.com/axgle/mahonia"
	"unsafe"
	"net/http"
	"strings"
	"io/ioutil"
)

//export Sum
func Sum(a int, b int) int { //最简单的计算和
	return a + b
}

//export Show
func Show(str string) { //显示传入的字符串
	fmt.Print(str)
}

//export ToNewGBKCStr
func ToNewGBKCStr(str string, c **C.char) { //生成gbk字符串
	enc := mahonia.NewEncoder("GBK")
	output := enc.ConvertString(str)
	*c = C.CString(output)
}

//export HttpGet
func HttpGet(url string,c **C.char){//http请求
	//生成client 参数为默认
	client := &http.Client{}
	//提交请求
	reqest, err := http.NewRequest("GET", url, strings.NewReader(""))
	reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if err != nil {
		fmt.Println(err)
		return
	}
	//处理返回结果
	response, err := client.Do(reqest)
	if err != nil {
		fmt.Println(err)
		return
	}
	buf,err:=ioutil.ReadAll(response.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	retstr:=string(buf)
	ToNewGBKCStr(retstr,c)
}

//export SetMyPoint
func SetMyPoint(p uintptr) { //修改传入的结构体
	mp := (*MyPoint)(unsafe.Pointer(p)) //将指针p强制转换为MyPoint类型
	mp.Name = "名称:" + mp.Name
	mp.X = 1 + mp.X
	mp.Y = 2 + mp.Y
}

//export JsonToMyPoint
func JsonToMyPoint(str string, p uintptr) {
	mp := (*MyPoint)(unsafe.Pointer(p))
	json.Unmarshal([]byte(str), mp)
}

type MyPoint struct {
	Name string `json:"name"`
	X    int    `json:"x"`
	Y    int    `json:"y"`
}

func main() {
	//测试golang结构体内存布局
	mp := MyPoint{Name: "测试", X: 1, Y: 2}
	p := uintptr(unsafe.Pointer(&mp))     //强制转换为指针
	name := *(*string)(unsafe.Pointer(p)) //将指针强制转换为string
	fmt.Println(name)

	//64位系统上string占16字节,前8字节是字符串的起始地址,后8字节为字符串占的字节数
	p += unsafe.Sizeof(string(""))
	x := *(*int)(unsafe.Pointer(p)) //将指针强制转换为int
	fmt.Println(x)

	p += unsafe.Sizeof(int(0)) //64位系统上int占8字节
	y := *(*int)(unsafe.Pointer(p))
	fmt.Println(y)
}

然后在这个文件目录下新建一个bat文件,输入

@echo off
set filepath=%1
set dllpath=%~dp1%~n1.dll

IF NOT EXIST "%filepath%" (
	goto err
) ELSE (
	goto make
)

:err
echo 文件不存在!
set/p exepath=请拖拽文件这里或输入文件路径然后按下回车
IF NOT EXIST "%filepath%" (
	goto err
) ELSE (
	goto make
)

:make
echo 编译 %~nx1 -^> %~n1.dll
call go build -ldflags "-s -w" -buildmode=c-shared -o "%dllpath%" "%filepath%"
echo 编译完成!按下回车关闭窗口

:end
pause

把main.go文件拖上去就会自动生成main.dll和main.h

接下来编写一个mian.cpp来调用这些函数

#include 
#include 
#include 
#include "main.h"

struct MyPoint{//类型和顺序必须和go中的MyPoint对应
	GoString Name;
	long long X;
	long long Y;
};

void dump(char *d, size_t len, const char* file) {
	FILE *fp = nullptr;
	fopen_s(&fp, file, "w+b");
	fwrite(d, sizeof(char), len, fp);
	fclose(fp);
}

void test(){
	printf("Sum(1,2)=%d\n",Sum(1,2));
	
	const char* str="测试字符串\n";
	GoString gstr{str,(ptrdiff_t)strlen(str)};//就是go中的string类型
	Show(gstr);
	//char* cgstr = (char*)malloc(gstr.n + 1);
	//if (!cgstr) { /* handle allocation failure */ }
	//memcpy(cgstr, gstr.p, gstr.n);
	//cgstr[gstr.n] = '\0';
	//dump(cgstr,gstr.n,"1.txt");//dump出来用notepad++打发现是utf-8编码
	//free(cgstr);
	
	MyPoint mp{gstr,0,0};
	SetMyPoint((GoUintptr)&mp);
	printf("%d,%d\n",mp.X,mp.Y);
	
	char* pname=nullptr;
	ToNewGBKCStr(mp.Name,&pname);
	printf("%s\n",pname);
	free(pname);
	pname=nullptr;
	
}
void testhttp(){
	const char* str="http://www.baidu.com";
	GoString gstr{str,(ptrdiff_t)strlen(str)};
	char* res=nullptr;
	HttpGet(gstr,&res);
	printf("%s\n",res);
	free(res);
	res=nullptr;
}
void testjson(){
	const char* str=R"_TWT_({"name":"来自json的点","x":100,"y":200})_TWT_";//C++11中的raw string,防止转义
	GoString gstr{str,(ptrdiff_t)strlen(str)};
	MyPoint mp;
	JsonToMyPoint(gstr,(GoUintptr)&mp);
	char* pname=nullptr;
	ToNewGBKCStr(mp.Name,&pname);
	printf("%s,%d,%d\n",pname,mp.X,mp.Y);
	free(pname);
	pname=nullptr;
}
int main(){
	testhttp();
	test();
	testjson();
	
	getchar();
	return 0;
}
再编写一个bat,输入以下内容
call g++ -c main.cpp -o main.o -g -std=c++11
call g++ main.o main.dll -o 1.exe -g -std=c++11
call 1.exe
pause

双击即可一键编译运行


结果都是正确的,可以愉快地玩耍了

所有文件下载:https://github.com/sleepinging/StudyGolang/tree/master/Test/win32/DllTest

转载请注明出处:https://blog.csdn.net/rtduq/article/details/80340461



你可能感兴趣的:(Go学习,C语言学习)