通过c#应用程序控制Matlab实例

本文关键字:Matlab 实例 程序控制 应用 通过 | 更新日期: 2023-09-27 18:18:06

背景

我有几个Matlab脚本,可以与用户创建交互式会话(有点像游戏),我想创建一个c# GUI作为前端来启动这些脚本,而不是在Matlab的命令窗口中手动输入。这样做的原因是这些脚本被分离到不同的目录中,并且需要设置几个输入参数,如果用户相同,其中许多参数是相同的。

问题描述

我的主要问题是如何与Matlab实例通信?我对来回传递数据不感兴趣;相反,我想发送一个命令到Matlab,让它做它的事情。例如:

cd('D:'Script1''); fnScript1(0, true, 'default') %command for Matlab to execute

我计划的方法是:

  1. 在GUI端生成命令并复制到剪贴板
  2. 使用SetForegroundWindow()给Matlab聚焦
  3. 把焦点放在命令提示符
  4. 使用SendKeys.Send("^v")从剪贴板粘贴命令
  5. 使用SendKeys.Send("{ENTER}")执行命令

我使用这种方法的一个大问题是,我没有一个很好的方法来执行步骤3。Matlab不使用标准的Windows控件,所以我很确定像UI自动化这样的东西在这里帮不了我。我能想到的解决方案是获得Matlab实例的客户端窗口区域,并在死点中心发送鼠标单击,因为这是在命令窗口的默认位置内(当然我会确保它实际上在那里)。

尽管如此,我意识到这是一个相当糟糕的解决方案,所以我希望有人能想出一个更好的解决方案,最好不要到处点击,假设事情会在它应该在的地方。我搜索了一下,类似问题的解决方案并不适用于我的情况:

  • 我不想在每次执行脚本时创建Matlab的新实例;我只是想重用已经存在的相同实例。
  • 我不想将Matlab代码集成到我的c#项目中,因为它正在做复杂的事情,包括直接在屏幕上绘制东西,将数据写入并行端口等
  • 我不确定我是否想用COM来做这件事,因为我根本没有COM的经验,也不知道从哪里开始。此外,使用COM(或DDE)传递单个字符串似乎有点小题大做
  • 我只有一个基本的许可证,没有访问花哨的工具箱

通过c#应用程序控制Matlab实例

我想出了一种通过COM来做到这一点的方法,既打开一个新的实例,又附加到一个现有的实例。我发现的一个警告是,您的应用程序必须具有与Matlab运行实例相同的特权,否则它将无法找到它;例如,如果Matlab正在运行,那么您的应用程序也必须如此。

设置>

为了使用Matlab的COM组件,您的项目需要添加对它的引用。在visual studio中,这是通过参考管理器完成的,可以在COM -> Type Libraries -> Matlab Application (Version 8.2) Type Library下找到。您的版本号可能不同。

另外,Matlab默认情况下不以启用COM开始。您可以修改传递给exe的命令行参数以启用它,但它将强制Matlab实例处于控制台模式。如果你想要正常的桌面模式,那么你需要在Matlab加载后启用COM。这可以通过启动来完成。M脚本如下:

enableservice('AutomationServer', true);

请注意,如果您选择通过COM创建您的Matlab实例,而不是附加到现有的实例,您不必这样做,因为它是默认启用的。

方法1:附加到正在运行的Matlab实例上,如果不存在则创建一个

此方法将获得对第一个Matlab实例运行的COM引用;在没有找到的情况下,它将创建一个新的Matlab实例。

//The desktop progID only supports single-instance operation
Type MatlabType = Type.GetTypeFromProgID("Matlab.Desktop.Application");
MLApp.MLApp matlab = (MLApp.MLApp)Activator.CreateInstance(MatlabType);
//check that we have a valid instance
if (matlab == default(MLApp.MLApp))
{
    MessageBox.Show("Matlab com object is null", "Error");
    return;
}
//make Matlab do something (give focus to command window)
try
{
    matlab.Execute("commandwindow");
}
catch (System.Runtime.InteropServices.COMException ex)
{
    //something went wrong with the COM call
    //such as Matlab getting killed and is no longer running
    MessageBox.Show(ex.Message, ex.GetType().ToString());
}

注意,上面提到的特权问题在这里起作用。如果您的Matlab实例被提升运行,而您的程序没有,那么此方法将无法找到提升的实例,并尝试创建一个非提升的实例。这可能是有问题的,因为Matlab的许可证管理器可以拒绝尝试并抛出许可证错误消息,其副作用是永久挂起您的应用程序。

方法2:挂载到正在运行的实例,如果不存在则挂载失败

与方法1不同的是,如果找不到新的Matlab实例,则此方法不会尝试创建新的Matlab实例。

using System.Runtime.InteropServices;
try
{
    MLApp.MLApp matlab = 
        (MLApp.MLApp)Marshal.GetActiveObject("Matlab.Desktop.Application");
}
catch (System.Runtime.InteropServices.COMException ex)
{
    //this happens if no Matlab instances were running
    MessageBox.Show(ex.Message, ex.GetType().ToString());
}

使用Matlab com对象

告诉Matlab做某事的最简单方法是调用您得到的COM对象的.Execute()方法,但是这有一个问题。由于COM接口是为Matlab和应用程序之间的双向通信而设计的,因此通常在Matlab命令窗口中显示的任何内容都会被重定向到.Execute()的返回值。如果您希望输出显示在Matlab的命令窗口中,则必须手动将命令发送到Matlab。下面是一种方法:

//this won't work, you won't see anything in Matlab
matlab.Execute(@"fprintf('Hello World')");
//copy the command to clipboard instead
Clipboard.SetText(@"fprintf('Hello World')");
//give Matlab's command window (global) focus
matlab.Execute("commandwindow");
System.Threading.Thread.Sleep(100);
//paste the command and run it
SendKeys.Send("^v{ENTER}");

请注意,这不是万无一失的,有很多事情可能会发生:

  • 剪贴板正在被其他应用程序使用,不能写入
  • 剪贴板内容在你有机会粘贴之前发生了变化
  • 在发送密钥之前Matlab失去焦点

输出重定向问题真的很困扰我,因为COM接口没有选项来禁用它。现在我依靠的是相当脆弱的复制/粘贴命令的方法,但这是我能想到的最好的方法。