You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:25:32