Как отправить ключи вместо символов в процесс? - PullRequest
7 голосов
/ 16 февраля 2010

System.Diagnostics.Process предоставляет StreamWriter с именем StandardInput, который, насколько я знаю, принимает только символы.

Но мне также нужно отправлять нажатия клавиш, и некоторые нажатия клавиш плохо сопоставляются с символами.

Что мне делать?

Ответы [ 4 ]

25 голосов
/ 21 февраля 2010

Вы смешиваете входные потоки с управляющими сигналами. Консольный процесс имеет входной поток по умолчанию, которым вы можете управлять с помощью StandardInput, как вы уже знаете. Но Ctrl-C и Ctrl-Break не являются символами, посылаемыми процессу через этот поток, но вместо этого они вместо этого управляющие сигналы , которые процесс получает, используя зарегистрированные обработчики сигналов, см. CTRL + C и Сигналы CTRL + BREAK :

По умолчанию, когда окно консоли имеет фокус клавиатуры, CTRL + C или CTRL + BREAK рассматривается как сигнал (SIGINT или SIGBREAK), а не как ввод с клавиатуры.

Чтобы отправить ложные сигналы процессу, вы можете использовать GenerateConsoleCtrlEvent и отправить либо CTRL_C_EVENT, либо CTRL_BREAK_EVENT. Этот API не имеет эквивалента .Net, поэтому вы должны выполнить его.

Чтобы использовать его из .NET, вам просто нужно включить определение функции:

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

[DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(
    uint dwCtrlEvent,
    uint dwProcessGroupId);
6 голосов
/ 21 февраля 2010

На Codeplex найден симулятор ввода, который может выполнить только вашу работу. Я работаю над примером кода и вскоре опубликую здесь, имейте в виду, что Симулятор ввода похож на то, что было найдено по ссылке, предоставленной Remus ...

Редактировать: Я обнаружил, что с этим есть ограничение, вы определенно можете обойтись без типичного System.Windows.Forms.SendKeys.Send метода, он работает эффективно! , но процесс должен иметь

  • Нет перенаправлений потоков
  • Нельзя скрыть окно (это то место, где оно выйдет из строя, поскольку дескриптор окна нигде не виден, нет способа вывести его на передний план, чтобы сделать его активным!)
  • Окно, показывающее процесс, чтобы это было эффективным!

В вашем случае нужно найти окно, установить его активным с помощью pinvoke 'SetForegroundWindow' и отправить последовательности ^{BREAK}, которые посылают сигнал Ctrl + Break процессу, который работает очень хорошо (особенно если процесс - это командная строка программы / командного файла). Вот статья о CodeProject , которая делает это точно и отражает SendKeys ... Я еще не вставил в это код, чтобы продемонстрировать ....

Редактирование # 2: На самом деле я весьма удивлен ... как покажет этот код (подтверждение концепции) ... он использует:

  • InputSimulator (как упоминалось ранее)
  • Форма окна, состоящая из кнопки, при загрузке формы она автоматически запускает класс. После нажатия на кнопку он отправляет Ctrl-Break в скрытый процесс
  • Выходной поток действительно перенаправлен и является скрытым окном.
  • Странная вещь, это то, что вывод захватывается, но не показывает результаты в окне отладки, то есть в режиме реального времени он буферизуется (я полагаю), пока процесс не завершится, весь вывод будет показан .. .
  • Я немного обманул вызов API FindWindow, потому что я знал, что заголовок окна был и каким-то образом мог вывести его на передний план и использовать InputSimulator для отправки на него нажатий клавиш ... или использовать традиционная простая старая функция SendKeys ... причина, по которой у меня была Thread.Sleep, заключается в том, чтобы гарантировать, что нажатия клавиш отправляются для того, чтобы их "толкали в очередь клавиатуры" активного окна переднего плана ", которая, несмотря на это, скрыта «
  • Я использовал команду 'netstat -e 5' для бесконечного цикла, обновляя результаты каждые 5 секунд, пока не получит Ctrl + C, чтобы разорвать бесконечный цикл.
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);
         }
     }
}

Кстати, я знаю, что netStat выполняется из потока 'BackgroundWorker', и я напрямую вызвал метод 'PostCtrlC' из основного потока GUI ... это педантично как код для проверки концепции , но он показывает, что ему нужно реализовать ISynchronizeInvoke, чтобы сделать его поточно-ориентированным, за исключением ... он действительно работает.

3 голосов
/ 17 февраля 2010

Вы видели этот замечательный инструмент - AutoIt . Это инструмент для сценариев. Для отправки возврата вы должны использовать Send("{BACKSPACE}")

Это отличный инструмент, и он может помочь в автоматизации многих ручных щелчков / двойных щелчков / и т.д.

Это относится к вашему вопросу?

0 голосов
/ 20 февраля 2010

Если у вас есть окно Windows Forms, в которое вы можете отправить ключи, то SendKeys может быть подходящим решением.

Для нажатия клавиши Backspace и Ctrl + C это должно быть

SendKeys.Send("{BACKSPACE}^C");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...