使用控制台应用程序在C#中使用GenerateConsoleCtrlEvent

我正在努力让这个工作变得更加困难,希望你们其中一个人之前做过这件事。

我有一个C#控制台应用程序正在运行一个inheritance其控制台的子进程。 我希望外部应用程序捕获的ctrl-c被传递到内部应用程序,以便它有机会很好地关闭。

我有一些非常简单的代码。 我启动一个进程,然后使用WaitForExit(10)进行轮询。 我还注册了一个CancelKeyPress处理程序,它在触发时将bool设置为true。 轮询循环也检查这个,当它为真时,它调用GenerateConsoleCtrlEvent()(我通过pinvoke映射)。

我已经尝试了很多params组合到GenerateConsoleCtrlEvent()。 第一个参数为0或1,第二个参数为0或子进程的ID。 似乎没什么用。 有时我得到一个假回来,Marshal.GetLastWin32Error()返回0,有时我得到回来。 但没有一个导致子应用程序接收ctrl-c。

为了绝对肯定,我编写了一个测试C#应用程序作为子应用程序,打印出它正在发生的事情,并validation在运行时手动输入ctrl-c正确导致它退出。

几个小时以来,我一直在反对这个问题。 谁能给我一些关于去哪里的指示?

不太确定这是一个好方法。 仅当使用CreateProcess()的CREATE_NEW_PROCESS_GROUP标志创建子进程时,此方法才有效。 但是,System.Diagnostics.Process类不支持此function。

考虑使用Main()方法的返回值。 Windows SDK中已为Ctrl + C中止定义了唯一值,STATUS_CONTROL_C_EXIT或0xC000013A。 父进程可以从Process.ExitCode属性获取该返回代码。

你有运气吗? 我的理解是,当您在控制台中按CTRL + C时,默认情况下,连接到控制台的所有进程都会接收它,而不仅仅是父进程。 这是一个例子:

Child.cs:

 using System; public class MyClass { public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args) { Console.WriteLine("Child killed by CTRL+C."); } public static void Main() { Console.WriteLine("Child start."); Console.CancelKeyPress += CtrlCHandler; System.Threading.Thread.Sleep(4000); Console.WriteLine("Child finish."); } } 

Parent.cs:

 using System; public class MyClass { public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args) { Console.WriteLine("Parent killed by CTRL+C."); } public static void Main() { Console.CancelKeyPress += CtrlCHandler; Console.WriteLine("Parent start."); System.Diagnostics.Process child = new System.Diagnostics.Process(); child.StartInfo.UseShellExecute = false; child.StartInfo.FileName = "child.exe"; child.Start(); child.WaitForExit(); Console.WriteLine("Parent finish."); } } 

输出:

 Y:\>parent Parent start. Child start. Parent killed by CTRL+C. Child killed by CTRL+C. ^C Y:\>parent Parent start. Child start. Child finish. Parent finish. 

所以我不认为你需要做任何特别的事情。 但是,如果你真的需要自己生成CTRL + C事件,事情可能就不那么容易了。 我不确定您描述的问题,但据我所知,您只能将CTRL + C事件发送到连接到控制台窗口的所有进程。 如果分离进程,则无法向其发送CTRL + C事件。 如果您想选择在哪些进程中发送CTRL + C事件,您似乎需要为每个进程创建新的控制台窗口。 我不知道是否有一些方法可以在没有可见窗口的情况下或者当您想使用管道重定向I / O时。

这是我将ctrl-c发送到进程的解决方案。 仅供参考,我从未让GenerateConsoleCtrlEvent工作。

而不是使用GenerateConsoleCtrlEvent,我发现如何将CTRL-C发送到进程。 仅供参考,在这种情况下,我不需要找到组进程ID。

 using System; using System.Diagnostics; using System.Text; using System.Threading; using System.Threading.Tasks; public class ConsoleAppManager { private readonly string appName; private readonly Process process = new Process(); private readonly object theLock = new object(); private SynchronizationContext context; private string pendingWriteData; public ConsoleAppManager(string appName) { this.appName = appName; this.process.StartInfo.FileName = this.appName; this.process.StartInfo.RedirectStandardError = true; this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8; this.process.StartInfo.RedirectStandardInput = true; this.process.StartInfo.RedirectStandardOutput = true; this.process.EnableRaisingEvents = true; this.process.StartInfo.CreateNoWindow = true; this.process.StartInfo.UseShellExecute = false; this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8; this.process.Exited += this.ProcessOnExited; } public event EventHandler ErrorTextReceived; public event EventHandler ProcessExited; public event EventHandler StandartTextReceived; public int ExitCode { get { return this.process.ExitCode; } } public bool Running { get; private set; } public void ExecuteAsync(params string[] args) { if (this.Running) { throw new InvalidOperationException( "Process is still Running. Please wait for the process to complete."); } string arguments = string.Join(" ", args); this.process.StartInfo.Arguments = arguments; this.context = SynchronizationContext.Current; this.process.Start(); this.Running = true; new Task(this.ReadOutputAsync).Start(); new Task(this.WriteInputTask).Start(); new Task(this.ReadOutputErrorAsync).Start(); } public void Write(string data) { if (data == null) { return; } lock (this.theLock) { this.pendingWriteData = data; } } public void WriteLine(string data) { this.Write(data + Environment.NewLine); } protected virtual void OnErrorTextReceived(string e) { EventHandler handler = this.ErrorTextReceived; if (handler != null) { if (this.context != null) { this.context.Post(delegate { handler(this, e); }, null); } else { handler(this, e); } } } protected virtual void OnProcessExited() { EventHandler handler = this.ProcessExited; if (handler != null) { handler(this, EventArgs.Empty); } } protected virtual void OnStandartTextReceived(string e) { EventHandler handler = this.StandartTextReceived; if (handler != null) { if (this.context != null) { this.context.Post(delegate { handler(this, e); }, null); } else { handler(this, e); } } } private void ProcessOnExited(object sender, EventArgs eventArgs) { this.OnProcessExited(); } private async void ReadOutputAsync() { var standart = new StringBuilder(); var buff = new char[1024]; int length; while (this.process.HasExited == false) { standart.Clear(); length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length); standart.Append(buff.SubArray(0, length)); this.OnStandartTextReceived(standart.ToString()); Thread.Sleep(1); } this.Running = false; } private async void ReadOutputErrorAsync() { var sb = new StringBuilder(); do { sb.Clear(); var buff = new char[1024]; int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length); sb.Append(buff.SubArray(0, length)); this.OnErrorTextReceived(sb.ToString()); Thread.Sleep(1); } while (this.process.HasExited == false); } private async void WriteInputTask() { while (this.process.HasExited == false) { Thread.Sleep(1); if (this.pendingWriteData != null) { await this.process.StandardInput.WriteLineAsync(this.pendingWriteData); await this.process.StandardInput.FlushAsync(); lock (this.theLock) { this.pendingWriteData = null; } } } } } 

然后,在实际运行该过程并在我的主应用程序中发送CTRL-C:

  DateTime maxStartDateTime = //... some date time; DateTime maxEndDateTime = //... some later date time var duration = maxEndDateTime.Subtract(maxStartDateTime); ConsoleAppManager appManager = new ConsoleAppManager("myapp.exe"); string[] args = new string[] { "args here" }; appManager.ExecuteAsync(args); await Task.Delay(Convert.ToInt32(duration.TotalSeconds * 1000) + 20000); if (appManager.Running) { // If stilll running, send CTRL-C appManager.Write("\x3"); } 

有关详细信息,请参阅重定向控制台应用程序的标准输入和Windows如何获取已在运行的进程的进程组?