transcurrido tiempo procesos net medir entre ejecucion comunicacion calcular c# command-line .net-2.0 process

tiempo - stopwatch c#



¿Cómo envío ctrl+c a un proceso en c#? (6)

@alonl: el usuario está intentando envolver un programa de línea de comandos. Los programas de línea de comandos no tienen bombas de mensajes a menos que se creen específicamente, e incluso si ese fuera el caso, Ctrl + C no tiene la misma semántica en una aplicación de entorno Windows (copia, de forma predeterminada) como lo hace en un entorno de línea de comandos (Break).

Tiré esto juntos. CtrlCClient.exe simplemente llama a Console.ReadLine () y espera:

static void Main(string[] args) { ProcessStartInfo psi = new ProcessStartInfo("CtrlCClient.exe"); psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; psi.UseShellExecute = false; Process proc = Process.Start(psi); Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited); proc.StandardInput.WriteLine("/x3"); Console.WriteLine(proc.StandardOutput.ReadToEnd()); Console.WriteLine("{0} is active: {1}", proc.Id, !proc.HasExited); Console.ReadLine(); }

Mi resultado parece hacer lo que quieras:

4080 is active: True 4080 is active: False

¡Espero que ayude!

(Para aclarar: / x3 es la secuencia de escape de hex para el carácter hexadecimal 3, que es ctrl + c. No es solo un número mágico.;))

Estoy escribiendo una clase contenedora para un ejecutable de línea de comando. Este exe acepta la entrada de stdin hasta que pulse ctrl + c en el intérprete de comando shell, en cuyo caso imprime salida en función de la entrada a stdout. Quiero simular que ctrl + c presione en c # code, enviando el comando kill a un objeto de proceso .Net. Intenté llamar a Process.kill (), pero eso no parece darme nada en el StreamReader StandardOutput del proceso. ¿Podría haber algo que no esté haciendo bien? Aquí está el código que intento usar:

ProcessStartInfo info = new ProcessStartInfo(exe, args); info.RedirectStandardError = true; info.RedirectStandardInput = true; info.RedirectStandardOutput = true; info.UseShellExecute = false; Process p = Process.Start(info); p.StandardInput.AutoFlush = true; p.StandardInput.WriteLine(scriptcode); p.Kill(); string error = p.StandardError.ReadToEnd(); if (!String.IsNullOrEmpty(error)) { throw new Exception(error); } string output = p.StandardOutput.ReadToEnd();

sin embargo, la salida siempre está vacía, aunque obtengo datos de stdout cuando ejecuto el exe manualmente. editar: esto es c # 2.0 por cierto


A pesar del hecho de que usar GenerateConsoleCtrlEvent para enviar la señal Ctrl + C es una respuesta correcta, necesita una aclaración significativa para que funcione en diferentes tipos de aplicaciones .NET.

Si su aplicación .NET no usa su propia consola (WinForms / WPF / Windows Service / ASP.NET), el flujo básico es:

  1. Adjunte el proceso .NET principal a la consola del proceso que desea Ctrl + C
  2. Evite que se detenga el proceso .NET principal debido al evento Ctrl + C con SetConsoleCtrlHandler
  3. Generar evento de consola para la consola actual con GenerateConsoleCtrlEvent (processGroupId debe ser cero! Responder con el código que envía p.SessionId no funcionará e incorrecto)
  4. Desconectar de la consola y restaurar el control de Ctrl + C por proceso principal

El siguiente fragmento de código ilustra cómo hacerlo:

Process p; if (AttachConsole((uint)p.Id)) { SetConsoleCtrlHandler(null, true); try { if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT,0)) return false; p.WaitForExit(); } finally { FreeConsole(); SetConsoleCtrlHandler(null, false); } return true; }

donde SetConsoleCtrlHandler, FreeConsole, AttachConsole y GenerateConsoleCtrlEvent son métodos nativos de WinAPI:

internal const int CTRL_C_EVENT = 0; [DllImport("kernel32.dll")] internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId); [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool AttachConsole(uint dwProcessId); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] internal static extern bool FreeConsole(); [DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); // Delegate type to be used as the Handler Routine for SCCH delegate Boolean ConsoleCtrlDelegate(uint CtrlType);

Las cosas se vuelven más complejas si necesita enviar Ctrl + C desde la aplicación de consola .NET. El enfoque no funcionará porque AttachConsole devuelve falso en este caso (la aplicación de la consola principal ya tiene una consola). Es posible llamar a FreeConsole antes de la llamada a AttachConsole, pero como resultado se perderá la consola de la aplicación .NET original, lo cual no es aceptable en la mayoría de los casos.

