在windows上使用go编译dll文件,供C++调用

  1. C++项目是win32的,所以go的编译环境也要改成win32的
cmd下,修改环境变量:
set GOARCH=386
set CGO_ENABLED=1
使用go env 查看是否生效

参考:https://bbs.csdn.net/topics/394513992.
2. 安装编译环境
MinGW下载安装gcc,g++编译器
参考:https://blog.csdn.net/cbb944131226/article/details/82940273
3. 编写go相关文件和代码
编写def文件
比如我要编译的dll文件,导出函数为GetIP
那么编写一个 godll.def (名字随便起)
godll.def

EXPORTS
    GetIP
package main

import "C"

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/hex"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"strings"
	"time"
)

func EncryptDES_ECB(src, key string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	//对明文数据进行补码
	data = PKCS5Padding(data, bs)
	if len(data)%bs != 0 {
		panic("Need a multiple of the blocksize")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		//对明文按照blocksize进行分块加密
		//必要时可以使用go关键字进行并行加密
		block.Encrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	return fmt.Sprintf("%X", out)
}


func DecryptDES_ECB(src, key string) string {
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	if len(data)%bs != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		block.Decrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	out = PKCS5UnPadding(out)
	return string(out)
}


func EncryptDES_CBC(src, key string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte )
	if err != nil {
		panic(err)
	}
	data = PKCS5Padding(data , block.BlockSize())
	//获取CBC加密模式
	iv := keyByte //用密钥作为向量(不建议这样使用)
	mode := cipher.NewCBCEncrypter(block, iv)
	out := make([]byte, len(data))
	mode .CryptBlocks(out, data)
	return fmt.Sprintf("%X", out)
}


func DecryptDES_CBC(src, key string) string {
	keyByte := []byte(key)
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	iv := keyByte //用密钥作为向量(不建议这样使用)
	mode := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(data))
	mode.CryptBlocks(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext)
}


func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func Get(url string) string {
	// 超时时间:5秒
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get(url)
	defer resp.Body.Close()
	if err != nil {
		//panic(err)
		//fmt.Println(err.Error())
		return "networkError"
	}
	var buffer [512]byte
	result := bytes.NewBuffer(nil)
	for {
		n, err := resp.Body.Read(buffer[0:])
		result.Write(buffer[0:n])
		if err != nil && err == io.EOF {
			break
		} else if err != nil {
			//panic(err)
			result = bytes.NewBuffer([]byte("networkError"))
		}
	}

	return result.String()
}

//export GetIP
func GetIP(signal int32, domainParam string) *C.char {
	defer func() {
		err := recover()
		if err != nil {
			//fmt.Println(err)
		}
	}()
	if signal != 8956142 { //  做一下验证防止被 恶意调用
		return C.CString("authError")
	}
	key := "xxxxxxxwww"
	domain := "xxx.com"
	//domain := "xxxx.cn"
	enc_str := EncryptDES_ECB(domain, key)
	httpDnsUrl := "http://xxxxx/d?dn=" + enc_str + "&id=888&ttl=1"
	respTxt := Get(httpDnsUrl)
	if respTxt == "networkError" {
		return C.CString("networkError")
	}
	descStr := DecryptDES_ECB(respTxt, key)
	ips_str := strings.Split(descStr, ",")[0]
	ips_slice := strings.Split(ips_str, ";")
	ips_length := len(ips_slice)
	if ips_length == 1 {
		return C.CString(ips_slice[0])
	} else {
		rand.Seed(time.Now().Unix())
		index := rand.Intn(ips_length)
		return C.CString(ips_slice[index])
	}
}

func main() {}

注意:在要导出的函数(GetIP)上面 写上 //export GetIP, 还要有main函数

实际上我应该将 C.CString 创建的内存,释放掉。
参考:
https://blog.csdn.net/weixin_34128501/article/details/91709373
https://blog.csdn.net/liangguangchuan/article/details/52920054
https://blog.csdn.net/qq_30549833/article/details/86157744

  1. 编译dll文件
go build -buildmode=c-archive httpdns.go
gcc godll.def httpdns.a -shared -lwinmm -lWs2_32 -o httpdns.dll -Wl,--out-implib,httpdns.lib

生成 .dll .lib. h文件

  1. 用C++调用, vs2017 (需要用到上面的.dll 和.h)
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include "httpdns.h" // dll的头文件
// 其中 httpdns.h里面的
//typedef __SIZE_TYPE__ GoUintptr;
//typedef float _Complex GoComplex64;
//typedef double _Complex GoComplex128; 这三行要注释掉

// 根据httpdns.h 里面导出函数定义下面类型
typedef char*(*funcPtrGetIP)(GoInt32, GoString);
using namespace std;
int main() {
	//加载动态库
	HINSTANCE hInstance = LoadLibrary("httpdns.dll");
	funcPtrGetIP pFunc_GetIP = (funcPtrGetIP)GetProcAddress(hInstance, "GetIP");
	
	int signal = 8956142;
	char* domain = const_cast<char *>("xxx.com");
	GoString gostr_domain{ domain,(ptrdiff_t)strlen(domain) };//就是go中的string类型
	char* ipstr = pFunc_GetIP(signal, gostr_domain);
	cout << strlen(ipstr) << endl;
	cout << ipstr << endl;
	//FreeLibrary(hInstance); //release模式会崩溃,原因未知
	return 0;
}

----2020-12-29----
补充下:
关于在go中使用C.String后,内存需要释放的,写一个释放内存的接口

/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"

//export FreeDecryUserKey
func FreeDecryUserKey(pointer *C.char) {
	fmt.Println("will free pointer ")
	fmt.Println(pointer)
	C.free(unsafe.Pointer(pointer))//释放内存 必须引入stdlib.h 标准库
}

在Cpp中这样使用

#include <iostream>
#include <string>
#include <Windows.h>
#include "aesdecry.h"
using namespace std;

typedef char*(*funcPtrGetDecryUserKey)(GoString, GoString);
typedef void (*funcPtrFreeDecryUserKey)(char*);

int main() {
	std::string user_base64_key = "1a07b51b220c5083ede4903cf0e1da88823e8134eb81b6a78396234a6de8d06de6f94a55d0e8762849ae58c70d436217";
	HINSTANCE hInstance = LoadLibrary("main.dll");
	funcPtrGetDecryUserKey pFunc_GetDecryUserKey = (funcPtrGetDecryUserKey)GetProcAddress(hInstance, "GetDecryUserKey");
	
	funcPtrFreeDecryUserKey pFunc_FreeDecryUserKey = (funcPtrFreeDecryUserKey)GetProcAddress(hInstance, "FreeDecryUserKey");

	char* encry_data = const_cast<char *>(user_base64_key.c_str());
	char* password = const_cast<char *>("aa6e8b08e4db270c");
	GoString gostr_encry_data{ encry_data,(ptrdiff_t)strlen(encry_data) };//就是go中的string类型
	GoString gostr_password{ password,(ptrdiff_t)strlen(password) };//就是go中的string类型
	char* real_user_key = pFunc_GetDecryUserKey(gostr_encry_data, gostr_password);

	printf("%x\n", real_user_key);
	printf("%p\n", real_user_key);

	std::string targetkey = real_user_key;
	cout << targetkey << endl;

	pFunc_FreeDecryUserKey(real_user_key);  // 释放掉内存
	cout << targetkey << endl;
	return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值