背景
我有几个Matlab脚本可以与用户创建交互式会话(有点像游戏),我想创建一个C#GUI作为前端来启动这些脚本而不是在Matlab中手动输入命令窗口。原因是这些脚本被分成不同的目录,需要设置几个输入参数,如果用户相同,其中许多都是相同的。
问题说明
我的主要问题是如何与Matlab实例进行通信?我对来回传递数据不感兴趣;相反,我想向Matlab发送1个命令让它做它的事情。一个例子是:
cd('D:\Script1\'); fnScript1(0, true, 'default') %command for Matlab to execute
我的计划方法是:
我采用这种方法的一个大问题是我没有很好的方法来做第3步.Matlab没有使用标准的Windows控件,所以我很确定像UI Automation这样的东西在这里没有帮助我。我能想到的解决方案是获取Matlab实例的客户端窗口区域并在死点中发送鼠标单击,因为这是在命令窗口的默认位置内(当然我会确保它是's'实际上那里)。
尽管如此,我还是意识到这是一个非常糟糕的解决方案,所以我希望有人能想出一个更好的解决方案,最好不要点击并假设事情应该在哪里。我已经四处寻找,类似问题的解决方案并不适用于我的案例:
答案 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}");
请注意这不是防弹的,可能会发生许多事情:
输出重定向问题确实让我烦恼,因为COM接口没有禁用它的选项。现在我依赖于相当脆弱的复制/粘贴命令方法,但这是我能想到的最好的。