Mi solución para este caso (que realmente funciona y no tiene efectos secundarios para la consola de proceso principal de .NET):

  1. Cree un pequeño programa de consola .NET compatible que acepte el ID de proceso desde los argumentos de línea de comando, pierda su propia consola con FreeConsole antes de la llamada a AttachConsole y envíe Ctrl + C al proceso de destino con el código mencionado anteriormente
  2. El proceso principal de la consola .NET invoca esta utilidad en un nuevo proceso cuando necesita enviar Ctrl + C a otro proceso de la consola

Aquí está mi solución para enviar ctrl-c a un proceso.

En lugar de usar GenerateConsoleCtrlEvent, he aquí cómo he enviado CTRL-C a un proceso. FYI, en este caso, nunca necesité encontrar el ID del proceso grupal.

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; } } } } }

Luego, al ejecutar realmente el proceso y enviar el CTRL-C en mi aplicación principal:

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"); }

Para obtener más información, consulte Redirigir la entrada estándar de la aplicación de consola y Windows cómo obtener el grupo de procesos de un proceso que ya se está ejecutando.


De hecho, me acabo de enterar de la respuesta. Gracias a ambos por sus respuestas, pero resulta que todo lo que tuve que hacer fue esto:

p.StandardInput.Close()

lo que provoca que el programa que he generado termine de leer desde stdin y genere lo que necesito.


Intente enviar la Combinación de teclas Ctrl + C, en lugar de finalizar directamente el proceso:

[DllImport("user32.dll")] public static extern int SendMessage( int hWnd, // handle to destination window uint Msg, // message long wParam, // first message parameter long lParam // second message parameter );

Búsquelo en MSDN, encontrará lo que necesita allí para enviar la combinación de teclas Ctrl + ... Sé que el mensaje que necesita para enviar Alt + Key es WM_SYSTEMKEYDOWN y WM_SYSTEMKEYUP, no puedo informarle sobre Ctrl ...


Ok, aquí hay una solución.

La forma de enviar la señal Ctrl-C es con GenerateConsoleCtrlEvent. SIN EMBARGO, esta llamada toma un parámetro processGroupdID y envía la señal Ctrl-C a todos los procesos en el grupo. Esto estaría bien si no fuera por el hecho de que no hay forma de engendrar el proceso hijo en .NET que está en un grupo de procesos diferente del que usted (el padre) tiene. Por lo tanto, cuando envía GenerateConsoleCtrlEvent, tanto el niño Y USTED (EL PADRE) OBTENGA. Entonces, necesita capturar el evento ctrl-c en el padre también, y luego determinar si es necesario ignorarlo.

En mi caso, quiero que el padre también pueda manejar los eventos de Ctrl-C, así que necesito distinguir entre los eventos de Ctrl-C enviados por el usuario en la consola y los enviados por el proceso padre al hijo. Hago esto simplemente ajustando / desarmando un indicador booleano mientras envío el comando ctrl-c al niño, y luego comprobando este indicador en el controlador de eventos ctrl-c del padre (es decir, si envía ctrl-c al hijo, luego lo ignora. )

Entonces, el código se vería así:

//import in the declaration for GenerateConsoleCtrlEvent [DllImport("kernel32.dll", SetLastError=true)] static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId); public enum ConsoleCtrlEvent { CTRL_C = 0, CTRL_BREAK = 1, CTRL_CLOSE = 2, CTRL_LOGOFF = 5, CTRL_SHUTDOWN = 6 } //set up the parents CtrlC event handler, so we can ignore the event while sending to the child public static volatile bool SENDING_CTRL_C_TO_CHILD = false; static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { e.Cancel = SENDING_CTRL_C_TO_CHILD; } //the main method.. static int Main(string[] args) { //hook up the event handler in the parent Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress); //spawn some child process System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); psi.Arguments = "childProcess.exe"; Process p = new Process(); p.StartInfo = psi; p.Start(); //sned the ctrl-c to the process group (the parent will get it too!) SENDING_CTRL_C_TO_CHILD = true; GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, p.SessionId); p.WaitForExit(); SENDING_CTRL_C_TO_CHILD = false; //note that the ctrl-c event will get called on the parent on background thread //so you need to be sure the parent has handled and checked SENDING_CTRL_C_TO_CHILD already before setting it to false. 1000 ways to do this, obviously. //get out.... return 0; }