.net - 如何彻底关闭以Process.Start启动的控制台应用程序?

标签 .net process console-application break

这看起来像是不可能完成的任务。绝对没有发现任何有效的方法。问题是如何干净地关闭以Process.Start启动的控制台应用程序,该应用程序已在没有控制台窗口且没有使用shell execute:(ProcessStartInfo.CreateNoWindow = true; ProcessStartInfo.UseShellExecute = false;)的情况下启动。

可以认为,如果正在启动的应用程序收到ctrl-c或ctrl-break信号,则将“干净地”关闭,但似乎无法向其发送一个有效的信号(尤其是GenerateConsoleCtrlEvent)。

  • Process.Kill不起作用。由于突然,它留下了损坏的文件
    杀死进程。
  • Process.CloseMainWindow不起作用。在这种情况下,没有主窗口,因此该函数返回false并且不执行任何操作。
  • 在所有线程上为该进程调用EnumThreadWindows并将WM_CLOSE发送到每个窗口都没有任何作用,而且也没有任何线程窗口。
  • GenerateConsoleCtrlEvent不起作用。它仅对同一组中的进程(.NET无法控制)有用,并且反而会关闭调用进程。该功能不允许您指定进程ID。

  • 谁能提供接受“Process”对象的代码的人都将被标记为答案,该对象使用上面的参数启动,从而导致启动的进程干净地关闭而不会影响调用进程。使用7z.exe(7压缩存档程序)作为示例控制台应用程序,该应用程序将开始压缩大文件,并且如果未完全终止,则会留下未完成的损坏文件。

    除非有人提供功能示例或导致功能示例的代码,否则这个问题将无法得到解答。我见过数十个人在网上问这个问题和数十个答案,但没有一个能起作用。鉴于其进程ID,.NET似乎不提供完全关闭控制台应用程序的支持,考虑到它是从.NET Process对象开始的,这很奇怪。问题的一部分是无法在新的流程组中创建流程,这使得使用GenerateConsoleCtrlEvent无效。必须对此有解决方案。

    最佳答案

    我花了几个小时试图自己解决这个问题。正如您提到的,网络上充斥着根本行不通的答案。很多人建议使用GenerateConsoleCtrlEvent,但他们不提供任何上下文,只是提供了无用的代码片段。下面的解决方案使用GenerateConsoleCtrlEvent,但它可以工作。我已经测试过了

    请注意,这是一个WinForms应用程序,我正在启动和停止的过程是FFmpeg。我还没有测试过其他解决方案。我在这里使用FFmpeg录制视频并将输出保存到名为“video.mp4”的文件中。

    下面的代码是我的Form1.cs文件的内容。这是Visual Studio在创建WinForms解决方案时为您创建的文件。

    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace ConsoleProcessShutdownDemo {
        public partial class Form1 : Form {
    
        BackgroundWorker worker;
        Process currentProcess;
    
        public Form1() {
            InitializeComponent();
        }
    
        private void Worker_DoWork(object sender, DoWorkEventArgs e) {
            const string outFile = "video.mp4";
    
            var info = new ProcessStartInfo();
            info.UseShellExecute = false;
            info.CreateNoWindow = true;
            info.FileName = "ffmpeg.exe";
            info.Arguments = string.Format("-f gdigrab -framerate 60 -i desktop -crf 0 -pix_fmt yuv444p -preset ultrafast {0}", outFile);
            info.RedirectStandardInput = true;
    
            Process p = Process.Start(info);
    
            worker.ReportProgress(-1, p);
        }
    
        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
            currentProcess = (Process)e.UserState;
        }
    
        private void btnStart_Click(object sender, EventArgs e) {
            btnStart.Enabled = false;
            btnStop.Enabled = true;
    
            worker = new BackgroundWorker();
    
            worker.WorkerSupportsCancellation = true;
            worker.WorkerReportsProgress = true;
            worker.DoWork += Worker_DoWork;
            worker.ProgressChanged += Worker_ProgressChanged;
    
            worker.RunWorkerAsync();
    
        }
    
        private void btnStop_Click(object sender, EventArgs e) {
            btnStop.Enabled = false;
            btnStart.Enabled = true;
    
            if (currentProcess != null)
                StopProgram(currentProcess);
        }
    
    
    
    
    
        //MAGIC BEGINS
    
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AttachConsole(uint dwProcessId);
    
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern bool FreeConsole();
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
    
        [DllImport("Kernel32", SetLastError = true)]
        private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
    
        enum CtrlTypes {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT,
            CTRL_CLOSE_EVENT,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT
        }
    
        private delegate bool HandlerRoutine(CtrlTypes CtrlType);
    
        public void StopProgram(Process proc) {
    
            int pid = proc.Id;
    
            FreeConsole();
    
            if (AttachConsole((uint)pid)) {
    
                SetConsoleCtrlHandler(null, true);
                GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
    
                Thread.Sleep(2000);
    
                FreeConsole();
    
                SetConsoleCtrlHandler(null, false);
            }
    
            proc.WaitForExit();
    
            proc.Close();
        }
    
    
        //MAGIC ENDS
    }
    

    }

    关于.net - 如何彻底关闭以Process.Start启动的控制台应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16139571/

    相关文章:

    .net - 队列任务调度程序 : How to deal with AppDomain unload?

    c# - InitializeComponent() 有什么作用,它在 WPF 中是如何工作的?

    Windows和系统进程

    windows - 为什么 WmiPrvSE.exe 持有我进程的作业对象的句柄?

    c# - 如何在 .Net 核心控制台应用程序中使用依赖注入(inject)

    c# - 简易喷油器 :Getting an instance in an extension method

    c# - 如何在当前用户下启动进程?

    C# - 控制台击键

    c# - Log4Net 不工作,但仅适用于发布控制台构建

    c# - 一种解析带有 'Flags' 的 .NET 枚举字符串或 int 值的方法