如何在Go中调用Matlab Simulink生成的DLL并读写输入输出
在Go中调用Simulink生成的DLL读写全局变量
问题背景
我通过Matlab Simulink/Codegen生成了一个DLL模型,在Python中运行正常,代码如下:
import ctypes real_T = ctypes.c_double # 加载DLL dll = ctypes.windll.LoadLibrary("./simulink.dll") # 模拟运行 dll.initialize() # 初始化 for i in range(10): mdlInput = (real_T).in_dll(dll, "my_custom_input") mdlInput.value = i # 执行步长 dll.step() # 步进 # 获取输出 mdlOutput = (real_T).in_dll(dll, "my_custom_output") print(mdlOutput.value) dll.terminate() # 结束模拟
现在想将这段代码转为Go语言以提升性能,但遇到问题:无法直接读写DLL中的my_custom_input和my_custom_output这两个real_T(即double类型)全局变量,不知道如何在Go中设置输入值并读取输出值。
解决方案
在Go中可以通过syscall包加载DLL,获取全局变量的内存地址后转换为对应类型的指针,直接通过指针读写变量值。以下是完整的Go实现代码:
package main import ( "fmt" "syscall" "unsafe" ) func main() { // 加载目标DLL dll, loadErr := syscall.LoadDLL("./simulink.dll") if loadErr != nil { panic(fmt.Sprintf("加载DLL失败: %v", loadErr)) } defer dll.Release() // 查找并初始化模拟所需的函数 initProc, initErr := dll.FindProc("initialize") if initErr != nil { panic(fmt.Sprintf("找不到initialize函数: %v", initErr)) } stepProc, stepErr := dll.FindProc("step") if stepErr != nil { panic(fmt.Sprintf("找不到step函数: %v", stepErr)) } termProc, termErr := dll.FindProc("terminate") if termErr != nil { panic(fmt.Sprintf("找不到terminate函数: %v", termErr)) } // 调用初始化函数 _, _, initCallErr := initProc.Call() if initCallErr != syscall.Errno(0) { panic(fmt.Sprintf("初始化失败: %v", initCallErr)) } // 确保程序退出前调用终止函数 defer func() { _, _, termCallErr := termProc.Call() if termCallErr != syscall.Errno(0) { fmt.Printf("终止模拟时出错: %v\n", termCallErr) } }() // 获取全局输入变量的地址,转换为*float64指针 inputSym, inputErr := dll.FindProc("my_custom_input") if inputErr != nil { panic(fmt.Sprintf("找不到my_custom_input变量: %v", inputErr)) } inputPtr := (*float64)(unsafe.Pointer(inputSym.Addr())) // 获取全局输出变量的地址,转换为*float64指针 outputSym, outputErr := dll.FindProc("my_custom_output") if outputErr != nil { panic(fmt.Sprintf("找不到my_custom_output变量: %v", outputErr)) } outputPtr := (*float64)(unsafe.Pointer(outputSym.Addr())) // 执行模拟循环 for i := 0; i < 10; i++ { // 设置输入值 *inputPtr = float64(i) // 执行步进 _, _, stepCallErr := stepProc.Call() if stepCallErr != syscall.Errno(0) { panic(fmt.Sprintf("步进执行失败: %v", stepCallErr)) } // 读取并打印输出值 fmt.Printf("第%d次步进输出: %.2f\n", i+1, *outputPtr) } }
关键说明
- 全局变量访问:Simulink生成的DLL会将全局变量作为导出符号,因此可以通过
FindProc获取其内存地址(尽管方法名带Proc,但导出的全局变量地址同样适用)。 - 类型转换:通过
unsafe.Pointer将获取到的地址转换为*float64指针,之后就可以像操作普通Go变量一样直接读写DLL中的全局变量。 - 错误处理:每个步骤都添加了错误检查,确保模拟流程的稳定性;通过
defer确保terminate函数一定会被调用,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Mingut
相关产品推荐
相关产品推荐

