这看起来像是不可能完成的任务。绝对没有发现任何有效的方法。问题是如何干净地关闭以Process.Start启动的控制台应用程序,该应用程序已在没有控制台窗口且没有使用shell execute:(ProcessStartInfo.CreateNoWindow = true; ProcessStartInfo.UseShellExecute = false;
)的情况下启动。
可以认为,如果正在启动的应用程序收到ctrl-c或ctrl-break信号,则将“干净地”关闭,但似乎无法向其发送一个有效的信号(尤其是GenerateConsoleCtrlEvent)。
杀死进程。
谁能提供接受“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/