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.
Related
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.
I have just created a windows service. Since there isn't really a way to debug services(that I know of)I created the majority of the application as a desktop console application that accessed the libraries I created that it uses. When doing this everything worked great. So once I created the service to do the same job the console was doing(all the console did was open and automatically start it's job) and installed it with sc.exe and started it up, it doesn't seem to be doing it's job which is basically listening for connections.
Is there anything else I have to do for my service to be able to access these libraries? Do I have to somehow register them so they will work together? This is a standard windows service created in C#.
I am so glad you got it working. I know i have seen many have this problem.... And to think my own question about it got down voted.
But anyways these are called Loader Service(by Microsoft) not Windows Service. They are the same thing except the former has the ability to interact with GUI.
I know its a couple of months later this answer but...Your problem is not the library... its the process itself. Use this Thread, I was able to create my own solution for this problem since many had the problem but noone gave the solution to it directly. My Solution - C# Windows Service Creates Process but doesn't executes it
Is there a way to catch windows messages within word (I tried overriding WinProc… no go)? I have a word VSTO plug-in that I’m trying to communicate with externally… ideally I would like to post a message from one application, catch that message in word, and then perform the appropriate response…
Since this functionality isn't exposed by the interop assemblies I would not suggest this method. You should only try and interact with office applications through these APIs.
If you just need interprocess communication and you have control over the sender and receiver I would suggest using a socket connection or a pipe. Word is going to to be handling it's own windows messages, and it would not be a good idea to interfere with that process.
Avoiding interprocess communication in Word (which is a mess when it comes to privileges with UAC) I came up with my own solution:
I set up a hook within my instance of my word VSTO plugin with SetWinEventHook() (hooking SYS_ALERT) … I simply monitor new windows created (OB_CREATE), if it’s an application that uses the explained camera then I close the currently running graph in DS, freeing the camera and allowing it to be used in the application that has focus. Each of my programs that use the camera will implement this class hook.
Better ideas welcome…
I have a good working experience with C# but now I want to develop a simple(may be a console app) software which just detects the name and time of the process started or ended on my computer.
For example (I am assuming that my small app is already running) if a user opens Firefox then it should just insert firefox.exe into a database with time and if a user close it then it also do the same.
And same as above if a user open notepad then it should insert notepad.exe with time and so on.
I know how to insert values in database but I just need your help in identifying when the process/program starts or ends on my system.
Honestly saying I never developed this kind of application before so i have no idea that it can be possible using console app or i need to make a windows service app etc.
So please provide your answer just considering me as a beginner.
In the part of C# I am able to understand it so need to worry about that.
I am using visual studio 2010 with .net 4.0.
To do this without polling requires WMI. This is well supported in .net and you can use the ManagementEventWatcher class to subscribe to WMI notifications.
This Code Project article illustrates how it is done. Here's an extract showing how straightforward it is.
notePad = new ProcessInfo("notepad.exe");
notePad.Started +=
new Win32Process.ProcessInfo.StartedEventHandler(this.NotepadStarted);
notePad.Terminated +=
new Win32Process.ProcessInfo.TerminatedEventHandler(this.NotepadTerminated);
Note that ProcessInfo is a class implemented in the code attached to that article.
I found there are lots of posts showing how to detect if the application instance already running. But I cant find any one that shows how to access or use the same running application.
I have created shell menu items and linked them an application. For ex. If you right click on any folder it shows "OS Monitor". If i clicked on that an application is started. If I again right clicked on the folder and selected "OS Monitor" another instance of same application is started. I have to prevent this. Further more when user closes the "OS Monitor" form I just made it hidden. So that if the user again selects the same menu option then the same running form need to show.
I have created the application using C#2005. Does anybody have the idea how I could access the same running instance of the application.
Thanks in advance.
As address spaces of applications are separated, you have to use some global mechanism/object. An example is named mutexes: you create a named mutex, if it already exists, then the application was already running. Using Mutexes for ensuring that only one instance is running is presented on this blog
The second step is to communicate with the running instance. Therefore you have to use some IPC mechanism. Easiest is to use Windows messages if you are running Windows (like in the example from Blog). Note that this would not be portable to MONO as you have to make native calls. If that matters you could use a network connection among other possibilities. See answers to this question: IPC Mechanisms in C# - Usage and Best Practices.
After having transmitted the parameters to the running instance, you have to exit of course, otherwise you'll end up with two applications running. For a short time (the time of parameter transfer) you have indeed two instances running but only one effectively is doing the job.
This response was made under the assumption that you can/wish to make modifications to "OS Monitor".