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

时间:2014-08-13 23:53:13

标签: c# matlab

背景

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

问题说明

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

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

我的计划方法是:

  1. 在GUI端生成命令并复制到剪贴板
  2. 使用SetForegroundWindow()将焦点放在Matlab
  3. 将焦点放在命令提示符
  4. 使用SendKeys.Send(" ^ v")
  5. 从剪贴板粘贴命令
  6. 使用SendKeys.Send执行命令(" {ENTER}")
  7. 我采用这种方法的一个大问题是我没有很好的方法来做第3步.Matlab没有使用标准的Windows控件,所以我很确定像UI Automation这样的东西在这里没有帮助我。我能想到的解决方案是获取Matlab实例的客户端窗口区域并在死点中发送鼠标单击,因为这是在命令窗口的默认位置内(当然我会确保它是's'实际上那里)。

    尽管如此,我还是意识到这是一个非常糟糕的解决方案,所以我希望有人能想出一个更好的解决方案,最好不要点击并假设事情应该在哪里。我已经四处寻找,类似问题的解决方案并不适用于我的案例:

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

1 个答案:

答案 0 :(得分:2)

我找到了一种通过COM实现此目的的方法,包括打开一个新实例并附加到现有实例。我发现一个警告是你的应用程序必须具有与正在运行的Matlab实例相同的权限,否则它将无法找到它;例如,如果Matlab运行得很高,那么你的应用程序也必须运行。

<强>设置

为了使用Matlab的COM组件,您的项目需要添加对它的引用。在visual studio中,这是通过参考管理器完成的,可以在COM - &gt;下找到。类型库 - &gt; Matlab应用程序(版本8.2)类型库。您的版本号可能不同。

此外,默认情况下,Matlab不会启用COM启动。您可以修改传递给exe的命令行参数以启用它,但它会强制Matlab实例处于控制台模式。如果你想要普通的桌面模式,那么你需要在加载Matlab后启用COM。这可以通过startup.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实例。

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接口没有禁用它的选项。现在我依赖于相当脆弱的复制/粘贴命令方法,但这是我能想到的最好的。