Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 尝试运行第二个实例时激活隐藏的wpf应用程序_C#_Wpf - Fatal编程技术网

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

C# 尝试运行第二个实例时激活隐藏的wpf应用程序,c#,wpf,C#,Wpf,我正在开发一个wpf应用程序,当用户关闭按钮时,我不是退出应用程序,而是将其最小化到托盘(类似于google talk) 我需要的是,如果用户忘记有一个应用程序实例,并试图打开一个新实例,我必须关闭第二个实例,并将我的应用程序设置为前台应用程序。如果应用程序处于最小化状态(未隐藏),我可以这样做。我正在使用以下代码 protected override void OnStartup(StartupEventArgs e) { Proce

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

我需要的是,如果用户忘记有一个应用程序实例,并试图打开一个新实例,我必须关闭第二个实例,并将我的应用程序设置为前台应用程序。如果应用程序处于最小化状态(未隐藏),我可以这样做。我正在使用以下代码

      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

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

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

更新

...
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的WIN32窗口的行为与标准的WIN32窗口稍有不同。它的类名由单词
HwndWrapper
、创建它的AppDomain的名称和唯一的随机
Guid
(每次启动时都会更改),例如HwndWrapper[WpfApp.exe;;4d426cdc-31cf-4e4c-88c7-ede846ab6d44]

更新2

当使用
Hide()
方法隐藏WPF的窗口时,它会在内部调用
UpdateVisibilityProperty(Visibility.hidden)
,从而将
UIElement
的内部可见性标志设置为false。调用
WPF窗口的
Show()
方法时,调用
UpdateVisibilityProperty(Visibility.Visible)
,并切换
UIElement
的内部可见性标志

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

通过查看
WPF窗口
内部实现,在不调用
Show()
Hide()
方法的情况下切换内部可视性标志的唯一方法是发送
WM\u 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;
}
用法示例

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

谢谢铁人,这太棒了。我只是在学习c#,为了让它发挥作用,我挣扎了一段时间。此外,我不能“添加评论”,因为这里没有足够的声誉,所以这篇文章。我正在使用WPF.NET5.0。 我四处搜索以实现此功能,因此对于其他新手,他们也需要在程序中使用类似以下内容的内容来接收消息(抱歉,不确定我从哪个页面复制了此消息,他们中的很多人(个人需要制作自己的Mainwindow\u加载事件处理程序)

在我的情况下,你提到的“站在前面”提示也是需要的,以下是需要的: (来自) 请将此内容放入“声明”部分:

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
并将以下内容放在“SendMessage(handle,WM_SHOWWINDOW,IntPtr.Zero,new IntPtr(SW_PARENTOPENING));”语句之后:

SetForegroundWindow(手柄)

如果不这样做,激活的窗口只会隐藏在其他窗口后面,必须通过在任务栏中手动搜索来找到它。
因此,现在我终于有了一个非隐藏窗口,但现在需要看看隐藏窗口需要什么,因为这是我的真正目标。

继我的早期文章之后,引用IronGeek的早期评论,问题是“如果一个进程没有与其相关联的图形界面(隐藏/最小化)然后MainWindowHandle值为零。因此,任何传递隐藏窗口句柄的尝试都是注定的,因为它不存在

因此,我找到了一种解决方法,尽管它需要目标进程定期检查是否存在新消息。因此,这仍然不理想,但它在2021年(WPF、.Net 5.0)对我有效,并且不需要导入user32.dll。相反,它执行一种临时的进程间通信(IPC)将MainWindowTitle用作被动发送消息的容器。 MainWindowTitle在运行时是可设置的,并且可以从其他进程查看,因此它可以像进程间变量一样使用。这是我下面的整个解决方案,请注意,需要将其发布到本地文件夹,以查看它如何运行,因为要运行多个实例

<Window x:Class="TitleComsTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TitleComsTest"
    mc:Ignorable="d"
    Name="TitleComsTest" Title="TitleComsTest" Height="400" Width="600" 
    WindowStartupLocation = "CenterScreen" Closing="TitleComsTest_Closing" Visibility="Hidden">
<Grid>
    <TextBox Name ="TextBox1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="230" Width="460"/>
    <Button Name="QuitButton" Content=" Really Quit " HorizontalAlignment="Center" Margin="0,0,0,30" VerticalAlignment="Bottom" Click="QuitButton_Click"/>
</Grid>

背后的代码:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;

namespace TitleComsTest
{
    public partial class MainWindow : Window
    {
        //string for MainWindowTitle when first instance and visible:
        const string my_Visible_exe_Name = "TitleComstest";
        //string for MainWindowTitle when first instance and Hidden:
        const string my_Hidden_exe_Name = "TitleComstest...";
        //string for MainWindowTitle when 2nd instance :
        const string my_exe_Name_Flag = "TitleComstest, Please-Wait";   

