Reputation: 29879
The following code implements a simple singleton that ensures only 1 instance of my application can be run. However, if another instance is started, I need to be able to grab that instance's command-line arguments, pass them to the initial instance, then terminate the second instance.
The issue comes in when I'm attempting to get hold of the first instance of the application. Once I've found the handle of that instance's main form, I pass it to the Control.FromHandle()
method, expecting to get back a MainForm
. Instead, the return value is always null
. (Control.FromChildHandle()
gives the same result.)
Therefore, my question is simply: what am I doing wrong? And is this even possible in .NET?
public class MainForm : Form
{
[DllImport("user32")]
extern static int ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
extern static bool SetForegroundWindow(IntPtr hWnd);
private Mutex singletonMutex;
private void MainForm_Load(object sender, EventArgs e)
{
bool wasCreated;
singletonMutex = new Mutex(false, Application.ProductName + "Mutex", out wasCreated);
// returns false for every instance except the first
if (!wasCreated)
{
Process thisProcess = Process.GetCurrentProcess();
Process[] peerProcesses = Process.GetProcessesByName(thisProcess.ProcessName.Replace(".vshost", string.Empty));
foreach (Process currentProcess in peerProcesses)
{
if (currentProcess.Handle != thisProcess.Handle)
{
ShowWindowAsync(currentProcess.MainWindowHandle, 1); // SW_NORMAL
SetForegroundWindow(currentProcess.MainWindowHandle);
// always returns null !!!
MainForm runningForm = (MainForm) Control.FromHandle(currentProcess.MainWindowHandle);
if (runningForm != null)
{
runningForm.Arguments = this.Arguments;
runningForm.ProcessArguments();
}
break;
}
}
Application.Exit();
return;
}
}
Upvotes: 8
Views: 16097
Reputation: 380
I use the Microsoft.VisualBasic.dll library described in the thread that nobugz pointed to. Yes, you can use it in C#. You just override the OnStartupNextInstance and pass the command line into your program in whatever way works best for you.
This is a whole lot easier than messing around with the threads manually.
Upvotes: 0
Reputation: 941705
Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.
Upvotes: 4
Reputation: 29594
You can't call code in another process directly, you need to use some form of inter-process communication
If you are communication only between processes started by the same user on the same computer you can use window messages (using WinAPI PostMessage and overriding WndProc), otherwise I think remoting is the easiest to use in .net
Upvotes: 0
Reputation: 754913
Try the following
var form = (Form)(Control.FromHandle(myHandle));
EDIT
Re-read your question and realized you are looking at a handle in another process. There is no way to convert a handle in another process to a Form instance in the current process. My solution will only work for handles in the same process.
The only way to get ahold of the Form instance is to use Remoting. But that will require cooperation on the part of both processes which does not appear to be what you are looking for.
Upvotes: 4
Reputation: 36659
You are really trying to implement a singleton application. There are a few examples in the Internet (sorry, haven't really tried myself), e.g.
http://www.codeproject.com/KB/cs/SingletonApplication.aspx
http://www.nathanm.com/csharp-wpf-singleton-application/
Upvotes: 2
Reputation: 273294
Control.FromHandle isn't going to work because the control you're looking for is in another process (and therefore in another appdomain).
You already have the WindowHandle but it's use is limited to the Win32 API. Nothing from WinForms is going to work.
You can send (WM_) messages but it's hard to get data across.
Options
use something low-level with a temp-file.
use remoting (WCF)
Upvotes: 3