How to send keys instead of characters to a process?

后端 未结 4 1987
名媛妹妹
名媛妹妹 2020-12-03 06:31

System.Diagnostics.Process exposes a StreamWriter named StandardInput, which accepts only characters as far as I know.

But I need to send keystrokes as well, and som

4条回答
  •  北荒
    北荒 (楼主)
    2020-12-03 06:47

    There's an input Simulator found here on Codeplex which may do just the job for you. I am working on a sample code and will post back here shortly, bear in mind the Input Simulator is similar to what was found in the link supplied by Remus...

    Edit: I have found that there is a limitation with this, you can definitely get away with the typical System.Windows.Forms.SendKeys.Send method, it does work effectively! , but, the process must have

    • No redirections of the streams
    • Cannot be hidden window (this is where it will fail, since the window's handle is nowhere to be seen, no way of bringing it to the foreground to make it active!)
    • A window showing the process for this to be effective!

    In your case, it's a matter of finding the window, set it active via pinvoke 'SetForegroundWindow', and send the sequences ^{BREAK} which sends the Ctrl+Break signal to the process which does work very well (especially if the process is a command line program/batch file). Here's an article on CodeProject that does this exactly and mirrors the SendKeys...I have yet to paste some code into this to demonstrate ....

    Edit#2: Actually I am quite surprised...as this code will show (proof of concept)...it is using:

    • InputSimulator (as mentioned previously)
    • A windows form that consists of a button, when the form is loaded it automatically runs the class. Upon clicking the button, it posts a ctrl-break to the hidden process
    • The output stream is indeed redirected and is a hidden window.
    • The weird thing, is the output is being captured but does not show the results in the debug window, in real-time that is, it is buffered (I guess) until the process terminates, the whole output is shown...
    • I cheated a bit on the FindWindow API call, because I knew the window's title was and was somehow, able to bring it to the foreground, and using the InputSimulator to send the keystrokes to it...or use the traditional plain old SendKeys function...the reason I had the Thread.Sleep is to ensure that the keystrokes are sent in order to be 'pushed into the keyboard queue of the "active foreground window", which despite that, is hidden'
    • I used the 'netstat -e 5' command to loop forever, refreshing the results every 5 seconds until it receives a 'Ctrl+C' to break the infinite loop.
    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 aside, I know that the netStat is running off the 'BackgroundWorker' thread, and I directly invoked the 'PostCtrlC' method from the main GUI thread...this is pedantic as a proof-of-concept code, but it does show that it needs to implement 'ISynchronizeInvoke' to make it thread-safe, that aside...it does indeed work.

提交回复
热议问题