我正在尝试使用Win API ReadConsole(...)
,我想传递一个分隔符字符来暂停来自控制台的输入。
以下代码有效但只停止读取\r\n
上的输入。
我希望它停止阅读'.'
上的控制台输入。
void read(char *cIn, char delim)
{
HANDLE hFile;
DWORD charsRead;
DWORD charsToRead = MAX_PATH;
CONSOLE_READCONSOLE_CONTROL cReadControl;
cReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
cReadControl.nInitialChars = 0;
cReadControl.dwCtrlWakeupMask = delim;
cReadControl.dwControlKeyState = NULL;
DWORD lpMode;
// char cIn[MAX_PATH]; //-- buffer to hold data from the console
hFile = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
GetConsoleMode(hFile,&lpMode);
// lpMode &= ~ENABLE_LINE_INPUT; //-- turns off this flag
// SetConsoleMode(hFile, lpMode); //-- set the mode with the new flag off
bool read = ReadConsole(hFile, cIn, charsToRead * sizeof(TCHAR), &charsRead, &cReadControl);
cIn[charsRead - 2] = '\0';
}
我知道有其他简单的方法可以做到这一点,但我只是想了解一些win api函数以及如何使用它们。
谢谢。
答案 0 :(得分:9)
我看到了这个问题,并认为这是微不足道的,但是花了最后30分钟试图解决它并最终得到了一些东西。
在dwCtrlWakeupMask
中,CONSOLE_READCONSOLE_CONTROL
的记录很差。 MSDN说“用户定义的控制字符,用于表示读取已完成。”,但为什么称为mask
?为什么它是ULONG
而不是TCHAR
或类似的东西?我尝试过喂它们的角色和wchars而没有任何事情发生,所以故事必须有更多。
我上网搜索该特定变量,发现此链接:
https://groups.google.com/forum/#!topic/golang-codereviews/KSp37ITmcUg这是一个随机Go库编码器寻求帮助,答案是标签是1 << '\t'
。我试过了,它有效!
因此,对于未来的网络搜索者,dwCtrlWakeupMask
是ASCII控制字符的位掩码,会导致ReadConsole
返回。您可以|
尽可能多地1 << ctrl_char
s ...但它不能是任意字符,因为它是32位值的位掩码,只有字符1-31(包括)是可能(这个组btw被称为控制字符,它包括tab,backspace,bell等内容;本身不代表可打印字符的东西。)
因此,这个面具:
cReadControl.dwCtrlWakeupMask = (1 << '\t') | (1 << 0x08);
当标签(ReadConsole
)或按下退格(\t
)时,会导致0x08
返回。
ctrl+ some_ascii_value
代表的字符是英文字母中该字母的编号,从== 1开始。因此,ctrl+d
为4
,ctrl+z
是26
。
因此,当用户点击ctrl+d
或ctrl+z
:
cReadControl.dwCtrlWakeupMask = (1 << 4) | (1 << 26);
请注意,当用户点击read
时,Linux终端驱动程序也会在ctrl+d
上返回,因此这可能是一个很好的兼容性事项。
我认为这个论点的重点是允许在处理输入模式下更容易完成制表符;否则,您必须关闭已处理的输入并逐个处理密钥才能执行此操作。现在你没有......虽然tbh,我仍然更喜欢用ReadConsoleInput
来输入交互式程序,因为你可以更好地控制它。
虽然还有很多其他方法可以做你想做的事情 - 并且在这里使用.
作为分隔符是不可能的,因为它的值大于等于= 32,所以你需要自己做。无论如何,了解这对我来说有什么意义,网上有稀缺的资源,所以我写这篇文章只是为了供将来参考。
请注意,这似乎不适用于wineconsole
,因此请确保您使用真正的Windows机箱进行测试。
现在,dwControlKeyState
实际上是由函数设置的。您传入的值将被忽略(至少据我所知),但您可以在函数返回时检查给定的标志。因此,例如,在调用ReadConsole
并按下该键后,如果您的numlock已启用,则为32。这将是48是numlock打开,你按Shift + tab(并打开numlock)。所以你在函数返回后测试它。
我通常喜欢MSDN文档,但IMO在解释这个参数时完全放弃了它!
答案 1 :(得分:1)
你会发现这段代码很荒谬。这很可能是唯一的方法。如果你必须适应以后使用ReadFile,那么它是唯一不会消耗更多输入的方法。
大部分时间你都不想在标准输入句柄上想要ReadFile的ReadConsole,但我离题了。
char *cInptr = cIn;
do {
bool read = ReadConsole(hFile, cInptr, sizeof(TCHAR), &charsRead, &cReadControl);
if (read) cInptr += charsRead;
} while (read && charsRead > 0 && cInptr[-1] && cInptr[-1] != '.');
由于偏执,我可能在循环中进行了太多测试。我不倾向于查找所有谓词以确定ReadConsole合同所隐含的内容。