You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm关闭前延迟执行任务并更新RichTextBox问题求助

窗体关闭前的日志更新与延迟关闭问题解决

问题背景

我需要实现:当窗体在处理任务时被关闭,先更新RichTextBox显示日志、删除当前工作目录,再等待3秒后关闭窗体。但当前代码直接关闭窗体,无法实现延迟并显示日志;另外关闭窗体时更新日志会报错,因为RichTextBox已被释放,在Logger类的LogInternal方法的_richTextBox.SelectionStart = _richTextBox.TextLength;行抛出异常。

同时有两个疑问:

  • 关闭窗体是否视为取消操作?
  • 对话框是否会暂停窗体进程?

现有代码

窗体相关代码

private bool isCancelling = false;

FormClosing事件处理代码:

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (isCancelling)
    {
        // Already cancelling, let it continue
        return;
    }

    if (processingFrames)
    {
        // Set the flag to indicate that we are cancelling
        isCancelling = true;

        // Trigger the cancellation
        if (cancellationTokenSource != null)
        {
            cancellationTokenSource.Cancel();
        }

        // Wait for a few seconds before closing the form
        int waitTimeInSeconds = 3; // You can adjust this as needed
        for (int i = waitTimeInSeconds; i > 0; i--)
        {
            logger.Log($"Cancelling in {i} seconds...", Color.Yellow);
            await Task.Delay(1000);
        }

        // Log the cancellation and close the form
        logger.Log("Session Cancelled at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"), Color.Red);
        logger.Log("", Color.Black);

        DeleteCurrentSession(); // Delete the current session folder

        e.Cancel = false; // Allow the form to close
    }
    else
    {
        // If not processing frames, allow the form to close immediately
        DeleteCurrentSession(); // Delete the current session folder
        e.Cancel = false;
    }
}

Logger类代码

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

namespace Test
{
    public class Logger
    {
        private readonly RichTextBox _richTextBox;
        private bool saveToFile; // Added a flag to indicate whether to save to a file
        private string logFilePath = "";

        public Logger(RichTextBox richTextBox, bool saveToFile = false)
        {
            _richTextBox = richTextBox;
            this.saveToFile = saveToFile;

            // Initialize the log file path in the constructor
            InitializeLogFilePath();
        }

        private void InitializeLogFilePath()
        {
            string folderName = "Logs"; // Replace with the desired folder name
            string fileName = "log.txt"; // Replace with the desired file name
            // Set the log file path without creating the directory or file
            logFilePath = Path.Combine(Application.LocalUserAppDataPath, folderName, fileName);
        }


        public void Log(string message, Color textColor)
        {
            if (_richTextBox.InvokeRequired)
            {
                _richTextBox.Invoke((MethodInvoker)delegate
                {
                    LogInternal(message, textColor);
                });
            }
            else
            {
                LogInternal(message, textColor);
            }
        }

        private void LogInternal(string message, Color textColor)
        {
            _richTextBox.SelectionStart = _richTextBox.TextLength;
            _richTextBox.SelectionLength = 0;
            _richTextBox.SelectionColor = textColor;
            _richTextBox.AppendText(message + Environment.NewLine);
            _richTextBox.SelectionColor = _richTextBox.ForeColor;

            // Scroll to the caret (the end)
            _richTextBox.ScrollToCaret();

            if (saveToFile)
            {
                SaveToFile(message); // Save to a file if the flag is set
            }
        }

        private void SaveToFile(string message)
        {
            try
            {
                // Ensure the directory exists, and create it if it doesn't
                string folderName = "Logs"; // Replace with the desired folder name
                string fileName = "log.txt"; // Replace with the desired file name

                // Application.LocalUserAppDataPath
                logFilePath = Path.Combine(Application.LocalUserAppDataPath, folderName, fileName);

                Directory.CreateDirectory(Path.Combine(Application.LocalUserAppDataPath, folderName));

                // Append the message to the log file
                File.AppendAllText(logFilePath, message + Environment.NewLine);
            }
            catch (Exception ex)
            {
                // Handle any exceptions related to file I/O here
                Console.WriteLine("Error saving to log file: " + ex.Message);
            }
        }

        public string LogFilePath()
        {
            return logFilePath;
        }
    }
}

解决方案

1. 修复延迟关闭不生效问题

问题核心是未先阻止窗体立即关闭。修改后的FormClosing事件代码:

private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (isCancelling)
    {
        return;
    }

    if (processingFrames)
    {
        e.Cancel = true; // 先阻止窗体关闭
        isCancelling = true;

        cancellationTokenSource?.Cancel();

        int waitTimeInSeconds = 3;
        for (int i = waitTimeInSeconds; i > 0; i--)
        {
            logger.Log($"Cancelling in {i} seconds...", Color.Yellow);
            await Task.Delay(1000);
        }

        logger.Log($"Session Cancelled at {DateTime.Now:dd/MM/yyyy HH:mm}", Color.Red);
        logger.Log("", Color.Black);

        DeleteCurrentSession();

        // 所有操作完成后手动关闭窗体
        this.Close();
    }
    else
    {
        DeleteCurrentSession();
        e.Cancel = false;
    }
}

2. 修复RichTextBox已释放的异常问题

在Logger类中增加控件状态检查,避免操作已释放的控件:

public void Log(string message, Color textColor)
{
    if (_richTextBox.IsDisposed || !_richTextBox.IsHandleCreated)
    {
        if (saveToFile)
        {
            SaveToFile(message);
        }
        return;
    }

    if (_richTextBox.InvokeRequired)
    {
        _richTextBox.Invoke((MethodInvoker)delegate
        {
            LogInternal(message, textColor);
        });
    }
    else
    {
        LogInternal(message, textColor);
    }
}

private void LogInternal(string message, Color textColor)
{
    if (_richTextBox.IsDisposed || !_richTextBox.IsHandleCreated)
    {
        if (saveToFile)
        {
            SaveToFile(message);
        }
        return;
    }

    _richTextBox.SelectionStart = _richTextBox.TextLength;
    _richTextBox.SelectionLength = 0;
    _richTextBox.SelectionColor = textColor;
    _richTextBox.AppendText(message + Environment.NewLine);
    _richTextBox.SelectionColor = _richTextBox.ForeColor;
    _richTextBox.ScrollToCaret();

    if (saveToFile)
    {
        SaveToFile(message);
    }
}

疑问解答

  • 关闭窗体是否视为取消操作:是的,用户关闭正在处理任务的窗体属于主动取消任务的场景,现有逻辑将其作为取消操作处理是合理的。
  • 对话框是否会暂停窗体进程:模态对话框(如MessageBox.Show())会阻塞当前UI线程,直到用户关闭对话框,但后台非UI线程的任务不受影响;非模态对话框则不会暂停UI线程。你的场景中,用延迟+日志更新的方式无需用户额外操作,体验更友好。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:13:10