2010-02-16 21 views
6

System.Diagnostics.Process expone un StreamWriter llamado StandardInput, que solo acepta caracteres por lo que sé.¿Cómo enviar claves en lugar de caracteres a un proceso?

Pero también necesito enviar las pulsaciones de teclas, y algunas teclas no se asignan bien a los caracteres.

¿Qué debo hacer?

+0

¿Qué teclas presionadas, por ejemplo? –

+0

@ Retroceso de retroceso, Ctrl + C –

+1

Esa es la última fuga en la abstracción de tuberías Unix. La redirección ocurre a través de las secuencias de caracteres, no puede simular pulsaciones de tecla que no sean de tipeo. –

Respuesta

23

Usted está mezclando flujos de entrada con las señales de control. Un proceso de consola tiene un flujo de entrada predeterminado que puede controlar con StandardInput, como ya sabe. Pero Ctrl-C y Ctrl-Break no son caracteres enviados al proceso a través de esta corriente, pero en cambio son en cambio señales de control que el proceso recibe usando los manejadores de señales registradas, ver CTRL+C and CTRL+BREAK Signals:

Por defecto, cuando una ventana de consola tiene , el foco del teclado, CTRL + C o CTRL + BREAK se trata como una señal (SIGINT o SIGBREAK) y no como entrada de teclado .

para enviar señales falsas a un proceso que puede utilizar GenerateConsoleCtrlEvent y enviar ya sea CTRL_C_EVENT o CTRL_BREAK_EVENT. Esta API no tiene equivalente en .Net, por lo que debe invocarla.

utilizarlo desde .NET sólo hay que incluir la definición de función:

const int CTRL_C_EVENT = 0; 
const int CTRL_BREAK_EVENT = 1; 

[DllImport("kernel32.dll")] 
static extern bool GenerateConsoleCtrlEvent(
    uint dwCtrlEvent, 
    uint dwProcessGroupId); 
+0

http: // www. google.com/codesearch/p?hl=en#ncfzeHH4QLA/pubs/consoledotnet/consoledotnet.zip%7CYrqh4ujA6zA/ConsoleDotNet/WinCon.cs –

+0

estoy usando el proceso FFmpeg de grabación de pantalla. ¿Es esto trabajo para mí? En realidad, quiero detener el inicio del proceso de grabación de pantalla con FFmpeg – Ahmad

3

Has visto esta gran herramienta - AutoIt. Esta es una herramienta de scripting. Para enviar un retroceso, debería usar Send("{BACKSPACE}")

Esta es una gran herramienta y puede ayudar a automatizar muchos clics manuales/doble clic/etc.

¿Es esto relevante para tu pregunta?

+0

Hola, es relevante, pero prefiero una solución que pueda usar directamente en .NET. –

+1

@Jader; hay un dll disponible para AutoIt que puede agregar fácilmente a su aplicación ... – jvenema

0

Si tiene una ventana de Windows Forms a la que puede enviar las claves, entonces SendKeys podría ser una solución adecuada.

Para presionar la tecla de retroceso y Ctrl + C, que debe ser

SendKeys.Send("{BACKSPACE}^C"); 
+0

Enviar Ctrl + C al estilo que realmente no funciona en un proceso, debería ser^{BREAK} ... – t0mm13b

6

Hay una entrada Simulador de encontrar aquí en Codeplex que puede hacer sólo el trabajo para usted. estoy trabajando en un código de ejemplo y se publicará de nuevo aquí en breve, tener en cuenta el simulador de entrada es similar a lo que se encontró en el enlace proporcionado por Remus ...

Editar: he encontrado que hay una limitación con esto, definitivamente puede salirse con la suya con el método típico System.Windows.Forms.SendKeys.Send, ¡funciona de manera efectiva! , pero, el proceso debe tener

  • No hay cambios de dirección de las corrientes
  • No se puede ventana oculta (aquí es donde se producirá un error, ya que el mango de la ventana es por ningún lado, no hay manera de llevarlo a el primer plano para hacerlo activo!)
  • ¡Una ventana que muestra el proceso para que esto sea efectivo!

En su caso, es una cuestión de encontrar la ventana, la pusieron activa a través de PInvoke 'SetForegroundWindow', y enviar las secuencias ^{BREAK} que envía la señal Ctrl + Pausa para el proceso que funciona muy bien (especialmente si el proceso es un programa de línea de comando/archivo por lotes). He aquí un artículo sobre CodeProject que hace esto exactamente y refleja la SendKeys ... todavía tengo que pegar un código en este para demostrar ....

