windows下go语言调用C语言xxx.dll动态库(带DEMO)

1、概述

本文主要实现在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编译

 

2、生成dll库

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

 

编译dll报错:exec: "gcc": executable file not found in %PATH%

因为go语言调用dll依赖于gcc进行编译,因此需要安装MinGW,下载地址:

https://sourceforge.net/projects/mingw-w64/

windows下go语言调用C语言xxx.dll动态库(带DEMO)_第1张图片

安装工程自行百度

安装好之后,配置环境变量,结果如下:

windows下go语言调用C语言xxx.dll动态库(带DEMO)_第2张图片

 

编译dll报错:cc1.exe: sorry, unimplemented: 64-bit mode not compiled in

原因:这是MinGW是32位的,更换成64位即可解决问题。

3、go语言调用

 

方式一:

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])





}

详细内容本人不在赘述

4、VisualStudio-2013 生成DLL库并被调用

使用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语言调用

 

后续如果解决了此问题,再更新。。

 

 

你可能感兴趣的:(go,dll,c语言,windows,go)