win api readConsole()

时间:2017-05-07 19:59:39

标签: c++ windows winapi

我正在尝试使用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函数以及如何使用它们。

谢谢。

2 个答案:

答案 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+d4ctrl+z26

因此,当用户点击ctrl+dctrl+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合同所隐含的内容。