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

关闭控制台窗口时保持后台服务程序运行的解决方案咨询

解决控制台窗口关闭导致后台程序终止的问题

当你通过AllocConsole创建控制台窗口后,默认情况下点击窗口红叉或按下Ctrl+C会触发进程终止信号,导致整个后台服务跟着退出。要实现仅关闭控制台窗口、保留后台服务运行的效果,需要拦截这两类事件,手动控制控制台的释放逻辑。

解决方案步骤

  1. 拦截控制台窗口关闭事件:获取控制台窗口句柄,替换窗口过程处理WM_CLOSE消息,调用FreeConsole而非让系统终止进程。
  2. 处理Ctrl+C信号:注册控制台事件回调,捕获CTRL_C_EVENT时执行控制台关闭逻辑,忽略默认的终止行为。
  3. 修改日志窗口显示逻辑:在ShowLoggingWindow中整合上述拦截逻辑,确保控制台关闭时仅释放资源。

修改后的完整代码

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

class Program
{
    // 控制台相关API
    [DllImport("kernel32.dll")]
    private static extern bool AllocConsole();

    [DllImport("kernel32.dll")]
    private static extern bool FreeConsole();

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetConsoleWindow();

    [DllImport("kernel32.dll")]
    private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handlerRoutine, bool add);

    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

    [DllImport("user32.dll")]
    private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    // 委托定义
    private delegate bool ConsoleCtrlDelegate(uint dwCtrlType);
    private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    // 常量定义
    private const int GWL_WNDPROC = -4;
    private const uint WM_CLOSE = 0x0010;
    private const uint CTRL_C_EVENT = 0;

    // 保存原窗口过程指针
    private static IntPtr _originalConsoleWndProc;
    private static WndProcDelegate _consoleWndProcDelegate;

    static void Main(string[] args)
    {
        // 系统托盘图标初始化
        NotifyIcon notifyIcon = new NotifyIcon();
        notifyIcon.Icon = SystemIcons.Shield;
        notifyIcon.Visible = true;
        notifyIcon.Text = "Background Service";

        notifyIcon.MouseClick += (s, e) =>
        {
            if (e.Button == MouseButtons.Left)
                ShowLoggingWindow();
        };

        // 右键菜单设置
        ContextMenuStrip trayIconMenuStrip = new ContextMenuStrip();
        ToolStripMenuItem menuItem1 = new ToolStripMenuItem("Show Output");
        ToolStripMenuItem menuItem2 = new ToolStripMenuItem("Exit");
        trayIconMenuStrip.Items.Add(menuItem1);
        trayIconMenuStrip.Items.Add(menuItem2);

        notifyIcon.ContextMenuStrip = trayIconMenuStrip;

        menuItem1.Click += (s, e) => ShowLoggingWindow();
        menuItem2.Click += (s, e) => Application.Exit();

        // 启动消息循环
        Application.Run();
    }

    static void ShowLoggingWindow()
    {
        AllocConsole();

        // 重定向标准输出
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding) { AutoFlush = true });

        // 注册Ctrl+C事件处理
        SetConsoleCtrlHandler(ConsoleCtrlHandler, true);

        // 获取控制台窗口句柄并替换窗口过程
        IntPtr consoleWnd = GetConsoleWindow();
        _consoleWndProcDelegate = ConsoleWndProc;
        _originalConsoleWndProc = SetWindowLongPtr(consoleWnd, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(_consoleWndProcDelegate));

        // 输出日志内容
        Console.WriteLine("=== 后台服务日志 ===");
        // ... 这里添加你的日志输出逻辑
        Console.WriteLine("Press any key to continue...");

        // 等待按键或窗口关闭
        Console.ReadKey(true);

        // 清理资源
        FreeConsole();
        SetConsoleCtrlHandler(ConsoleCtrlHandler, false);
    }

    // 控制台窗口过程处理函数
    private static IntPtr ConsoleWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        if (msg == WM_CLOSE)
        {
            // 手动释放控制台,阻止系统终止进程
            FreeConsole();
            // 恢复原窗口过程
            SetWindowLongPtr(hWnd, GWL_WNDPROC, _originalConsoleWndProc);
            return IntPtr.Zero;
        }
        // 其他消息交给原窗口过程处理
        return CallWindowProc(_originalConsoleWndProc, hWnd, msg, wParam, lParam);
    }

    // Ctrl+C事件处理函数
    private static bool ConsoleCtrlHandler(uint dwCtrlType)
    {
        if (dwCtrlType == CTRL_C_EVENT)
        {
            // 关闭控制台窗口
            FreeConsole();
            // 返回true表示已处理该事件,阻止系统终止进程
            return true;
        }
        return false;
    }
}

代码说明

  • SetConsoleCtrlHandler:注册回调函数捕获Ctrl+C信号,返回true表示已处理该事件,避免系统触发进程终止。
  • ConsoleWndProc:替换控制台窗口的默认消息处理逻辑,当收到WM_CLOSE(点击红叉)时,调用FreeConsole释放控制台资源,同时恢复原窗口过程,阻止系统终止进程。
  • 资源清理:在控制台关闭后,移除Ctrl+C事件处理,避免内存泄漏。

这样修改后,无论是点击红叉还是按下Ctrl+C,都只会关闭控制台窗口,后台服务会继续正常运行。

内容的提问来源于stack exchange,提问作者Sam Hill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 03:23:16