如何从其 Win32 句柄获取 System.Windows.Form 实例?
以下代码实现了一个简单的单例,确保我的应用程序只能运行 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;
}
}
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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
.NET 框架很好地支持单实例应用程序。 检查此线程 的示例完全符合您的需要。
Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.
尝试以下
编辑
重新阅读您的问题,并意识到您正在另一个进程中查看句柄。 无法将另一个进程中的句柄转换为当前进程中的 Form 实例。 我的解决方案仅适用于同一进程中的句柄。
获取 Form 实例的唯一方法是使用 Remoting。 但这需要两个过程的合作,这似乎不是您所寻求的。
Try the following
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.
Control.FromHandle 不起作用,因为您要查找的控件位于另一个进程中(因此位于另一个应用程序域中)。
您已经有了 WindowHandle,但它的使用仅限于 Win32 API。 WinForms 中的任何内容都不起作用。
您可以发送(WM_)消息,但很难传递数据。
选项
使用低级的东西
临时文件。
使用远程处理 (WCF)
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)
您确实正在尝试实现一个单例应用程序。 互联网上有一些例子(抱歉,我自己还没有真正尝试过),例如
http://www.codeproject.com/KB/cs/SingletonApplication.aspx
http://www.nathanm.com/csharp-wpf-singleton-application/
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/
您不能直接调用另一个进程中的代码,您需要使用某种形式的进程间通信
如果您仅在同一台计算机上同一用户启动的进程之间进行通信,则可以使用窗口消息(使用 WinAPI PostMessage 并覆盖 WndProc ),否则我认为远程处理是.net中最容易使用的
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
我使用 nobugz 指向的线程中描述的 Microsoft.VisualBasic.dll 库。 是的,您可以在 C# 中使用它。 您只需重写 OnStartupNextInstance 并以最适合您的方式将命令行传递到您的程序中。
这比手动搞乱线程要容易得多。
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.