System.Diagnostics.Process公开了一个名为StandardInput的StreamWriter,据我所知,它只接受字符。
但我需要发送按键,而有些按键不能很好地映射到字符。
我该怎么办?
System.Diagnostics.Process公开了一个名为StandardInput的StreamWriter,据我所知,它只接受字符。
但我需要发送按键,而有些按键不能很好地映射到字符。
我该怎么办?
您混淆了输入流和控制信号。 控制台进程有一个默认的输入流,您可以使用StandardInput控制,正如您已经知道的那样。 但是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);
System.Windows.Forms.SendKeys.Send
方法,它确实有效!但是,该进程必须满足以下条件:^{BREAK}
,这将向进程发送Ctrl+Break信号,这很有效(特别是如果进程是命令行程序/批处理文件)。这里是CodeProject上的一篇文章,它确切地完成了这个,而且与SendKeys非常相似...我还没有把代码粘贴到这里来演示......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”使其线程安全,但是...它确实可以工作。
Send("{BACKSPACE}")
。如果您有一个可以发送按键的 Windows Forms 窗口,那么 SendKeys 可能是一个适当的解决方案。
对于按下回退和 Ctrl+C,应该是
SendKeys.Send("{BACKSPACE}^C");