通过C#应用程序控制Matlab实例 [英] Controlling Matlab instance through C# application

查看:99
本文介绍了通过C#应用程序控制Matlab实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

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

I have several Matlab scripts that create interactive sessions with the user (kind of like a game), and I want to create a C# GUI as a front-end to kickoff these scripts instead of manually typing in Matlab's command window. The reason for this is these scripts are separated into distinct directories and require several input parameters to set up, many of which are identical if the user is the same.

问题描述

我的主要问题是如何与Matlab实例通信?我对来回传递数据不感兴趣;相反,我想向Matlab发送1条命令并让其完成任务.一个例子是:

The main question I have is how do I communicate with the Matlab instance? I'm not interested in passing data back and forth; rather, I would like to send 1 command to Matlab and let it do its thing. An example would be:

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实例的客户端窗口区域,并在死点位置发送鼠标单击,因为这在命令窗口的默认位置之内(当然,我会确保它确实在该位置).

The big problem I have with this approach is that I don't have a good way of doing step 3. Matlab doesn't use standard Windows controls so I'm pretty sure something like UI Automation won't help me here. The solution I can think of is to get the client window area of the Matlab instance and send a mouse click in the dead center, since this is within the default positioning of the command window (of course I would make sure it's actually there).

还是,我意识到这是一个很糟糕的解决方案,所以我希望有人能提出更好的解决方案,最好不必单击周围并假设一切都在正确的位置.我进行了搜索,发现类似问题的解决方案不适用于我的情况:

Still, I realize this is a pretty lousy solution so I'm hoping someone can come up with a better one, preferably without having to click around and assuming things will be where it should be. I've searched around and the solutions to similar questions don't apply well to my case:

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

推荐答案

我想出了一种通过COM进行此操作的方法,既可以打开新实例,也可以附加到现有实例上.我发现的一个警告是,您的应用程序必须具有与正在运行的Matlab实例相同的特权,否则它将无法找到它.例如,如果Matlab处于高运行状态,则您的应用程序也必须这样做.

I figured out a way to do this via COM, both opening a new instance and attaching to an existing instance. One caveat I found was that your application has to have the same privileges as the running instance of Matlab, otherwise it won't be able to find it; for example, if Matlab is running elevated then your application has to as well.

设置

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

In order to use the COM component of Matlab, your project needs to add a reference to it. In visual studio this is done via the reference manager and can be found under COM -> Type Libraries -> Matlab Application (Version 8.2) Type Library. Your version number might be different.

此外,默认情况下,Matlab不会以启用COM开头.您可以修改传递给exe的命令行参数以启用它,但是它将强制Matlab实例处于控制台模式.如果要使用普通桌面模式,则需要在Matlab加载后启用COM.可以通过如下所示的startup.m脚本来完成此操作:

Additionally, Matlab by default does not start with COM enabled. You can modify the command line parameters passed to the exe to enable it, but it will force the Matlab instance to be in console mode. If you want the normal desktop mode then you need to enable COM after Matlab has loaded. This can be done via the startup.m script like this:

enableservice('AutomationServer', true);

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

Note that if you elect to create your Matlab instance through COM instead of attaching to an existing one, you don't have to do this since it is enabled by default.

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

此方法将为您提供第一个正在运行的Matlab实例的COM引用;如果找不到一个,它将创建一个新的Matlab实例.

This method will get you a COM reference to the first Matlab instance running; in the case where it doesn't find one it will create a new Matlab instance.

//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的许可证管理器可以拒绝尝试并抛出许可证错误消息,并带有永久挂起您的应用程序的副作用.

Note that the privilege issue mentioned above comes into play here. If your Matlab instance was run elevated and your program was not, then this method will fail to find the elevated instance and try to create a non-elevated one. This can be problematic since Matlab's license manager can reject the attempt and throw a license error message with the side affect of permanently hanging your application.

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

与方法1不同,如果找不到该方法,则该方法将不会尝试创建新的Matlab实例.

Unlike method 1, this method won't try to create a new instance of Matlab if none can be found.

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.这是一种方法:

The simplest way to tell Matlab to do something is to call the .Execute() method of the COM object you got, however there is a gotcha to this. Since the COM interface was designed for two-way communication between Matlab and your application, anything that usually is displayed in Matlab's command window gets redirected to the return value of .Execute(). If you want the output to appear in Matlab's command window instead, you would have to manually send the commands to Matlab. Here is one approach:

//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}");

请注意这不是防弹措施,并且可能发生许多事情:

Note how this is not bullet-proof and a number of things can happen:

  • 剪贴板已被其他应用程序使用,无法写入
  • 剪贴板内容发生变化,然后才有机会粘贴
  • Matlab在发送密钥之前就失去了焦点

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

The output redirect issue is really bugging me since the COM interface doesn't have options to disable it. Right now I'm relying on the rather fragile method of copy/pasting commands, but that was the best I could come up with.

这篇关于通过C#应用程序控制Matlab实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