代码之家  ›  专栏  ›  技术社区  ›  Jader Dias

如何向进程发送键而不是字符?

  •  6
  • Jader Dias  · 技术社区  · 14 年前

    system.diagnostics.process公开一个名为standardinput的streamwriter,据我所知,它只接受字符。

    但我也需要发送击键,有些击键不能很好地映射到字符。

    我该怎么办?

    4 回复  |  直到 13 年前
        1
  •  24
  •   Jader Dias    14 年前

    您正在将输入流与控制信号混合。控制台进程有一个默认的输入流,您可以用标准输入来控制它,正如您已经知道的那样。但ctrl-c和ctrl-break不是通过此流发送到进程的字符,而是 控制信号 进程使用注册的信号处理程序接收的,请参见 CTRL+C and CTRL+BREAK Signals :

    默认情况下,当控制台窗口 键盘焦点,ctrl+c或 ctrl+break被视为一个信号 (sigint或sigbreak)而不是 键盘输入。

    将假信号发送到您可以使用的进程 GenerateConsoleCtrlEvent 并发送任何一个 CTRL_C_EVENT CTRL_BREAK_EVENT . 这个API没有.NET等价物,所以您必须使用PInvoke。

    要从.NET中使用它,只需包含函数定义:

    const int CTRL_C_EVENT = 0;
    const int CTRL_BREAK_EVENT = 1;
    
    [DllImport("kernel32.dll")]
    static extern bool GenerateConsoleCtrlEvent(
        uint dwCtrlEvent,
        uint dwProcessGroupId);
    
        2
  •  6
  •   Tim Cooper    13 年前

    这里有一个输入模拟器 Codeplex 这可能只是你的工作。 我正在编写一个示例代码,稍后将在这里发布,请记住,输入模拟器与Remus提供的链接中发现的类似。

    编辑: 我发现这有一个局限性,你绝对可以摆脱典型的 System.Windows.Forms.SendKeys.Send 方法,它确实有效! 但是,这个过程必须

    • 流没有重定向
    • 不能是隐藏窗口(这是它将失败的地方,因为窗口的句柄不可见,无法将其置于前台使其处于活动状态!)
    • 一个窗口,显示此过程的有效性!

    在您的情况下,需要找到窗口,通过pinvoke“setForegroundWindow”将其设置为活动状态,然后发送序列 ^{BREAK} 它会将ctrl+break信号发送到运行良好的进程(特别是如果进程是命令行程序/批处理文件)。这是一篇关于 CodeProject 这样做完全是为了反映发送键……我还没有将一些代码粘贴到其中来演示……

    编辑2: 实际上,我很惊讶……正如这段代码所显示的(概念验证)…它使用的是:

    • 输入模拟器(如前所述)
    • 一个由按钮组成的Windows窗体,当加载该窗体时,它会自动运行该类。单击该按钮后,它会向隐藏的进程发送一个ctrl-break
    • 输出流确实被重定向,并且是一个隐藏窗口。
    • 奇怪的是,输出正在被捕获,但不会在调试窗口中显示结果,也就是说,它会被缓冲(我猜)直到进程终止,整个输出都会显示出来…
    • 我有点作弊 FindWindow API调用,因为我知道窗口的标题是,并且不知何故,能够将其置于前台,并使用输入模拟器将击键发送给它……或者使用传统的普通老版本 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”线程,我直接从主GUI线程调用了“postctrlc”方法……这是一种迂腐的概念验证代码,但它确实表明它需要实现“isynchronizeinvoke”以使其线程安全,而且……它确实有效。

        3
  •  3
  •   Subbu    14 年前

    你见过这个很棒的工具吗- AutoIt . 这是一个脚本工具。用退格键发送 Send("{BACKSPACE}")

    这是一个很好的工具,它可以帮助自动执行许多手动单击/双击等操作。

    这与你的问题有关吗?

        4
  •  0
  •   AndiDog    14 年前

    如果有Windows窗体窗口可以将密钥发送到,则 SendKeys 可能是一个合适的解决方案。

    按backspace和ctrl+c,应该是

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