        bool reallyCloseThisProgram = false;
        private DispatcherTimer timer1, timer2;
        
        public MainWindow()
        {
            InitializeComponent();

            //Get an array of processes with the chosen name
            Process[] TitleComstests = Process.GetProcessesByName(my_Visible_exe_Name);
            if (TitleComstests.Length > 1)
            {   //Then this is not the first instance
                for (int i = 0; i < TitleComstests.Length; i++)
                {
                    if (TitleComstests[i].MainWindowTitle == my_Visible_exe_Name)
                    {   //The first instance is visible as the MainWindowTitle has been set to the visible name
                        Close(); //Quit - nothing to do but close the new instance
                    }
                }
                //The first instance is hidden, so set MainWindowTitle so the first instance can see it and react
                this.Title = my_exe_Name_Flag;
                this.WindowState = WindowState.Minimized; //Minimize the window to avoid having two windows shown at once
                this.Visibility = Visibility.Visible;     //The second instance needs to be visible (minimized is enough) to be seen
                StartTimerQuit(4000); //arbitrary time, needs to be longer than 2000ms which is the checking period - see StartTimerLook(2000);
            }
            else
            {
                TextBox1.Text = "This is Multi-instance demo using the 'MainWindowTitle' to send messages\r\nto the first (hidden) instance to wake it up.";
                TextBox1.Text += "\r\n\r\nThis demo requires the program be published to a local folder and \r\nnot run in the debugger.";
                TextBox1.Text += "\r\n\r\nYou can type here to mark this instance: _____________ \r\n\r\nand then hide me by clicking top right close window 'X'";
                TextBox1.Text += "\r\n\r\nOnce closed then start the program again to see the 1st instance pop up.";
                TextBox1.Text += "\r\n\r\nFinally use the 'Really Quit' button to end this demo.";
                this.Visibility = Visibility.Visible;
            }
        }

        private void StartTimerQuit(Int32 interval) //Timer to Quit setup and start
        {
            timer1 = new DispatcherTimer();   timer1.Tick += timerQuit_Tick;
            timer1.Interval = new TimeSpan(0, 0, 0, 0, interval);   timer1.Start();
        }
        private void timerQuit_Tick(object sender, EventArgs e)
        {
            reallyCloseThisProgram = true;   Close(); 
        }

