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

Posted

技术标签:

【中文标题】如何将密钥而不是字符发送到进程?【英文标题】:How to send keys instead of characters to a process? 【发布时间】:2011-01-17 11:53:35 【问题描述】:

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

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

我该怎么办?

【问题讨论】:

例如哪些击键? 这是 Unix 管道抽象的终极漏洞。重定向是通过字符流发生的,它不能模拟非打字的击键。 您是在尝试捕获然后重新发送到另一个进程,还是只是发送不可打印字符作为控制手段等? @Marc 我正在尝试为像 octave.exe 这样的交互式脚本解释器创建一个 GUI 见***.com/search?q=autohotkey 【参考方案1】:

你见过这个很棒的工具吗 - AutoIt。这是一个脚本工具。要发送退格键,您可以使用 Send("BACKSPACE")

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

这与您的问题有关吗?

【讨论】:

您好,这是相关的,但我更喜欢可以直接在 .NET 中使用的解决方案。 @Jader;有一个可用于 AutoIt 的 dll,您可以轻松地将其添加到您的应用程序中......【参考方案2】:

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

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

SendKeys.Send("BACKSPACE^C");

【讨论】:

像这样发送 Ctrl+C 实际上在进程中不起作用,应该是 ^BREAK...【参考方案3】:

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

默认情况下,当控制台窗口有 键盘焦点,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);

【讨论】:

google.com/codesearch/p?hl=en#ncfzeHH4QLA/pubs/consoledotnet/… 我正在使用 FFmpeg 屏幕录制过程。这个工作适合我吗?其实我想停止使用 FFmpeg 进行屏幕录制的过程开始【参考方案4】:

在Codeplex 上可以找到一个输入模拟器,它可以为您完成这项工作。 我正在编写一个示例代码,很快就会在这里发布,请记住输入模拟器类似于 Remus 提供的链接中的内容...

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

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

在您的情况下,只需找到窗口,通过 pinvoke 'SetForegroundWindow' 将其设置为活动状态,然后发送序列^BREAK 将 Ctrl+Break 信号发送到运行良好的进程(特别是如果process 是一个命令行程序/批处理文件)。这是CodeProject 上的一篇文章,它完全做到了这一点并反映了 SendKeys...我还没有将一些代码粘贴到这里来演示......

Edit#2:其实我很惊讶...因为这段代码将显示(概念证明)...它正在使用:

InputSimulator(如前所述) 由一个按钮组成的 windows 窗体,当窗体被加载时,它会自动运行该类。单击按钮后,它会向隐藏进程发布 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);
         
     

除了吹毛求疵,我知道netStat 正在运行“BackgroundWorker”线程,我直接从主 GUI 线程调用了“PostCtrlC”方法......这是一个迂腐的概念验证代码,但它确实表明它需要实现“ISynchronizeInvoke”以使其成为线程安全的,除此之外......它确实有效。

【讨论】:

但是如果您运行多个使用 netstat.exe 的进程,那么您将使用 PostCtrlC() 影响所有进程?当您知道进程 SessionId 时,有没有办法做同样的事情? @Constantine 该代码只针对一个运行 netstat.exe 的窗口。您可以使用EnumWindows 并创建回调函数EnumWindowsProc 来获取句柄并检查它以查看该句柄的标题是否在标题中包含netstat.exe ...如果找到,则将其置于前台并像上面的概念验证代码演示的那样“删除”它:)

以上是关于如何将密钥而不是字符发送到进程?的主要内容,如果未能解决你的问题,请参考以下文章

从服务器向AWS JS SDK发送签名,而不是使用纯文本密钥

将 WM_POWERBROADCAST 发送到其他进程

如何将字符串或信息发送到我在 Delphi 7 中使用 Createprocess 打开的进程

向非活动应用程序发送密钥

使用 jquery ajax 将 javascript 数组变量发送到 php 脚本(获取字符串作为输出而不是数组)

vue js中的axios将请求作为字符串而不是对象发送