将TAB发送到子控制台(窗口)

时间:2021-08-25 17:27:31

I create a child console application with

我创建了一个子控制台应用程序

_process = new Process();
_process.StartInfo.FileName = @"cmd.exe";
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.RedirectStandardInput = true;
_process.StartInfo.RedirectStandardOutput = true;
_process.StartInfo.CreateNoWindow = true;

_proccess.Start();

Now I can go to c:\aaa

现在我可以去c:\ aaa

_process.StandardInput.Write("cd c:\\aaa\xD\xA");

But normally user can type c:\ + TAB + ENTER. How can I do the same? This does not work:

但通常用户可以输入c:\ + TAB + ENTER。我怎么能这样做?这不起作用:

_process.StandardInput.Write("cd c:\\\0x9\xD\xA");

2 个解决方案

#1


There's a fundamental difference between the standard input of the child process, and the console that it is attached to. A command interpreter in interactive mode is expecting to be talking either to a console or to a pipe/file as its standard input, and it changes its behaviour to match which one it finds. For a pipe/file, it just makes a simple call the ReadFile() function. For a console, however, it receives input through ReadConsoleInput(), where non-alphanumeric keys are recognizable by their virtual key codes.

子进程的标准输入与它所附加的控制台之间存在根本区别。交互模式下的命令解释器期望与控制台或管道/文件作为其标准输入进行通信,并且它会改变其行为以匹配它找到的行为。对于管道/文件,它只是简单地调用ReadFile()函数。但是,对于控制台,它通过ReadConsoleInput()接收输入,其中非字母数字键可通过其虚拟键代码识别。

See the ReadCommand() function in the ReactOS CMD for an example.

有关示例,请参阅ReactOS CMD中的ReadCommand()函数。

If you write a byte with the value 9 down a pipe to the standard input of a process, that's what it will see from its call to ReadFile(). But it won't treat it as a Tab keypress, because (for starters) it isn't one; it's just byte value 9 coming down a pipe. Moreover, the command-line editing that a command interpreter does in interactive mode only occurs when the standard input is detected to be a console, not when it is a pipe or a file. The command interpreter uses ReadConsoleInput(), which doesn't read buffers of bytes but reads sequences of INPUT_RECORDs; and thus you have to WriteConsoleInput() to that console handle an appropriate INPUT_RECORD simulating the Tab key if you want the command interpreter to see a Tab keypress in its input stream.

如果将一个值为9的字节写入流程的标准输入管道,那么它将从其对ReadFile()的调用中看到它。但它不会将它视为Tab键,因为(对于初学者而言)它不是一个;它只是字节值9来自管道。此外,命令解释器在交互模式下执行的命令行编辑仅在检测到标准输入是控制台时发生,而不是在管道或文件时发生。命令解释器使用ReadConsoleInput(),它不读取字节缓冲区但读取INPUT_RECORDs的序列;因此,如果希望命令解释器在其输入流中看到Tab键,则必须向该控制台处理WriteConsoleInput()以处理模拟Tab键的相应INPUT_RECORD。

Of course, since you've set StartInfo.RedirectStandardInput to true, under the covers .NET has connected the standard input of the command interpreter process to a pipe; and so the command interpreter is just calling ReadFile(), without doing any interactive command-line editing, as it would if its standard input had been a console. Even if you could find and open a handle to the command interpreter process' console, and write a INPUT_RECORD for a Tab key to it, the command interpreter is reading a pipe for its standard input, and ignoring its console.

当然,既然你已经将StartInfo.RedirectStandardInput设置为true,那么.NET已经将命令解释器进程的标准输入连接到管道;所以命令解释器只调用ReadFile(),而不进行任何交互式命令行编辑,就像它的标准输入是控制台一样。即使您可以找到并打开命令解释程序进程'控制台的句柄,并为其键入一个Tab键的INPUT_RECORD,命令解释程序正在为其标准输入读取管道,并忽略其控制台。

If you want a command interpreter to act just as it would if it were talking to a console, command line editing and all, you have to spawn the process with its standard input as a console, whose handle you have to (then) obtain so that you can simulate keypresses with WriteConsoleInput(). That's fairly difficult within .NET.

如果你想要一个命令解释器就像它与控制台,命令行编辑和所有人一样行动,你必须用它的标准输入作为控制台产生进程,你必须得到它的句柄(然后)您可以使用WriteConsoleInput()模拟按键。这在.NET中相当困难。

#2


What about dipping into the Windows API?

那么深入了解Windows API呢?