        private void TitleComsTest_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!reallyCloseThisProgram)
            {
                e.Cancel = true;
                this.Title = my_Hidden_exe_Name; //Set the Title text to flag a hidden state
                this.Visibility = Visibility.Hidden;
                //Start checking every 2 secs at the process names - could be faster but this is a constant background process
                StartTimerLook(2000);
            }
        }

        private void StartTimerLook(Int32 interval) //Timer to look for new instances setup and start
        {
            timer2 = new DispatcherTimer();  timer2.Tick += timerLook_Tick;
            timer2.Interval = new TimeSpan(0, 0, 0, 0, interval);   timer2.Start();
        }

        private void timerLook_Tick(object sender, EventArgs e)
        {   //Every timer interval check to see if a process is present with the Ttile name flag 
            Process[] myNameFlagProcesses = Process.GetProcessesByName(my_Visible_exe_Name);

            for (int i = 0; i < myNameFlagProcesses.Length; i++)
            {
                if (myNameFlagProcesses[i].MainWindowTitle == my_exe_Name_Flag) //If name flag is seen ...
                {   //... then wake up
                    TextBox1.Text += "\r\n Saw the other window";
                    this.Visibility = Visibility.Visible;
                    this.Title = my_Visible_exe_Name; //Set the Title text to flag a visible state
                    this.Show();
                    this.Activate();
                    timer2.Stop();
                }
            }

        }

        private void QuitButton_Click(object sender, RoutedEventArgs e)
        {
            reallyCloseThisProgram = true;   Close();
        }
    }

}
使用系统;
使用系统诊断;
使用System.Windows;
使用System.Windows.Threading;
名称空间标题测试
{
公共部分类主窗口:窗口
{
//当第一个实例和可见时,MainWindowTitle的字符串:
常量字符串my\u Visible\u exe\u Name=“TitleComstest”;
//第一个实例和隐藏时MainWindowTitle的字符串:
常量字符串my_Hidden_exe_Name=“TitleComstest…”;
//第二个实例时MainWindowTitle的字符串:
常量字符串my\u exe\u Name\u Flag=“TitleComstest,请稍候”;
bool reallyCloseThisProgram=false;
私人调度员定时器1、定时器2;
公共主窗口()
{
初始化组件();
//获取具有所选名称的进程数组
Process[]TitleComstests=Process.getProcesssByName(我的\u可见\u执行\u名称);
如果(TitleComstests.Length>1)
{//那么这不是第一个实例
对于(int i=0;iusing System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;

namespace TitleComsTest
{
    public partial class MainWindow : Window
    {
        //string for MainWindowTitle when first instance and visible:
        const string my_Visible_exe_Name = "TitleComstest";
        //string for MainWindowTitle when first instance and Hidden:
        const string my_Hidden_exe_Name = "TitleComstest...";
        //string for MainWindowTitle when 2nd instance :
        const string my_exe_Name_Flag = "TitleComstest, Please-Wait";   

        bool reallyCloseThisProgram = false;
        private DispatcherTimer timer1, timer2;
        
        public MainWindow()
        {
            InitializeComponent();

            //Get an array of processes with the chosen name
            Process[] TitleComstests = Process.GetProcessesByName(my_Visible_exe_Name);
            if (TitleComstests.Length > 1)
            {   //Then this is not the first instance
                for (int i = 0; i < TitleComstests.Length; i++)
                {
                    if (TitleComstests[i].MainWindowTitle == my_Visible_exe_Name)
                    {   //The first instance is visible as the MainWindowTitle has been set to the visible name
                        Close(); //Quit - nothing to do but close the new instance
                    }
                }
                //The first instance is hidden, so set MainWindowTitle so the first instance can see it and react
                this.Title = my_exe_Name_Flag;
                this.WindowState = WindowState.Minimized; //Minimize the window to avoid having two windows shown at once
                this.Visibility = Visibility.Visible;     //The second instance needs to be visible (minimized is enough) to be seen
                StartTimerQuit(4000); //arbitrary time, needs to be longer than 2000ms which is the checking period - see StartTimerLook(2000);
            }
            else
            {
                TextBox1.Text = "This is Multi-instance demo using the 'MainWindowTitle' to send messages\r\nto the first (hidden) instance to wake it up.";
                TextBox1.Text += "\r\n\r\nThis demo requires the program be published to a local folder and \r\nnot run in the debugger.";
                TextBox1.Text += "\r\n\r\nYou can type here to mark this instance: _____________ \r\n\r\nand then hide me by clicking top right close window 'X'";
                TextBox1.Text += "\r\n\r\nOnce closed then start the program again to see the 1st instance pop up.";
                TextBox1.Text += "\r\n\r\nFinally use the 'Really Quit' button to end this demo.";
                this.Visibility = Visibility.Visible;
            }
        }

        private void StartTimerQuit(Int32 interval) //Timer to Quit setup and start
        {
            timer1 = new DispatcherTimer();   timer1.Tick += timerQuit_Tick;
            timer1.Interval = new TimeSpan(0, 0, 0, 0, interval);   timer1.Start();
        }
        private void timerQuit_Tick(object sender, EventArgs e)
        {
            reallyCloseThisProgram = true;   Close(); 
        }

        private void TitleComsTest_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!reallyCloseThisProgram)
            {
                e.Cancel = true;
                this.Title = my_Hidden_exe_Name; //Set the Title text to flag a hidden state
                this.Visibility = Visibility.Hidden;
                //Start checking every 2 secs at the process names - could be faster but this is a constant background process
                StartTimerLook(2000);
            }
        }

        private void StartTimerLook(Int32 interval) //Timer to look for new instances setup and start
        {
            timer2 = new DispatcherTimer();  timer2.Tick += timerLook_Tick;
            timer2.Interval = new TimeSpan(0, 0, 0, 0, interval);   timer2.Start();
        }

        private void timerLook_Tick(object sender, EventArgs e)
        {   //Every timer interval check to see if a process is present with the Ttile name flag 
            Process[] myNameFlagProcesses = Process.GetProcessesByName(my_Visible_exe_Name);

            for (int i = 0; i < myNameFlagProcesses.Length; i++)
            {
                if (myNameFlagProcesses[i].MainWindowTitle == my_exe_Name_Flag) //If name flag is seen ...
                {   //... then wake up
                    TextBox1.Text += "\r\n Saw the other window";
                    this.Visibility = Visibility.Visible;
                    this.Title = my_Visible_exe_Name; //Set the Title text to flag a visible state
                    this.Show();
                    this.Activate();
                    timer2.Stop();
                }
            }

        }

        private void QuitButton_Click(object sender, RoutedEventArgs e)
        {
            reallyCloseThisProgram = true;   Close();
        }
    }

}