CSharp - 尝试运行第二个实例时激活隐藏的wpf应用程序

  显示原文与译文双语对照的内容

在用户关闭按钮时,我正在处理wpf应用程序,当用户关闭按钮时,我将它减少。


 void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
 {
 e.Cancel = true;

 this.Hide();
 }

我需要的是,如果用户忘记了应用实例并尝试打开一个新实例,我必须关闭第二个实例。 如果应用程序处于最小化状态( 未隐藏),我就能做到这一点。 我正在使用以下代码


 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. 我认为这就是我的应用程序在隐藏状态下没有打开的原因,但是不知道如何修复它。

告诉我是否需要发布更多代码或者澄清任何内容。 先 谢谢 你 了.

时间: 作者:

隐藏该应用程序时,runningProcess的MainWindowHandle显示为 0

如果进程没有与它的关联的图形界面,则( 隐藏/最小化) 值为零。

使用workaround函数,可以通过枚举所有打开的并将它的进程id与隐藏/最小化 Windows id的进程id进行比较来尝试获得隐藏窗口的HANDLE

更新

wpf窗口的WIN32 与标准 WIN32 窗口的行为有一点不同。 它具有组成 word HwndWrapper,AppDomain AppDomain created created的类名,以及一个唯一的随机 Guid ( 每次发射的变化) 。比如 。HwndWrapper [WpfApp.exe;;4d426cdc-31cf-4e4c-88c7-ede846ab6d44] 。

更新2

使用 Hide() 方法隐藏wpf窗口时,将在内部调用 UpdateVisibilityProperty(Visibility.Hidden) 然后,将 UIElement的内部可见性标志设置为 false 。 当我们调用 WPF WindowShow() 方法时 UpdateVisibilityProperty(Visibility.Visible) ,则 UIElement的内部可见性标志将被切换。

当使用 ShowWindow() 显示WPF窗口时,UpdateVisibilityProperty() 方法不是 trigerred,因此内部可见性标志不会得到反向的( 这将使窗口显示为黑色 backround ) 。

通过查看 WPF Window 内部实现,切换内部,标志的唯一方法是,不调用 Show() 或者 Hide() 方法。


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

...