点这里获得项目完整代码

需要的知识:

内存对齐。Golang的数据结构和DLL中数据结构的内存结构必须相等。

不过幸运的是,Go和C的内存结构一般都是一样的,编程时需要确保数据类型一样。

export_test.h
/*此头很有必要,别人在调用的时候知道有哪些方法*/
#define BUILD_DLL
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endiftypedef struct fd_para // 定义一个结构体
{double      	    timeMark;			//			8字节int 				signalVar; 			//			4字节double 				signalDVar; 		//			8字节unsigned char		signalStatus;		//		    1字节unsigned char		alarmStatus;		//			1字节
}FDPARA;EXPORT FDPARA* exportFcn(int a); // 定义exportFcn函数

因此,与其一一对应,要传输的数据结构是这样的:

type FDPARA struct {timeMark     float64signalVar    int32signalDVar   float64signalStatus uint8alarmStatus  uint8
}

Go代码

package mainimport ("bytes""encoding/binary""fmt""log""math""syscall""unsafe"
)type FDPARA struct {timeMark     float64signalVar    int32signalDVar   float64signalStatus uint8alarmStatus  uint8
}func IntPtr(n int) uintptr {return uintptr(n)
}// 将字节流转换为float64。
func BytesToFloat64(bytes []byte) float64 {bits := binary.LittleEndian.Uint64(bytes)return math.Float64frombits(bits)
}// 将字节流转换为Int32.
func BytesToInt32(b []byte) int32 {bytesBuffer := bytes.NewBuffer(b)var x int32err := binary.Read(bytesBuffer, binary.LittleEndian, &x)if err != nil {log.Fatal(err.Error())}return x
}// 主函数。执行了加载动态链接库的过程。
func main() {// export_test.dll在文件夹”c“中.文件夹”c“中存放动态链接库export_test.dll。// 建议用绝对路径的方式导入。h, err := syscall.LoadLibrary("path\\to\\export_test.dll")if err != nil {abort("LoadLibrary", err)}defer syscall.FreeLibrary(h)proc, err := syscall.GetProcAddress(h, "exportFcn")if err != nil {abort("GetProcAddress", err)}r, _, _ := syscall.Syscall(uintptr(proc), 1, IntPtr(123), 0, 0)// trap是系统调用号p := (*byte)(unsafe.Pointer(r))data := make([]byte, 0)var fdPara *FDPARAfdPara = &FDPARA{0, 0, 0, 0, 0}for i := 0; i < int(unsafe.Sizeof(*fdPara)); i++ { // 从返回的数值中,一个字节一个字节的读取,并且存入[]byte数组中。data = append(data, *p)r += unsafe.Sizeof(byte(0))p = (*byte)(unsafe.Pointer(r))}// 以下代码需要用到内存对齐的知识。// 对于我的Windows x64平台,那么#pragma pack(n) 中,n=8// 因此结构体的各个属性存储位置如下所示。fmt.Printf("%v\n", data)fmt.Printf("timeMark:%f\n", BytesToFloat64(data[:8]))fmt.Printf("signalVar:%d\n", BytesToInt32(data[8:12]))fmt.Printf("signalDVar:%f\n", BytesToFloat64(data[16:24]))fmt.Printf("signalStatus:%d\n", uint8(data[24]))fmt.Printf("alarmStatus:%d\n", uint8(data[25]))// 也可以不必使用上面几行那样复杂的转换方式。// 可以用下面这种转换方式:fdPara = *(**FDPARA)(unsafe.Pointer(&data))fmt.Printf("*fdPara: %v\n", *fdPara)fmt.Printf("收到的结构体字节流:%v\n", data)
}func abort(funcname string, err error) {panic(funcname + " failed: " + err.Error())
}

动态链接库文件

动态链接库采用C语言编写,在64位Windows系统上,文件夹"dllcompile"下面的export_test.dll,可以直接使用。

注意,gcc编译出来的dll一定要和Golang的位数相同。否则可能报错”dll不是有效的win32程序。“

  • 在Windows x64平台下,若gcc.exe是32位的,那么需要调整Golang的编译目标就是生成32位可执行文件;
  • 如果Golang一定要生成64位可执行文件,那么一般的mingw编译器不支持64位。可以下载Mingw-w64,支持windows编译64位。

资源文件:

path\to\gcc.exe -o run main.c export_test.c

编译动态链接库的命令:

cd dllcompile
your\path\to\gcc.exe --share export_test.c -o export_test.dll

完整代码

项目完整代码在这里:
https://gitee.com/hzy15610046011/GolangCallDLLDemo