using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_CHAR = 0x0102;
private const int VK_TAB = 0x9;
private const int VK_RETURN = 0xD;
//...
SendMessage(_process.Handle, WM_CHAR, new IntPtr(VK_TAB), new IntPtr(0));
SendMessage(_process.Handle, WM_CHAR, new IntPtr(VK_RETURN), new IntPtr(0));

However that doesn't allways work according to Kevin Montrose's answer here.

然而,根据Kevin Montrose的回答,这并不总是有效。

#1


There's a fundamental difference between the standard input of the child process, and the console that it is attached to. A command interpreter in interactive mode is expecting to be talking either to a console or to a pipe/file as its standard input, and it changes its behaviour to match which one it finds. For a pipe/file, it just makes a simple call the ReadFile() function. For a console, however, it receives input through ReadConsoleInput(), where non-alphanumeric keys are recognizable by their virtual key codes.

子进程的标准输入与它所附加的控制台之间存在根本区别。交互模式下的命令解释器期望与控制台或管道/文件作为其标准输入进行通信,并且它会改变其行为以匹配它找到的行为。对于管道/文件,它只是简单地调用ReadFile()函数。但是,对于控制台,它通过ReadConsoleInput()接收输入,其中非字母数字键可通过其虚拟键代码识别。

See the ReadCommand() function in the ReactOS CMD for an example.

有关示例,请参阅ReactOS CMD中的ReadCommand()函数。

If you write a byte with the value 9 down a pipe to the standard input of a process, that's what it will see from its call to ReadFile(). But it won't treat it as a Tab keypress, because (for starters) it isn't one; it's just byte value 9 coming down a pipe. Moreover, the command-line editing that a command interpreter does in interactive mode only occurs when the standard input is detected to be a console, not when it is a pipe or a file. The command interpreter uses ReadConsoleInput(), which doesn't read buffers of bytes but reads sequences of INPUT_RECORDs; and thus you have to WriteConsoleInput() to that console handle an appropriate INPUT_RECORD simulating the Tab key if you want the command interpreter to see a Tab keypress in its input stream.

如果将一个值为9的字节写入流程的标准输入管道,那么它将从其对ReadFile()的调用中看到它。但它不会将它视为Tab键,因为(对于初学者而言)它不是一个;它只是字节值9来自管道。此外,命令解释器在交互模式下执行的命令行编辑仅在检测到标准输入是控制台时发生,而不是在管道或文件时发生。命令解释器使用ReadConsoleInput(),它不读取字节缓冲区但读取INPUT_RECORDs的序列;因此,如果希望命令解释器在其输入流中看到Tab键,则必须向该控制台处理WriteConsoleInput()以处理模拟Tab键的相应INPUT_RECORD。

Of course, since you've set StartInfo.RedirectStandardInput to true, under the covers .NET has connected the standard input of the command interpreter process to a pipe; and so the command interpreter is just calling ReadFile(), without doing any interactive command-line editing, as it would if its standard input had been a console. Even if you could find and open a handle to the command interpreter process' console, and write a INPUT_RECORD for a Tab key to it, the command interpreter is reading a pipe for its standard input, and ignoring its console.

当然,既然你已经将StartInfo.RedirectStandardInput设置为true,那么.NET已经将命令解释器进程的标准输入连接到管道;所以命令解释器只调用ReadFile(),而不进行任何交互式命令行编辑,就像它的标准输入是控制台一样。即使您可以找到并打开命令解释程序进程'控制台的句柄,并为其键入一个Tab键的INPUT_RECORD,命令解释程序正在为其标准输入读取管道,并忽略其控制台。

If you want a command interpreter to act just as it would if it were talking to a console, command line editing and all, you have to spawn the process with its standard input as a console, whose handle you have to (then) obtain so that you can simulate keypresses with WriteConsoleInput(). That's fairly difficult within .NET.

如果你想要一个命令解释器就像它与控制台,命令行编辑和所有人一样行动,你必须用它的标准输入作为控制台产生进程,你必须得到它的句柄(然后)您可以使用WriteConsoleInput()模拟按键。这在.NET中相当困难。

#2


What about dipping into the Windows API?

那么深入了解Windows API呢?

using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_CHAR = 0x0102;
private const int VK_TAB = 0x9;
private const int VK_RETURN = 0xD;
//...
SendMessage(_process.Handle, WM_CHAR, new IntPtr(VK_TAB), new IntPtr(0));
SendMessage(_process.Handle, WM_CHAR, new IntPtr(VK_RETURN), new IntPtr(0));

However that doesn't allways work according to Kevin Montrose's answer here.

然而,根据Kevin Montrose的回答,这并不总是有效。