Edición # 2: En realidad estoy bastante sorprendido ... como este código se mostrará (prueba de concepto) ... que está utilizando:

  • InputSimulator (como se mencionó anteriormente)
  • una forma de ventanas que consta de un botón, cuando el formulario se carga automáticamente ejecuta la clase . Al hacer clic en el botón, se publica un ctrl-break en el proceso oculto
  • La secuencia de salida se redirige y es una ventana oculta.
  • Lo extraño es que la salida se está capturando pero no muestra los resultados en la ventana de depuración, en tiempo real, es decir, se almacena en el búfer (supongo) hasta que finaliza el proceso, se muestra toda la salida. .
  • Hice una trampa en la llamada a la API FindWindow, porque sabía que el título de la ventana era y de alguna manera era capaz de ponerlo en primer plano, y usar el InputSimulator para enviarle las teclas ... o usar el tradicional simple viejo SendKeys función ... la razón por la que tuve el Thread.Sleep es asegurar que las teclas se envían para ser 'empujadas a la cola del teclado de la' ventana activa de primer plano ', que a pesar de eso, está oculta'
  • I usó el comando 'netstat -e 5' para repetir forev por ejemplo, actualizar los resultados cada 5 segundos hasta que reciba un 'Ctrl + C' para romper el ciclo infinito.
public partial class Form1 : Form 
{ 
    private TestNetStat netStat = new TestNetStat(); 
    public Form1() 
    { 
     InitializeComponent(); 
     using (BackgroundWorker bgWorker = new BackgroundWorker()) 
     { 
      bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork); 
      bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted); 
      bgWorker.RunWorkerAsync(); 
     } 
    } 

    void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     System.Diagnostics.Debug.WriteLine("BGWORKER ENDED!"); 
    } 

    private void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     netStat.Run(); 
    } 
    void btnPost_Click(object sender, EventArgs e) 
    { 
     netStat.PostCtrlC(); 
     System.Diagnostics.Debug.WriteLine(string.Format("[{0}] - {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), this.netStat.OutputData.Replace(Environment.NewLine, ""))); 
    } 
} 

public class TestNetStat 
{ 
    private StringBuilder sbRedirectedOutput = new StringBuilder(); 
    // 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 
    [DllImport("user32")] 
    public static extern int SetForegroundWindow(IntPtr hwnd); 
    public string OutputData 
    { 
     get { return this.sbRedirectedOutput.ToString(); } 
    } 
    public void PostCtrlC() 
    { 
     IntPtr ptr = FindWindow(null, @"C:\Windows\System32\netstat.exe"); 
     if (ptr != null) 
     { 
      SetForegroundWindow(ptr); 
      Thread.Sleep(1000); 
      WindowsInput.InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.CANCEL); 
      // SendKeys.Send("^{BREAK}"); 
      Thread.Sleep(1000); 
     } 
    } 
    public void Run() 
    { 
     System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo(); 
     ps.FileName = "netstat"; 
     ps.ErrorDialog = false; 
     ps.Arguments = "-e 5"; 
     ps.CreateNoWindow = true; 
     ps.UseShellExecute = false; 
     ps.RedirectStandardOutput = true; 
     ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 
     using (System.Diagnostics.Process proc = new System.Diagnostics.Process()) 
     { 
      proc.StartInfo = ps; 
      proc.EnableRaisingEvents = true; 
      proc.Exited += new EventHandler(proc_Exited); 
      proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived); 
      proc.Start(); 
      proc.BeginOutputReadLine(); 
      proc.WaitForExit(); 
     } 
    } 

    void proc_Exited(object sender, EventArgs e) 
    { 
     System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended"); 
    } 

    void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) 
    { 
     if (e.Data != null) 
     { 
      this.sbRedirectedOutput.Append(e.Data + Environment.NewLine); 
      System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data); 
     } 
    } 
} 

nitpicky un lado, sé que el netStat se está ejecutando el hilo 'BackgroundWorker', y yo invocado directamente el método 'PostCtrlC' desde el hilo principal interfaz gráfica de usuario ... esto es como un pedante código de prueba de concepto, pero muestra que necesita implementar 'ISynchronizeInvoke' para que sea seguro para subprocesos, aparte ... de hecho funciona.

+0

, pero si tiene varios procesos que usan netstat.exe, entonces afectará todo con PostCtrlC()? ¿Hay alguna manera de hacer lo mismo cuando conoces el proceso SessionId? – Constantin

+0

@Constantine El código solo se dirige a una ventana que ejecuta netstat.exe. Puede usar [EnumWindows] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497 (v = vs.85) .aspx) y crear la función de devolución de llamada [EnumWindowsProc] (http: //msdn.microsoft.com/en-us/library/windows/desktop/ms633498(v=vs.85).aspx) para obtener el identificador e inspeccionarlo para ver si el título de ese identificador tiene netstat.exe en el title ... si se encuentra, entonces póngalo en primer plano y "zap" como lo demuestra el código de prueba de concepto anterior :) – t0mm13b

Cuestiones relacionadas