Застрял на GenerateConsoleCtrlEvent в C # с консольными приложениями - PullRequest
3 голосов
/ 18 ноября 2008

Я испытываю самые тяжелые времена, пытаясь заставить это работать, надеясь, что один из вас делал это раньше.

У меня есть консольное приложение C #, которое выполняет дочерний процесс, который наследует его консоль. Я хочу, чтобы ctrl-c, перехваченный внешним приложением, передавался во внутреннее приложение, чтобы у него была возможность красиво завершить работу.

У меня есть очень простой код. Я запускаю процесс, затем опрашиваю его с помощью WaitForExit (10). У меня также зарегистрирован обработчик CancelKeyPress, который устанавливает значение bool в true при его запуске. Цикл опроса также проверяет это, и когда это так, он вызывает GenerateConsoleCtrlEvent () (который я сопоставил через pinvoke).

Я пробовал много комбинаций параметров для GenerateConsoleCtrlEvent (). 0 или 1 для первого параметра и либо 0, либо идентификатор дочернего процесса для второго параметра. Ничто не похоже на работу. Иногда я получаю ложное возвращение и Marshal.GetLastWin32Error () возвращает 0, а иногда я получаю истинное возвращение. Но ни одна из них не заставляет дочернее приложение получить Ctrl-C.

Чтобы быть абсолютно уверенным, я написал тестовое приложение на C # в качестве дочернего приложения, которое распечатывает, что с ним происходит, и проверяет, что ручной ввод ctrl-c при запуске правильно завершает его.

Я бился головой об это пару часов. Кто-нибудь может дать мне несколько советов о том, куда идти с этим?

Ответы [ 3 ]

3 голосов
/ 18 ноября 2008

Не уверен, что это хороший подход. Это работает, только если дочерний процесс создан с флагом CREATE_NEW_PROCESS_GROUP для CreateProcess(). Однако класс System.Diagnostics.Process не поддерживает это.

Рассмотрите возможность использования возвращаемого значения из метода Main (). Уже существует уникальное значение, определенное в Windows SDK для отмены Ctrl + C, STATUS_CONTROL_C_EXIT или 0xC000013A. Родительский процесс может получить этот код возврата из свойства Process.ExitCode.

2 голосов
/ 26 ноября 2009

Вам повезло с этим? Насколько я понимаю, когда вы нажимаете 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, вам, похоже, нужно создать новые окна консоли для каждого из них. Я понятия не имею, есть ли какой-нибудь способ сделать это без видимых окон или когда вы хотите перенаправить ввод / вывод с использованием каналов.

0 голосов
/ 13 мая 2018

Вот мое решение для отправки ctrl-c процессу. К вашему сведению, я никогда не заставлял GenerateConsoleCtrlEvent работать.

Вместо того, чтобы использовать GenerateConsoleCtrlEvent, я нашел способ отправить CTRL-C процессу. К вашему сведению, в этом случае мне не нужно было искать идентификатор группового процесса.

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<string> ErrorTextReceived;
    public event EventHandler ProcessExited;
    public event EventHandler<string> 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<string> 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<string> 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, как получить группу процессов уже запущенного процесса?

...