从C#应用程序发送命令到无窗口命令行进程

5
我有一个命令行应用程序,它启动并执行一些任务。在此期间,它会监听按键(s => 显示状态)。这不是您通常按“s”和< ENTER>的命令提示符 - 它是一种类型,只要按键被按下,状态就会显示出来。
现在我正在尝试从花哨的GUI应用程序中通过发送按键来“控制”该命令行应用程序。我已经尝试了更传统的方法,即写入进程的StandardInput,但似乎没有任何效果。而且,由于实际进程没有窗口(使用CreateNoWindow=true启动),因此我无法尝试使用Win32 API向窗口发送按键。
还有其他方法吗?

4
这个命令行应用程序是你编写的吗(或者至少有源代码)?如果是,我建议将想要重复使用的部分重构成一个单独的库,这样GUI和命令行工具都可以使用。 - Jon Skeet
在使用 Process 的 StandardInput 时,您是否确保刷新了流? - jclozano
@JonSkeet:哈哈,如果我有源代码的奢侈品,我就不会问这个问题了: P我只是改变应用程序以使其更好。是的,我通过调用Flush()命令刷新了流。没有帮助。 - Iv4n
1
你能创建一个带有隐藏窗口(wShowWindow=SW_HIDE)的控制台应用程序,然后向其发送按键吗? - arx
3个回答

2
“花哨的控制台应用程序存在问题。它们倾向于直接读取键盘输入,而不是通过标准输入流进行。它们还倾向于直接控制它们的控制台,而不是通过标准输出流进行。”
据我所知,没有办法通过编程来控制这些应用程序。如果您真的非常需要的话,可以尝试使用AutoHotKey控制在私人桌面上的应用程序(AHK使用虚拟键盘/鼠标驱动程序)。不过,我不确定如何从控制台读取结果;可能可以创建一个中间控制台应用程序,由您的程序(在私人桌面上)启动并启动目标应用程序。然后,中间应用程序将共享其控制台与目标应用程序,并使用低级I/O来检测更改。
或者,您可以使用Detours来强制目标应用程序按照您的意愿运行。

谢谢你的回答。实际上,我可以轻松地从中读取信息。只是发送输入似乎有问题。我会检查这两个方面,看看是否有效。编辑:发送按键(就像在键盘上键入一样)并不像写入STDIN那么标准,这真是相当奇怪 :) - Iv4n
1
实际上,这是一场军备竞赛。一方面是那些真正想要从真正的键盘读取的应用程序,另一方面是那些真正想要欺骗这些应用程序的应用程序。WM_KEYUP/WM_KEYDOWNkeybd_eventSendInput(SendKeys)等等。我曾经被承包写一个虚拟键盘驱动程序,因为有一个程序根本不愿意被虚拟化。在这场军备竞赛中,虚拟化总是赢得胜利。:)直到TPM被使用。:( - Stephen Cleary

1

我找到了一种更好的方法来实现功能,而不会在同时进行用户输入和窗口切换时产生理论上的风险。

诀窍是使用名为PostMessage的WinAPI函数向执行相同操作的进程发送KeyDown(或KeyUp)消息。无需将进程窗口置于前台并立即隐藏它!

我正在使用键“S”作为参数发送按键命令:

        // 0x0100 - VM_KEYDOWN
        // 0x0101 - VM_KEYUP
        // 0x53 - S-key
        PostMessage(workerProcess.MainWindowHandle, 0x0100, 0x53, 0);
        PostMessage(workerProcess.MainWindowHandle, 0x0101, 0x53, 0);

PostMessage?它来自哪里? - Erçin Dedeoğlu
1
WinAPI函数 - Iv4n
它适用于隐藏的cmd应用程序吗?我尝试向可见应用程序发送消息,而且没有任何问题,但当目标应用程序被隐藏时,在我的情况下它不起作用。 - Cozdemir
@Cozdemir 2011年时它是可行的,但自那以后我就没有再碰过那段代码了,所以我真的不确定11年后同样的WinAPI是否仍然有效。 - Iv4n
谢谢您的回复。实际上还是可以工作的,但当我重定向标准输出时它就不起作用了。 - Cozdemir

1

好的,我似乎找到了我的问题的答案。它是一个真正的“拼凑在一起”的解决方案,但它有效 - 对于我构建的应用程序的所有目的而言,这并不重要。

所以,我做的是使用两个名为WinAPI函数

static extern bool ShowWindow(IntPtr WindowHandle, int nCmdShow);
static extern bool SetForegroundWindow(IntPtr WindowHandle);

第一个可以通过将nCmdShow分别更改为1和0来显示/隐藏窗口。另一个将窗口(由WindowHandle确定)置于前台。将这两个组合在一起,我能够编程地将控制台窗口置于前台,执行简单的SendKeys.Send()操作,然后再次隐藏它。
// Use a WIN API command to bring the command line to front
SetForegroundWindow(workerProcess.MainWindowHandle);
// Send a keystore to re-display the STATUS of the worker
SendKeys.Send("s");
// Hide the window again.
ShowWindow(workerProcess.MainWindowHandle, 0); 

现在,它是一个真正的折中方案,但它完成了工作。一个潜在的陷阱是,如果用户正在使用计算机进行其他操作,并且恰好在窗口处于活动状态时按下“q”的那一瞬间——它将退出工作程序。但该应用程序旨在用于专用机器上,这些机器很可能甚至不会连接显示器、键盘或鼠标,因此这不会成为问题。

感谢所有回答问题的人,因为你们以一种或另一种方式引导我找到了正确的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接