Задний план
У меня есть несколько скриптов Matlab, которые создают интерактивные сеансы с пользователем (вроде игры), и я хочу создать графический интерфейс С# в качестве front-end, чтобы запускать эти сценарии, а не вручную вводить в командном окне Matlab. Причина этого заключается в том, что эти сценарии разделены на разные каталоги и требуют нескольких входных параметров для настройки, многие из которых идентичны, если пользователь является одним и тем же.
описание проблемы
Главный вопрос, который у меня есть, - как мне взаимодействовать с экземпляром Matlab? Я не заинтересован в передаче данных туда и обратно; Скорее, я хотел бы отправить 1 команду в Matlab и позволить ей сделать что-то. Примером может служить:
cd('D:\Script1\'); fnScript1(0, true, 'default') %command for Matlab to execute
Мой планируемый подход:
Большая проблема, с которой я сталкиваюсь, заключается в том, что у меня нет хорошего способа сделать шаг 3. Matlab не использует стандартные элементы управления Windows, поэтому я уверен, что что-то вроде UI Automation мне не поможет. Решение, о котором я могу думать, - это получить клиентскую область окна экземпляра Matlab и отправить клик мыши в мертвую точку, так как это находится в позиции по умолчанию в командном окне (конечно, я бы убедился, что на самом деле там).
Тем не менее, я понимаю, что это довольно паршивое решение, поэтому я надеюсь, что кто-то может придумать лучший, желательно без необходимости щелкнуть и предположить, что все будет так, как должно быть. Я искал, и решения по подобным вопросам не очень хорошо подходят для моего случая:
Я понял способ сделать это через COM, открыв новый экземпляр и присоединив к существующему экземпляру. Одно из предостережений, которое я обнаружил, заключается в том, что ваше приложение должно иметь те же привилегии, что и исполняемый экземпляр Matlab, иначе он не сможет его найти; например, если Matlab работает повышенным, то ваше приложение также должно быть.
Настроить
Чтобы использовать COM-компонент Matlab, вашему проекту необходимо добавить ссылку на него. В визуальной студии это делается через диспетчер ссылок и можно найти в библиотеке типов COM → Тип → Matlab Application (Version 8.2). Номер версии может отличаться.
Кроме того, Matlab по умолчанию не запускается с включенным COM. Вы можете изменить параметры командной строки, переданные в exe, чтобы включить его, но это заставит экземпляр Matlab быть в режиме консоли. Если вы хотите нормальный режим рабочего стола, вам необходимо включить COM после загрузки Matlab. Это можно сделать с помощью скрипта startup.m:
enableservice('AutomationServer', true);
Обратите внимание: если вы решили создать экземпляр Matlab через COM вместо привязки к существующему, вам не нужно это делать, поскольку он включен по умолчанию.
Способ 1. Присоедините к исполняемому экземпляру Matlab или создайте его, если его не существует
Этот метод даст вам ссылку COM на первый экземпляр Matlab; в случае, если он не находит его, он создаст новый экземпляр 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 что-то сделать - это вызвать метод .Execute()
COM-объекта, который вы получили, однако для этого есть gotcha. Поскольку интерфейс 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 command window (global) focus
matlab.Execute("commandwindow");
System.Threading.Thread.Sleep(100);
//paste the command and run it
SendKeys.Send("^v{ENTER}");
Обратите внимание, что это не пуленепробиваемый, и может произойти ряд вещей:
Проблема с перенаправлением вывода на самом деле вызывает ошибку, поскольку интерфейс COM не имеет параметров для ее отключения. Сейчас я полагаюсь на довольно хрупкий метод копирования/вставки команд, но это было лучшее, что я мог придумать.
function [] = printAndExec(cmd) disp(cmd); evalin('base', cmd); end
а затем вызвать эту функцию из C #, используя .Execute ()?