尝试运行第二个实例时激活隐藏的WPF应用程序 [英] Activate a hidden wpf application when trying to run a second instance
问题描述
我正在开发一个wpf应用程序,而不是在用户关闭按钮时退出该应用程序,而是将其最小化到托盘上(类似于Google Talk).
I am working on a wpf application where instead of exiting the app when user closes the button I am minimizing it to the tray(similar to google talk).
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
我需要的是,如果用户忘记了该应用程序的一个实例并尝试打开一个新实例,则必须关闭第二个实例并将我的应用程序设置为前台应用程序.如果应用处于最小化状态(未隐藏),则可以执行此操作.我正在使用以下代码
What I need is if user forgets that there is an instance of the app and tries to open a new instance I have to shut down the second instance and set my application as the foreground app. If the app is in minimized state (not hidden) I am able to do this. I am using the following code
protected override void OnStartup(StartupEventArgs e)
{
Process currentProcess = Process.GetCurrentProcess();
var runningProcess = (from process in Process.GetProcesses()
where
process.Id != currentProcess.Id &&
process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal)
select process).FirstOrDefault();
if (runningProcess != null)
{
Application.Current.Shutdown();
ShowWindow(runningProcess.MainWindowHandle, 5);
ShowWindow(runningProcess.MainWindowHandle, 3);
}
}
[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
将应用程序最小化时,它对于MainWindowHandle具有一些唯一的值.当我隐藏应用程序时, runningProcess 的MainWindowHandle显示为0.我认为这就是为什么我的应用程序处于隐藏状态时无法打开,但不知道如何解决的原因.
When the app is minimized it has some unique value for MainWindowHandle. When I hide the app, the MainWindowHandle of runningProcess is showing as 0. I think this is why my application is not opening when it is in hidden state, but don't know how to fix it.
如果我需要发布更多代码或澄清任何事情,请告诉我.预先谢谢你.
Tell me if I need to post more code or clarify anything. Thank you in advance.
推荐答案
当我隐藏应用程序时,runningProcess的MainWindowHandle显示为0
When I hide the app, the MainWindowHandle of runningProcess is showing as 0
您是对的.如果某个进程没有与之关联的图形界面(隐藏/最小化),则 MainWindowHandle
值为零.
You're right. If a process doesn't have a graphical interface associated with it (hidden/ minimized) then the MainWindowHandle
value is zero.
作为解决方法,您可以尝试使用 EnumDesktopWindows
函数枚举所有打开的窗口,并为隐藏窗口获取 HANDLE
,并将其进程ID与隐藏/最小化的窗口进程进行比较ID.
As workaround, you could try getting the HANDLE
for the hidden window by enumerating all open windows using EnumDesktopWindows
function and compare its process id with the hidden/ minimized windows's process id.
更新
WPF的WIN32窗口的行为与标准WIN32窗口略有不同.它的类名称由单词 HwndWrapper
,在其中创建的AppDomain的名称以及唯一的随机 Guid
(每次启动都会更改)组成,例如, HwndWrapper [WpfApp.exe ;; 4d426cdc-31cf-4e4c-88c7-ede846ab6d44] .
The WPF's WIN32 window has a bit different behavior than the standard WIN32 window. It has the class name composed of the word HwndWrapper
, the name of AppDomain it was created in, and a unique random Guid
(which changes on every launch), e.g., HwndWrapper[WpfApp.exe;;4d426cdc-31cf-4e4c-88c7-ede846ab6d44].
更新2
通过使用 Hide()
方法隐藏WPF的窗口时,它将在内部调用 UpdateVisibilityProperty(Visibility.Hidden)
,从而依次为 UIElement
设置为false.当我们调用 WPF Window
的 Show()
方法时,将调用 UpdateVisibilityProperty(Visibility.Visible)
,并且的内部可见性标志> UIElement
被切换.
When WPF's window is hidden by using the Hide()
method, it internally calls UpdateVisibilityProperty(Visibility.Hidden)
, which in turns set the internal visibility flag for UIElement
to false. When we call the Show()
method of WPF Window
, UpdateVisibilityProperty(Visibility.Visible)
is called, and the internal visibility flag for UIElement
is toggled.
当我们使用 ShowWindow()
显示WPF窗口时,不会触发 UpdateVisibilityProperty()
方法,因此内部可见性标记不会反转(这导致带有黑色背景的窗口).
When we show the WPF window using the ShowWindow()
, the UpdateVisibilityProperty()
method is not trigerred, thus the internal visibility flag does not get reversed (which causes the window to be displayed with black backround).
通过查看 WPF窗口
内部实现,这是切换内部可见性标志的唯一方法,而无需调用 Show()
或 Hide()
方法是通过发送 WM_SHOWWINDOW
消息.
By looking at the WPF Window
internal implementation, the only way to toggle the internal visiblity flag, without calling the Show()
or Hide()
method, is by sending a WM_SHOWWINDOW
message.
const int GWL_EXSTYLE = (-20);
const uint WS_EX_APPWINDOW = 0x40000;
const uint WM_SHOWWINDOW = 0x0018;
const int SW_PARENTOPENING = 3;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc ewp, int lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll")]
private static extern uint GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern uint GetWindowText(IntPtr hWnd, StringBuilder lpString, uint nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
static bool IsApplicationWindow(IntPtr hWnd) {
return (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_APPWINDOW) != 0;
}
static IntPtr GetWindowHandle(int pid, string title) {
var result = IntPtr.Zero;
EnumWindowsProc enumerateHandle = delegate(IntPtr hWnd, int lParam)
{
int id;
GetWindowThreadProcessId(hWnd, out id);
if (pid == id) {
var clsName = new StringBuilder(256);
var hasClass = GetClassName(hWnd, clsName, 256);
if (hasClass) {
var maxLength = (int)GetWindowTextLength(hWnd);
var builder = new StringBuilder(maxLength + 1);
GetWindowText(hWnd, builder, (uint)builder.Capacity);
var text = builder.ToString();
var className = clsName.ToString();
// There could be multiple handle associated with our pid,
// so we return the first handle that satisfy:
// 1) the handle title/ caption matches our window title,
// 2) the window class name starts with HwndWrapper (WPF specific)
// 3) the window has WS_EX_APPWINDOW style
if (title == text && className.StartsWith("HwndWrapper") && IsApplicationWindow(hWnd))
{
result = hWnd;
return false;
}
}
}
return true;
};
EnumDesktopWindows(IntPtr.Zero, enumerateHandle, 0);
return result;
}
用法示例
...
if (runningProcess.MainWindowHandle == IntPtr.Zero) {
var handle = GetWindowHandle(runningProcess.Id, runningProcess.MainWindowTitle);
if (handle != IntPtr.Zero) {
// show window
ShowWindow(handle, 5);
// send WM_SHOWWINDOW message to toggle the visibility flag
SendMessage(handle, WM_SHOWWINDOW, IntPtr.Zero, new IntPtr(SW_PARENTOPENING));
}
}
...
这篇关于尝试运行第二个实例时激活隐藏的WPF应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!