C# 如何从其 Win32 句柄获取 System.Windows.Form 实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/581795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:09:05  来源:igfitidea点击:

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

c#winformswinapiinteropipc

提问by Ian Kemp

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.

以下代码实现了一个简单的单例,以确保我的应用程序只能运行 1 个实例。但是,如果启动了另一个实例,我需要能够获取该实例的命令行参数,将它们传递给初始实例,然后终止第二个实例。

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.)

当我试图获取应用程序的第一个实例时,问题就出现了。找到该实例的主窗体的句柄后,我将其传递给该Control.FromHandle()方法,期望返回一个MainForm. 相反,返回值始终为null。(Control.FromChildHandle()给出相同的结果。)

Therefore, my question is simply: what am I doing wrong? And is this even possible in .NET?

因此,我的问题很简单:我做错了什么?这在 .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;
  }
}

采纳答案by Hans Passant

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

.NET 框架很好地支持单实例应用程序。检查此线程以获取完全满足您需要的示例。

回答by JaredPar

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.

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

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.

获得 Form 实例的唯一方法是使用 Remoting。但这将需要两个进程之间的合作,而这似乎不是您想要的。

回答by Henk Holterman

Control.FromHandle isn't going to work because the control you're looking for is in another process (and therefore in another appdomain).

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

You already have the WindowHandle but it's use is limited to the Win32 API. Nothing from WinForms is going to work.

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

You can send (WM_) messages but it's hard to get data across.

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

Options

选项

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

  2. use remoting (WCF)

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

  2. 使用远程处理 (WCF)

回答by Grzenio

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.codeproject.com/KB/cs/SingletonApplication.aspx

http://www.nathanm.com/csharp-wpf-singleton-application/

http://www.nathanm.com/csharp-wpf-singleton-application/

回答by Nir

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

如果您仅在同一台计算机上由同一用户启动的进程之间进行通信,则可以使用窗口消息(使用 WinAPI PostMessage 并覆盖 WndProc),否则我认为远程处理是 .net 中最容易使用的

回答by Jesse Smith

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.

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

This is a whole lot easier than messing around with the threads manually.

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