如何通过其Win32句柄获取System.Windows.Form实例?

8
以下代码实现了一个简单的单例模式,确保只能运行1个我的应用程序实例。但是,如果启动另一个实例,我需要能够获取该实例的命令行参数,将它们传递给初始实例,然后终止第二个实例。
问题出在当我试图获取应用程序的第一个实例时。一旦我找到该实例主窗体的句柄,我将其传递给Control.FromHandle()方法,期望得到一个MainForm。然而,返回值总是null。(Control.FromChildHandle()得到相同的结果。)
因此,我的问题很简单:我做错了什么?这在.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;
  }
}
6个回答

4
尝试以下步骤:
var form = (Form)(Control.FromHandle(myHandle));

编辑

重新阅读了您的问题并意识到您正在查看另一个进程中的句柄。没有办法将另一个进程中的句柄转换为当前进程中的窗体实例。我的解决方案仅适用于同一进程中的句柄。

唯一获取窗体实例的方法是使用远程处理。但这需要两个进程的合作,这似乎不是您要寻找的内容。


4

单实例应用程序在.NET框架中得到了很好的支持。请查看这个线程,其中提供了一个完全符合您需求的示例。


谢谢你提醒我 - 我完全忘记了WindowsFormsApplicationBase类。对我来说,这样一个有用的类没有包含在System.Windows.Forms命名空间中似乎很奇怪,但是没关系。 - Ian Kemp

3

Control.FromHandle无法工作,因为您要查找的控件位于另一个进程中(因此位于另一个应用程序域中)。

您已经拥有了WindowHandle,但它的使用仅限于Win32 API。来自WinForms的任何内容都不会起作用。

您可以发送(WM_)消息,但很难传递数据。

选项

  1. 使用具有临时文件的低级别内容。

  2. 使用远程处理(WCF)


2

0

你不能直接调用另一个进程中的代码,你需要使用某种形式的进程间通信。

如果你只是在同一台计算机上由同一用户启动的进程之间进行通信,你可以使用窗口消息(使用WinAPI PostMessage和重写WndProc),否则我认为在.NET中使用远程调用是最容易的。


0

我使用nobugz指向的线程中描述的Microsoft.VisualBasic.dll库。是的,你可以在C#中使用它。你只需要重写OnStartupNextInstance并以最适合你的方式将命令行传递到程序中。

这比手动处理线程要容易得多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接