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

Raj*_*123 5 c# wpf

我正在开发一个wpf应用程序,而不是在用户关闭按钮时退出应用程序我将其最小化到托盘(类似于谷歌谈话).

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

        this.Hide();
    }
Run Code Online (Sandbox Code Playgroud)

我需要的是,如果用户忘记了应用程序的实例并尝试打开新实例,我必须关闭第二个实例并将我的应用程序设置为前台应用程序.如果应用程序处于最小化状态(未隐藏),我可以执行此操作.我使用以下代码

      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);
Run Code Online (Sandbox Code Playgroud)

当应用程序最小化时,它具有MainWindowHandle的一些唯一值.当我隐藏应用程序时,runningProcess的MainWindowHandle 显示为0.我认为这就是为什么我的应用程序在处于隐藏状态时不打开,但不知道如何修复它.

告诉我是否需要发布更多代码或澄清任何内容.先感谢您.

Iro*_*eek 9

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

你是对的.如果进程没有与之关联的图形界面(隐藏/最小化),则该MainWindowHandle值为零.

作为解决方法,您可以尝试HANDLE使用EnumDesktopWindows函数枚举所有打开的窗口来获取隐藏窗口,并将其进程ID与隐藏/最小化窗口的进程ID进行比较.

更新

WPF的WIN32窗口与标准WIN32窗口的行为略有不同.它的类名由单词HwndWrapper,创建它的AppDomain名称和唯一随机Guid(每次启动时更改)组成,例如,HwndWrapper [WpfApp.exe ;; 4d426cdc-31cf-4e4c-88c7-ede846ab6d44].

更新2

当使用该Hide()方法隐藏WPF的窗口时,它会在内部调用UpdateVisibilityProperty(Visibility.Hidden),然后将内部可见性标志设置UIElement为false.当我们调用Show()方法时WPF Window,UpdateVisibilityProperty(Visibility.Visible)调用,并UIElement切换内部可见性标志.

当我们使用the显示WPF窗口时ShowWindow(),该UpdateVisibilityProperty()方法不会被触发,因此内部可见性标志不会被反转(这会导致窗口以黑色背景显示).

通过查看WPF Window内部实现,在不调用Show()or Hide()方法的情况下切换内部visiblity标志的唯一方法是发送WM_SHOWWINDOW消息.

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;
}
Run Code Online (Sandbox Code Playgroud)

用法示例

...
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));
  }
}
...
Run Code Online (Sandbox Code Playgroud)

  • 使用`SendMessage()`的黑窗口修复对我来说不起作用(不知道为什么).然而,通过监听窗口的`Activated`事件(或覆盖`OnActivated`)(当窗口显示为`ShowWindow`时调用),然后将其设置为可见(如果不是),我能够修复它那里. (2认同)