C# 如何在主窗口关闭时重新创建windows窗体应用程序

C# 如何在主窗口关闭时重新创建windows窗体应用程序,c#,.net,multithreading,winforms,C#,.net,Multithreading,Winforms,我有windows窗体应用程序。当它关闭时,主窗口被释放,然后当用户点击托盘窗口被重新创建-它工作。然而,当我在使用FileSystemWatcher时尝试将应用程序恢复时,我遇到了一个问题。这个想法很简单,当文件被更改时,应用程序被带回来。但在这种情况下,应用程序会出现,但会挂起,然后消失。窗口的形状恢复了,但没有响应,在窗口上移动鼠标会显示应用程序正在“思考”或“挂起”,应用程序的任务栏上没有图标。 我的猜测是,这与线程/同步有关,但我不知道如何使它再次工作。我尝试了许多与线程相关的不同方法

我有windows窗体应用程序。当它关闭时,主窗口被释放,然后当用户点击托盘窗口被重新创建-它工作。然而,当我在使用FileSystemWatcher时尝试将应用程序恢复时,我遇到了一个问题。这个想法很简单,当文件被更改时,应用程序被带回来。但在这种情况下,应用程序会出现,但会挂起,然后消失。窗口的形状恢复了,但没有响应,在窗口上移动鼠标会显示应用程序正在“思考”或“挂起”,应用程序的任务栏上没有图标。 我的猜测是,这与线程/同步有关,但我不知道如何使它再次工作。我尝试了许多与线程相关的不同方法,但都失败了。我无法在UI线程中再次创建此窗口,因为据我所知,我可以编写
\u mainWindow.BeginInvoke
,但在创建此表单之前,我不能这样做。 我创建了一个演示该问题的最小工作示例。可从以下网址获取:

Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GettingFormsUp
{
    static class Program
    {
        private static bool hideFlag = true;

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var initApplicationContext = new InitApplicationContext();

            Console.WriteLine();
            var fileSystemWatcher = new FileSystemWatcher(Path.GetFullPath("../.."), "watcher");
            fileSystemWatcher.Changed += (sender, args) =>
            {
                Console.WriteLine("Watched");
                initApplicationContext.Show();
            };
            fileSystemWatcher.EnableRaisingEvents = true;

            Application.Run(initApplicationContext);
        }

        private class InitApplicationContext : ApplicationContext
        {
            private static MainWindow _mainWindow;

            public InitApplicationContext()
            {
                NewForm();
            }

            private void NewForm()
            {
                Console.WriteLine("Creating new MainWindow");
                _mainWindow = new MainWindow();
                _mainWindow.Invoke((MethodInvoker) delegate
                {
                    _mainWindow.Show(); 
                });
            }

            public void Show()
            {
                if (_mainWindow == null || _mainWindow.IsDisposed)
                {
                    NewForm();
                }
                else if (!_mainWindow.Visible)
                {
                    _mainWindow.BeginInvoke((MethodInvoker) delegate
                    {
                        Console.WriteLine("showing");
                        _mainWindow.Show();
                    });
                }
            }

            public void Delete()
            {
                if (_mainWindow != null && !_mainWindow.IsDisposed)
                {
                    _mainWindow.Dispose();
                }
            }
        }
    }
}
MainWindow.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GettingFormsUp
{
    public class MainWindow : Form
    {
        public MainWindow()
        {
            CreateHandle();
            InitializeComponent();
        }

        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Form1";
        }
    }
}

如何使其工作?

代码的问题是,当您再次创建窗口时,它是在用于引发
FileSystemWatcher.Changed
事件的线程中创建的,该事件是一个后台线程,而不是
应用程序.Run()
方法用于输出窗口消息的线程。因此,背景线程最终拥有该窗口

窗口的消息被发送到拥有它的线程,但该线程没有消息泵循环,因此窗口永远看不到消息。这些消息非常重要,因为它们处理与窗口交互的所有内容,包括用户输入和绘制窗口所涉及的所有内容(除了Windows桌面管理器处理的最小值)。这些消息甚至用于处理调用
Control.Invoke()
Control.BeginInvoke()
之类的事情。如果没有消息泵送循环,
BeginInvoke()
委托将永远不会被处理,
Invoke()
甚至永远不会返回

有多种方法可以解决这个问题。首先不处理窗口可能是一个选项(您可以覆盖
OnFormClosing()
,取消事件并隐藏窗口)。然后,对
\u main window.Invoke()
的调用将始终转到正确的线程,并按预期工作:

public partial class MainWindow : Form
{
    // ...

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        Visible = false;
        e.Cancel = true;

        base.OnFormClosing(e);
    }

    // ...
}
或者,您可以捕获主线程的
SynchronizationContext
对象,该对象可用于执行与
Control.Invoke()相同的操作。该技术的关键在于,在线程中创建Winforms组件之前,该线程不会有
SynchronizationContext
。在代码中,表单是在创建
InitApplicationContext
对象时创建的,因此在此之后捕获
SynchronizationContext
将起作用:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    var initApplicationContext = new InitApplicationContext();
    SynchronizationContext context = SynchronizationContext.Current;

    Console.WriteLine();
    string path = Path.GetFullPath("../..");
    Console.WriteLine($"Watching {path}");
    var fileSystemWatcher = new FileSystemWatcher(path, "watcher");
    fileSystemWatcher.Changed += (sender, args) =>
    {
        context.Post(o =>
        {
            Console.WriteLine("Watched");
            initApplicationContext.Show();
        }, null);
    };
    fileSystemWatcher.EnableRaisingEvents = true;

    Application.Run(initApplicationContext);
}
如果采用这种方法,那么在创建窗口时当然不需要调用
Control.Invoke()
。无论如何,第一次使用它是多余的,因为您将使用
FileSystemWatcher中的
SynchronizationContext
。在随后的实例中更改了事件处理程序,因此也不需要它:

private void NewForm()
{
    Console.WriteLine("Creating new MainWindow");
    _mainWindow = new MainWindow();
    _mainWindow.Show();
}

当你完全期待它回来的时候,你为什么要处理它?只需使用正常的系统托盘窗口行为,并将激活逻辑添加到FileWatcher处理。为了不使用系统资源,应用程序不需要。在窗体窗口上单击x使其释放。托盘是独立于表单的组件。但如果窗口不断返回,这只是暂时的节省,除非机器的资源非常有限,否则您可能会过度优化。用户手动重新打开窗口或触发文件监视程序的频率如何?感谢您的全面响应。它解决了我的问题,此外,我了解问题所在。实际上,我很接近SynchronizationContext的解决方案,但最终我失败了,可能是因为我在创建窗口之前尝试分配了它,此外,我在SynchronizationContext上“运行”代码时遇到了问题,现在我知道我可以使用“Post”来完成它。:)