Background
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.
Problem Description
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
My planned approach is:
Generate the command on the GUI side and copy to clipboard
Use SetForegroundWindow() to give focus to Matlab
Give focus to the command prompt
Paste the command from the clipboard using SendKeys.Send("^v")
Execute the command using SendKeys.Send("{ENTER}")
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:
I don't want to create new instances of Matlab every time I want to execute a script; I just want to reuse the same instance that is already there.
I don't want to integrate Matlab code into my C# project since it's doing complicated things involving painting stuff directly to the screen, writing data to parallel ports ...etc
I'm not sure if I want to use COM to do this since I have no experience with COM at all and don't know where to begin with. Besides, using COM (or DDE for that matter) to pass a single string seems like overkill
I only have a basic license and don't have access to the fancy toolboxes
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.
Setup
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.
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);
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.
Method 1: Attach to a running instance of Matlab or create one if none exists
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());
}
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.
Method 2: Attach to a running instance or fail if none exists
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());
}
Using the Matlab com object
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:
The clipboard is in use by another application and cannot be written to
The clipboard contents changed before you got a chance to paste it
Matlab lost focus before you could send the keys
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.
Related
I've been thinking of giving the in Windows implemented cmd a fresh look and make a WinForm out of it(C# .net4.0 or later or latest mono# distribution).
Now, what I plan on doing is:
only showing the form, no console visible(even in the task bar)
telling the cmd what to do by virtually typing into it
catching the consoles output and using the form to make the user interact
I thought of some kind of "return" thing, like a dll would do, but I have not used consoles and forms together in a single project, so there's my question:
How do I not show a console window but write commands into it's line and receive the output with a WinForms application?
Thanks in advance.
--EDIT
I should maybe add that my main problem is typing catching the consoles output and also typing into it while it's not visible and thus not focusable.
You may well run into difficulties other than simply suppressing the appearance of a console window. But as far as that particular requirement goes, it's not hard.
You'll use the System.Diagnostics.Process class to start the process. Before starting the process, you'll need to see ProcessStartInfo.CreateNoWindow property to true. Note that you also need to set ProcessStartInfo.UseShellExecute to false, otherwise the CreateNoWindow property is ignored.
As for the broader problem: it seems likely that you'll need to start the console process using "cmd.exe /k" to instantiate a new command-line interpreter process without it exiting before you're done with it. Then you'll also need to use the redirection features in the Process class to read from stdout and stderr, and to write to stdin.
What I thought would be pretty easy is quickly defeating me. I'm not a native C# programmer, but was asked to create a WinForm application that has a single instance. I''ve seen the Mutex examples already on StackOverflow, but the one thing that eludes me is the ability to pass parameters to window on the command line, parse the values and repaint the form with the new values.
Anyone have an example of this? The main thing that seems to be tripping me up is the threading. I want to run my.exe and show the window. Each time the form is run, I don't want a new form -- just to get the new parameters and show them in the form.
Any/All replies are appreciated!
When you starting another instance of your application, you are running same code, but on different process. So, you need to look on passing data between processes. Something like Named Pipes or Remoting.
#lazyberezovsky is right. Invoking again the application from the command line will spawn a different, unrelated process and you would require inter-process communication to forward the new parameters to the previously running app instance, before quitting the new process being invoked.
IMHO, the easiest way (not the best certainly) to communicate between these two processes would be using the Windows Registry, as this is already thread-safe and the API is very simple.
First, when the application runs, before showing the main form, I would perform a check to see if another instance of the app is running.
If false, it is the first time the app runs and I would process the command line and show the form as regular. I would also clear the registry key used for inter-process communication (see below).
If true, then I would store the command line in the registry on a specific key that will serve for inter-process communication and then I would terminate the application without even showing the main form.
Your running application (the first instance) will require to start a polling mechanism (could be a Windows timer firing once each second) that regularly examines the registry key where a new command line is expected . It would normally find and empty string and do nothing, but if the retrieved value is not empty, then it would mean the user spawned again the application with a different set of parameters, then you can proceed to decode your command line and repaint the window as necessary. After this, make sure you clear the registry entry again, so the polling mechanism resumes and detects the next time the application is invoked by the user.
Named pipes, WCF, .remoting or TCP sockets are IPC mechanisms that can be used and won't require a polling mechanism, that may be frowned upon by some. ;)
Hope this helps!
I keep getting multiple verification prompt from a 3rd party application where on many instances I have to select the prompt window, type my password and click on Ok. I thought of writing a simple application in WHITE framework which can capture the window, enter my password and deliver a click automatically.
I need to detect whenever a new window has popped up in Windows environment. I don't want to use either a timer or a loop. Can I get an event when ever a new window appears (registers) ?
-- Regards
Akshay Mishra
Outside of managed code you could use global WindowHooks, however this involves injecting a DLL into another process space.
This is not easily done with managed DLLs (read: assemblies). (It can be done, see here)
I wrote an article on CodeProject a while ago on how to create a sort of managed global hook for WM_CREATE and WM_DESTROY messages.
It includes full source code and it probably has what you need. Your application will need administrator privileges for this!
I think that is what this is called:
I want to be able to add stuff like that to my program. Such as an open button and other buttons that would execute a method within the app. This is in C#, by the way.
I DID look into the Windows 7 API Code Pack, but it.. doesn't work the way I want. It won't let me execute a method inside my app like I want. It just lets you open other apps.
Is something like this possible?
I think its you who doesnt understand how JumpLists work.
"It just lets you open other apps" Is exactly what it does, nothing else. Thats Windows 7 feature, not API Code Pack limitation. Key point to this is fact, that your application is not running, so WHERE it should execute your method?
Correct implementation would be to make your JumpList run your application with correct parameters and then inside your application Main method invoke different methods depending on those parameters.
Dont forget, even window applications have input parameters, just like console apps.
If you look at Outlook and Messenger, they invoke other commands within the app, so you know it is possible. But as #Euphoric says, the architecture of jumplists is really simple - choosing a destination (eg a file name or URL) results in Windows launching another instance of the app and passing that destination as a command line argument. Choosing a task results in Windows launching that task, which needs to be some other exe.
How to resolve this seeming paradox? The other exe is a helper exe that communicates with the main app. Examples of this are in short supply but I am working on a demo for an early November talk, and will blog it when I have it done. Perhaps this architectural pointer will get you started.
Assign URLs to the jump list items and use an HTTP listener to invoke methods upon your application. Like this URL mapping with C# HttpListener
Additionally, you can go the awekward route and use a shared mutex to make your application single instance only and forward command line arguments across a named pipe. But the http listener is nicer ;)
I have written an Office shared Add-in with C# and .NET 2.0. It uses the same COM Shim for all office apps. Currently, every instance creates its own instance of my class -- they have no knowledge of the fact that the add-in is running on another application.
Is it possible to make it so that, say, when the Word add-in launches it can detect that the Excel add-in is already running? Can they communicate between each other?
Let's say my dll is called Addin.dll. When, for example, Word opens, it runs the code in Addin.dll that implements the IExtensibility interface, and creates a class, let's call it WordAddin. When Excel opens, it also runs the code in Addin.dll, and it creates an instance of ExcelAddin. Now, suppose that Word is running and WordAddin exists. When Excel is opened, Addin.dll is loaded into a different AppDomain. It has no knowledge that WordAddin exists. I want ExcelAddin to have know WordAddin exists, and be able to communicate with it, perhaps through a parent class that creates both.
Anyone know how to do this?
You could do this using a Microsoft Message Queue (MSMQ): Use Microsoft Message Queuing in C# for inter-process communication
This code project article shows how to detect a running instance and how to copy command line parameters between them:
Single-Instance C# Application - for .NET 2.0
Not quite what you are asking but might be of interest: Detecting a running instance of a program and passing it information
I ended up using named pipes to communicate between processes
I have in fact done exactly what you're asking about. I went for old fashioned windows messages. Nothing fancy - so it will just work! :-)
Let your addin create a "window" using the NativeWindow class and give it a predetermned, unique name. Then search for another instance of such a window using GetWindow and GetWindowText when your addin launches. Communicate using SendMessage or PostMessage. Easy as that.