Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a System.Windows.Form instance from its Win32 handle?

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;
  }
}
like image 764
Ian Kemp Avatar asked Feb 24 '09 13:02

Ian Kemp


3 Answers

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

like image 187
Hans Passant Avatar answered Oct 10 '22 09:10

Hans Passant


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

  1. use something low-level with a temp-file.

  2. use remoting (WCF)

like image 26
Henk Holterman Avatar answered Oct 10 '22 10:10

Henk Holterman


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.

like image 41
JaredPar Avatar answered Oct 10 '22 09:10

JaredPar