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

System.Diagnostics.Process公开名为StandardInput的StreamWriter,据我所知,它只接受字符。

但我也需要发送击键,并且一些击键不能很好地映射到角色。

我该怎么办?

您正在将输入流与控制信号混合。 正如您所知,控制台进程有一个默认输入流,您可以使用StandardInput控制它。 但Ctrl-C和Ctrl-Break不是通过此流发送到进程的字符,而是它们是使用注册的信号处理程序接收进程接收的控制信号 ,请参阅CTRL + C和CTRL + BREAK信号 :

默认情况下,当控制台窗口具有键盘焦点时,CTRL + C或CTRL + BREAK将被视为信号(SIGINT或SIGBREAK),而不是键盘输入。

要向进程发送虚假信号,您可以使用GenerateConsoleCtrlEvent并发送CTRL_C_EVENTCTRL_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); 

在Codeplex上有一个输入模拟器,它可以为你完成这项工作。 我正在研究一个示例代码,并将很快发布回来,请记住输入模拟器类似于Remus提供的链接…

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

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

在你的情况下,这是一个找到窗口的问题,通过pinvoke’SetForegroundWindow’将其设置为活动,并发送序列^{BREAK} ,它将Ctrl + Break信号发送到工作得很好的进程(特别是如果进程是命令行程序/批处理文件)。 这是一篇关于CodeProject的文章,它完全正确地执行了这个并反映了SendKeys …我还没有将一些代码粘贴到这里来演示….

编辑#2:实际上我很惊讶……因为这段代码会显示(概念certificate)……它正在使用:

  • InputSimulator(如前所述)
  • 由窗体组成的窗体,在加载窗体时会自动运行该类。 单击该按钮后,它会向隐藏进程发送一个ctrl-break
  • 输出流确实被重定向并且是隐藏窗口。
  • 奇怪的是,输出被捕获但是没有在调试窗口中显示结果,实时也就是说,它被缓冲(我猜)直到进程终止,整个输出显示…
  • 我在FindWindow API调用上有点欺骗,因为我知道窗口的标题是,并且以某种方式,能够将它带到前台,并使用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); } } } 

除了Nitpicky,我知道netStat正在运行’BackgroundWorker’线程,我直接从主GUI线程调用’PostCtrlC’方法…这是一个迂腐的概念validation代码,但它确实显示它需要实现’ISynchronizeInvoke’以使其成为线程安全的,除此之外……它确实有效。

你见过这个伟大的工具 – AutoIt 。 这是一个脚本工具。 要发送退格键,您可以使用Send("{BACKSPACE}")

这是一个很棒的工具,它可以帮助自动化许多手动点击/双击/等。

这与你的问题有关吗?

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

对于按退格键和Ctrl + C,应该是

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