本文主要实现在windows下使用go语言对C语言生成的dll库的调用。
DEMO详细地址放在: https://download.csdn.net/download/ok532655221/12527256
源代码路径:
demo1_go_win: go调用dll库方式一(vesdk.dll)
demo2_go_win: go调用dll库方式二(vesdk.dll)
GenDll_vs2013_windows:VS2013生成DLL库(GenDll.dll)
vesdk_gcc_windows:生成vesdk.dll的源代码
过程:
通常情况下,windows的下的dll库会通过VisualStudio进行编译产生,因此,最好的方案是直接调用此dll库,最能提高开发效率。查找网上的资料,基本是使用gcc编译生成dll库,并使用go进行调用,本文同时尝试了使用VS2013编译dll库进行测试,结果不是很理想,详细的源代码都放在的整个资料中,有兴趣可以自行实验。
编译环境:
windows: mingw-64.7.0.0 ,gcc-9.2.0
go版本:go1.14.4
VisualStudio 使用 vs2013编译
DLL库中设置了一个函数如下,vesdk.c文件函数如下:
(PrintHex函数为打印函数,不需要刻意去掉,此处仅为测试函数效果)
int DLL_API VESDK_SM4ECBEncryp(void *pContext, int nKeynum, unsigned char *pKey, unsigned int nKeyLen,unsigned char *pInData, unsigned int nInDataLen, unsigned char *pOutData, unsigned int *pOutDataLen)
{
int g;
printf("C language nKeynum = %d \n", nKeynum );
PrintHex("C language pKey",pKey,nKeyLen,32);
PrintHex("C language pInData",pInData,nInDataLen,32);
g = nKeynum + nKeyLen + nInDataLen;
memset(pOutData,0x33,16);
*pOutDataLen = 16;
return g;
}
vesdk.h文件如下
#ifndef _VESDK_H_
#define _VESDK_H_
#define DLL_API __declspec(dllexport)
int DLL_API VESDK_SM4ECBEncryp(void *pContext, int nKeynum, unsigned char *pKey, unsigned int nKeyLen,unsigned char *pInData, unsigned int nInDataLen, unsigned char *pOutData, unsigned int *pOutDataLen);
#endif
编译命令:
gcc -shared -fPIC -o vesdk.dll vesdk.c
因为go语言调用dll依赖于gcc进行编译,因此需要安装MinGW,下载地址:
https://sourceforge.net/projects/mingw-w64/
安装工程自行百度
安装好之后,配置环境变量,结果如下:
原因:这是MinGW是32位的,更换成64位即可解决问题。
package main
import "C"
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var ret uintptr = 0
var context = ""
key := [...]byte{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}
inData := [...]byte{0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30}
arr := [128]byte{}
len := [16]byte{}
//对应C语言中的void*
//var pContext uintptr = 0
var pContext = (uintptr)(unsafe.Pointer(&context))
var nKeynum uintptr = 1
var pKey = (uintptr)(unsafe.Pointer(&key))
var nKeyLen uintptr = 16
var pInData = (uintptr)(unsafe.Pointer(&inData))
var nInDataLen uintptr = 16
var pOutData = (uintptr)(unsafe.Pointer(&arr))
var pOutDataLen = (uintptr)(unsafe.Pointer(&len))
myDllHandle, err := syscall.LoadLibrary("vesdk.dll")
if err != nil {
fmt.Println("err",err)
}
myDllProc, err := syscall.GetProcAddress(myDllHandle, "VESDK_SM4ECBEncryp")
if err != nil {
fmt.Println("err",err)
}
var nArgs uintptr = 8
fmt.Println("nKeynum",nKeynum)
fmt.Println("pKey",pKey)
fmt.Println("nKeyLen",nKeyLen)
fmt.Println("pInData",pInData)
fmt.Println("nInDataLen",nInDataLen)
ret, _, callErr := syscall.Syscall9(myDllProc, nArgs, pContext, nKeynum, pKey, nKeyLen, pInData, nInDataLen, pOutData, pOutDataLen, 0)
if callErr != 0 {
fmt.Printf("Call myDllproc: %v\n", callErr)
}
fmt.Println("SM4ECBEncryp error ret =" , ret)
//打印0~15字节数据
fmt.Println("arr ",arr[:16])
fmt.Println("len ",len[0])
}
package main
import "C"
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var ret uintptr = 0
var context = ""
key := [...]byte{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}
inData := [...]byte{0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30}
arr := [128]byte{}
len := [16]byte{}
//对应C语言中的void*
var pContext = (uintptr)(unsafe.Pointer(&context))
var nKeynum uintptr = 1
var pKey = (uintptr)(unsafe.Pointer(&key))
var nKeyLen uintptr = 16
var pInData = (uintptr)(unsafe.Pointer(&inData))
var nInDataLen uintptr = 16
var pOutData = (uintptr)(unsafe.Pointer(&arr))
var pOutDataLen = (uintptr)(unsafe.Pointer(&len))
myLazyDLL := syscall.NewLazyDLL("vesdk.dll")
SM4ECBEncryp := myLazyDLL.NewProc("VESDK_SM4ECBEncryp")
fmt.Println("call dll: ",myLazyDLL.Name)
fmt.Println("nKeynum",nKeynum)
fmt.Println("pKey",pKey)
fmt.Println("nKeyLen",nKeyLen)
fmt.Println("pInData",pInData)
fmt.Println("nInDataLen",nInDataLen)
ret, _, _ = SM4ECBEncryp.Call(pContext, nKeynum,pKey, nKeyLen,pInData,nInDataLen,pOutData,pOutDataLen)
fmt.Println("SM4ECBEncryp error ret =" , ret)
//打印0~15字节数据
fmt.Println("arr ",arr[:16])
fmt.Println("len ",len[0])
}
详细内容本人不在赘述
使用vs2013生成的GenDll.dll库的接口形式和内容与vesdk.dll库一样,因此可以使用同一个go程序调用。
vesdk.c
#include "global.h"
int DLL_API PrintHex(char* itemName, void* pInData, unsigned int dataLength, unsigned int rowCount)
{
int i,j;
unsigned char *sourceData = (unsigned char *)pInData;
if((sourceData == NULL) || (rowCount == 0) || (dataLength == 0))
return -1;
if(itemName != NULL)
printf("%s[%d]:\n",itemName,dataLength);
for(i=0;i<(int)(dataLength/rowCount);i++)
{
printf("%08x ",i*rowCount);
for(j=0;j<(int)rowCount;j++)
{
printf("%02x ",*(sourceData+i*rowCount+j));
}
printf("\n");
}
if (!(dataLength%rowCount))
return 0;
printf("%08x ",(dataLength/rowCount)*rowCount);
for(j=0;j<(int)(dataLength%rowCount);j++)
{
printf("%02x ",*(sourceData+(dataLength/rowCount)*rowCount+j));
}
printf("\n");
return 0;
}
int DLL_API VESDK_SM4ECBEncrypt(void *pContext, int nKeynum, unsigned char *pKey, unsigned int nKeyLen,
unsigned char *pInData, unsigned int nInDataLen, unsigned char *pOutData, unsigned int *pOutDataLen)
{
printf("============start==================\n");
printf("nKeynum = %d \n", nKeynum );
PrintHex("pKey",pKey,nKeyLen,32);
PrintHex("pInData",pInData,nInDataLen,32);
memset(pOutData,0x33,16);
*pOutDataLen = 16;
printf("============end==================\n");
return 0;
}
vesdk.h
#ifndef _VESDK_H_
#define _VESDK_H_
#define DLL_API __declspec(dllexport)
int DLL_API VESDK_SM4ECBEncrypt(void *pContext, int nKeynum, unsigned char *pKey, unsigned int nKeyLen, unsigned char *pInData, unsigned int nInDataLen, unsigned char *pOutData, unsigned int *pOutDataLen);
int DLL_API PrintHex(char* itemName, void* pInData, unsigned int dataLength, unsigned int rowCount);
#endif
global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#include
#include
#include
#include
#include
#include
#include "vesdk.h"
#endif
test.c
int main()
{
int ret = -1;
void * pContext = NULL;
unsigned int nKeynum = 1;
unsigned char pKey[128] = {"1111111111111111"};
unsigned int nKeyLen = 16;
unsigned char pInData[128] = {"2222222222222222"};
unsigned int nInDataLen = 16;
unsigned char pOutData[128] = {0};
unsigned int pOutDataLen = 0;
ret = VESDK_SM4ECBEncrypt(&pContext, nKeynum,pKey,nKeyLen,pInData,nInDataLen,pOutData,&pOutDataLen);
if(ret != 0 )
{
printf(" ************VESDK_SM4ECBEncryp error ret = %d \n", ret);
}
else
{
printf("pOutDataLen = %d \n", pOutDataLen);
PrintHex("pOutData",pOutData,pOutDataLen,32);
printf(" VESDK_SM4ECBEncryp success \n");
}
getchar();
return 0;
}
C语言测试结果:
============start==================
nKeynum = 1
pKey[16]:
00000000 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
pInData[16]:
00000000 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
============end==================
pOutDataLen = 16
pOutData[16]:
00000000 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
VESDK_SM4ECBEncryp success
go语言测试结果:
GOROOT=C:\SoftWareInstall\go #gosetup
GOPATH=C:\WorkSpace\GoWorkSpace #gosetup
C:\SoftWareInstall\go\bin\go.exe build -o C:\Users\da9000\AppData\Local\Temp\___go_build_u2pppw_call_dll2.exe u2pppw/call_dll2 #gosetup
C:\Users\da9000\AppData\Local\Temp\___go_build_u2pppw_call_dll2.exe #gosetup
call dll: GenDll.dll
nKeynum 1
pKey 824634244624
nKeyLen 16
pInData 824634244640
nInDataLen 16
panic: Failed to find VESDK_SM4ECBEncryp procedure in GenDll.dll: The specified procedure could not be found.
goroutine 1 [running]:
syscall.(*LazyProc).mustFind(0xc00006e330)
C:/SoftWareInstall/go/src/syscall/dll_windows.go:311 +0x5f
syscall.(*LazyProc).Call(0xc00006e330, 0xc0000101c0, 0x8, 0x8, 0x2, 0xe, 0x0, 0x0)
C:/SoftWareInstall/go/src/syscall/dll_windows.go:327 +0x36
main.main()
C:/WorkSpace/GoWorkSpace/src/u2pppw/call_dll2/sdk2.go:46 +0x535
Process finished with exit code 2
本次测试未实现使用VisualStudio2013生成dll库,并使用gcc进行调用,仅实现了使用gcc编译生成dll库,使用go语言调用
后续如果解决了此问题,再更新。。