2010-02-16 42 views
6

System.Diagnostics.Process公开了一个名为StandardInput的StreamWriter,它只接受字符,据我所知。如何将密钥而不是字符发送到进程?

但是我还需要发送击键以及一些击键不能很好地映射到字符。

我该怎么办?

+0

哪些按键,例如? – 2010-02-16 17:05:56

+0

@Rick退格,Ctrl + C – 2010-02-16 17:14:16

+1

这是Unix管道抽象中的最终漏洞。重定向通过字符流发生,它不能模拟非键入的按键。 – 2010-02-16 17:33:48

回答

23

你混合输入流的控制信号。正如你已经知道的,控制台进程有一个默认的输入流,你可以用StandardInput控制它。但是,按Ctrl-C和Ctrl-中断无法通过此流发送到进程的人物,而是他们是不是控制信号该进程接收使用已注册的信号处理程序,请参见CTRL+C and CTRL+BREAK Signals

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

要发送假信号的过程中,你可以使用GenerateConsoleCtrlEvent和发送任何CTRL_C_EVENTCTRL_BREAK_EVENT。这个API没有.Net等价物,所以你必须PInvoke它。

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=zh-CN#ncfzeHH4QLA/pubs/consoledotnet/consoledotnet.zip%7CYrqh4ujA6zA/ConsoleDotNet/WinCon.cs – 2010-02-20 23:17:36

+0

我正在使用FFmpeg屏幕录制过程。这对我有用吗?其实我想停止使用FFmpeg – Ahmad 2017-07-09 08:41:27

3

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

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

这与您的问题有关吗?

+0

嗨,这是相关的,但我更喜欢我可以直接在.NET中使用的解决方案。 – 2010-02-17 14:21:18

+1

@Jader;有一个DLL可用于AutoIt,你可以很容易地添加到您的应用程序... – jvenema 2010-02-20 23:17:01

0

如果你有一个Windows窗口可以发送密钥,那么SendKeys可能是一个合适的解决方案。

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

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

发送Ctrl + C这样做实际上并没有在一个过程中,它应该是^ {BREAK} ... – t0mm13b 2010-02-21 16:12:31

6

有一个输入模拟器发现这里Codeplex它可以为你做只是工作:

从.NET你只需要包含函数定义中使用它。 我工作的一个示例代码,将回到这里后不久,牢记输入模拟器是类似于在由瑞摩斯提供的链接找到...

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

  • 不能隐藏窗口的流没有重定向(这是它会失败,因为窗口的句柄是无处可看,没有将其带到方式使其活跃的前景!)
  • 一个窗口显示过程,这是有效的!

在你的情况下,它发现窗口的问题,将其通过的PInvoke活跃“SetForegroundWindow”,并发送序列^{BREAK}其发送CTRL + BREAK信号的过程,并很好地工作(特别是如果进程是命令行程序/批处理文件)。这里有CodeProject,这是否准确,反映了...的SendKeys我还没有粘贴一些代码,这证明了一篇文章....

编辑#2:其实我很惊讶......作为这个代码将显示(概念的证明)...它使用:

  • InputSimulator(如前所述)
  • 甲窗口形式,它由一个按钮,当窗体加载它自动运行类。点击该按钮后,它会向隐藏进程发送ctrl-break
  • 输出流确实是重定向的,并且是一个隐藏窗口。
  • 奇怪的是,输出被捕获,但没有在调试窗口中实时显示结果,也就是说,它被缓冲(我猜),直到进程终止,整个输出显示。
  • 我在FindWindow API调用中有点欺骗,因为我知道窗口的标题是,并且以某种方式能够将它带到前台,并使用InputSimulator将键击发送给它......或使用传统普通旧SendKeys函数...我有Thread.Sleep的原因是为了确保按键被发送,以便'被推入到'主动前景窗口'的键盘队列中,尽管如此,它被隐藏'
  • I使用'netstat -e 5'命令来循环forev呃,每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”的方法......这是迂腐的但它确实表明它需要实现'ISynchronizeInvoke'以使其线程安全,除此之外......它确实有效。

+0

屏幕录制的过程开始,但如果你有多个进程使用netstat。exe,那么你会影响所有与PostCtrlC()?当你知道进程SessionId时,有没有办法做同样的事情? – Constantin 2012-05-24 19:35:28

+0

@Constantine代码只能运行一个运行netstat.exe的窗口。您可以使用[EnumWindows](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497(v = vs.85).aspx)并创建回调函数[EnumWindowsProc](http: //msdn.microsoft.com/en-us/library/windows/desktop/ms633498(v=vs.85).aspx)来获取句柄并检查它是否在该句柄的标题中有netstat.exe标题......如果找到它,然后将它带入前台,并将其作为概念验证代码“zap”进行演示:) – t0mm13b 2012-05-28 22:59:59

相关问题