首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何干净利落地关闭以Process.Start启动的控制台应用?

如何干净利落地关闭以Process.Start启动的控制台应用?
EN

Stack Overflow用户
提问于 2013-04-22 12:41:50
回答 2查看 6.2K关注 0票数 23

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

如果启动的应用程序接收到ctrl-c或ctrl-break信号,那么它将“干净地”关闭,但似乎没有办法向它发送一个有效的信号(特别是GenerateConsoleCtrlEvent)。

  • Process.Kill不工作。由于突然杀死process.
  • Process.CloseMainWindow不起作用,它会留下损坏的文件。在这种情况下没有主窗口,所以函数返回false,并在进程的所有线程上执行nothing.
  • Calling EnumThreadWindows,并且向每个窗口发送WM_CLOSE没有任何作用,并且没有任何线程窗口anyway.
  • GenerateConsoleCtrlEvent不起作用。它只对同一组中的进程有用( .NET不允许您控制该组中的进程),而且还会有关闭调用进程的副作用。此函数不允许您指定进程id。

任何人都可以提供代码,接受使用上述参数启动的" process“对象,从而在不影响调用进程的情况下干净地关闭已启动的进程,这将被标记为答案。使用7z.exe (7-zip archiver)作为示例控制台应用程序,它开始压缩大文件,如果不彻底终止,将留下损坏的、未完成的文件。

除非有人提供了函数示例或导致函数示例的代码,否则这个问题是没有答案的。我已经在网上看到几十个人问这个问题和几十个答案,但都没有奏效。给定控制台应用程序的进程id,.NET似乎不支持干净利落地关闭它,考虑到它是以.NET进程对象启动的,这很奇怪。部分问题是无法在新的进程组中创建进程,这使得使用GenerateConsoleCtrlEvent变得无用。对此必须有一个解决方案。

EN

回答 2

Stack Overflow用户

发布于 2016-08-22 03:32:51

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

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

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

代码语言:javascript
复制
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
}

}

票数 5
EN

Stack Overflow用户

发布于 2015-07-23 19:35:28

这有点晚了,所以你可能不会再使用它了,但也许它会对其他人有所帮助……

你想得太多了。问题是,您只能发出来自共享同一控制台的进程的中断信号-解决方案应该是相当明显的。

创建控制台项目。该项目将以通常的方式启动目标应用程序:

代码语言:javascript
复制
var psi = new ProcessStartInfo();
psi.FileName = @"D:\Test\7z\7z.exe";
psi.WorkingDirectory = @"D:\Test\7z\";
psi.Arguments = "a output.7z input.bin";
psi.UseShellExecute = false;

var process = Process.Start(psi);

UseShellExecute是重要的部分-这确保了这两个应用程序将共享同一个控制台。

这允许您将中断发送到助手应用程序,该中断也将被传递到托管应用程序:

代码语言:javascript
复制
Console.CancelKeyPress += (s, e) => e.Cancel = true;
Thread.Sleep(1000);
GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0);

这将在宿主应用程序启动后一秒内中断它。轻松、安全。CancelKeyPress并不是必需的--我把它放在那里只是为了清楚地表明,您可以中断宿主进程,但仍然可以继续运行。在真正的助手应用程序中,这可以用于一些通知或类似的事情,但这并不是真正需要的。

现在,您只需要一种方法来通知助手应用程序发出break命令-最简单的方法是只使用简单的控制台输入,但这可能会干扰宿主应用程序。如果这不是您的选择,那么一个简单的互斥锁就可以很好地工作:

代码语言:javascript
复制
using (var mutex = Mutex.OpenExisting(args[0]))
using (var processWaitHandle = new SafeWaitHandle(process.Handle, false))
using (var processMre = new ManualResetEvent(false) { SafeWaitHandle = processWaitHandle })
{
    var which = WaitHandle.WaitAny(new WaitHandle[] { mutex, processMre });

    if (which == 0)
    {
        Console.WriteLine("Got signalled.");
        GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0);
    }
    else if (which == 1)
    {
        Console.WriteLine("Exitted normally.");
    }
}

这将等待互斥锁上的信号,或者等待宿主应用程序退出。要启动助手应用程序,您只需执行以下操作:

代码语言:javascript
复制
var mutexName = Guid.NewGuid().ToString();
mutex = new Mutex(true, mutexName);

var process = Process.Start(@"TestBreak.exe", mutexName);

要发出中断,只需释放互斥:

代码语言:javascript
复制
mutex.ReleaseMutex();

就这样。如果你需要更严格的控制,使用像命名管道这样的东西可能是一个更好的选择,但是如果你只需要break信号,互斥锁就可以了。您需要传递的任何参数都可以作为参数传递给helper应用程序,您甚至可以使用shell脚本(只需使用helper应用程序运行您需要运行和中断的任何内容)。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16139571

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档