views:

652

answers:

5

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;
  }
}
+1  A: 

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.

JaredPar
+2  A: 

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/

Grzenio
A: 

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

Nir
+3  A: 

Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.

Hans Passant
Thanks for that - I'd completely forgotten about the WindowsFormsApplicationBase class. Seems strange to me that such a useful class isn't included in the System.Windows.Forms namespace, but oh well.
Ian Kemp
A: 

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.

Jesse Smith