尝试运行第二个实例时激活隐藏的WPF应用程序 [英] Activate a hidden wpf application when trying to run a second instance

查看:54
本文介绍了尝试运行第二个实例时激活隐藏的WPF应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