【问题标题】:win api readConsole()win api readConsole()
【发布时间】:2017-05-07 19:59:38
【问题描述】:

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

谢谢。

【问题讨论】:

  • 如果您想要完整的控制台输入并获得有关按下任何键的信息,就在按下时(但不等待 VK_RETURN),您需要使用 ReadConsoleInput i> 而不是 ReadConsole
  • @RbMm 谢谢,这很有帮助。

标签: c++ windows winapi


【解决方案1】:

我看到这个问题并认为它是微不足道的,但花了最后 30 分钟试图弄清楚它并最终得到了一些东西。

dwCtrlWakeupMaskCONSOLE_READCONSOLE_CONTROL 中的记录非常差。 MSDN 说“一个用户定义的控制字符,用于表示读取完成。”,但为什么叫mask?为什么它是ULONG 而不是TCHAR 或类似的东西?我试着喂它 chars 和 wchars 并没有发生任何事情,所以这个故事肯定还有更多。

我在网上搜索该特定变量并找到此链接: https://groups.google.com/forum/#!topic/golang-codereviews/KSp37ITmcUg 这是一个随机的 Go 库编码器寻求帮助,答案是标签是 1 << '\t'。我试过了,效果很好!

因此,对于未来的网络搜索者,dwCtrlWakeupMask 是 ASCII 控制字符的位掩码,将导致 ReadConsole 返回。您可以将| 放在一起任意数量的1 << ctrl_chars...但它不能是任意字符,因为它是 32 位值中的位掩码,只有字符 1-31(含)是可能的(此组顺便说一句,被称为控制字符,它包括制表符、退格键、铃铛之类的东西;本身并不代表可打印字符的东西。

因此,这个面具:

cReadControl.dwCtrlWakeupMask = (1 << '\t') | (1 << 0x08);

当按下制表符 (\t) 或退格 (0x08) 时将导致 ReadConsole 返回。

ctrl+ some_ascii_value 表示的字符是该字母在英文字母表中的编号,从 a == 1 开始。所以,ctrl+d4ctrl+z26

因此,当用户点击ctrl+dctrl+z时,这将返回:

cReadControl.dwCtrlWakeupMask = (1 << 4) | (1 << 26);

请注意,当用户点击 ctrl+d 时,Linux 终端驱动程序也会返回 read,因此这可能是一个很好的兼容性。

我相信这个论点的重点是允许在处理输入模式下更容易地完成制表符;否则,您必须关闭已处理的输入并一一处理键才能做到这一点。现在您不必...。尽管如此,我仍然更喜欢将我的输入与ReadConsoleInput 一起用于交互式程序,因为您可以更好地控制这一切。

但是,虽然有很多其他方法可以做你想做的事 - 在这里使用 . 作为分隔符是不可能的,因为它的值 >= 32,所以你需要自己做......理解无论如何,这对我来说很有趣,而且网络上的资源稀缺,所以我写这个只是为了将来参考。

请注意,这似乎不适用于 wineconsole,因此请确保您在真正的 Windows 机器上进行测试。

现在,dwControlKeyState 实际上是由函数设置的。您传入的值被忽略(至少据我所知),但您可以在函数返回时检查给定标志。因此,例如,在调用ReadConsole 并按下键后,如果您的numlock 已打开,它将是32。它将是 48 表示打开了 numlock 并且您按下了 shift+tab(并且打开了 numlock)。所以你在函数返回后测试它。

我通常喜欢 MSDN 文档,但 IMO 他们在解释这个参数时完全放弃了!

【讨论】:

  • @ilw 确实,空间是代码 32,所以有点太大,无法放入其中。
  • 我又开始使用这个函数了,想写下更多关于它如何工作的细节。当您传递 CONSOLE_READCONSOLE_CONTROL 结构时,nInitialChars 成员将一些输入缓冲区保留在控制台的编辑缓冲区中。当它返回时,输入将用字符替换缓冲区中光标位置的字符,并将编辑行的其余部分保留在那里。您可以扫描它并编辑缓冲区,然后再次调用并设置该nInitialChars 成员以保留以供下一步使用。虽然如果您在一行的中间完成 Tab,则无法将光标保持在中间。
  • 如果请求 buf 大小太大,例如 65000 字节,任何人都可以帮助我解决为什么 ReadConsole、WriteConsole、ReadFile(console_in)、WriteFile(console_out) 会失败?我希望它能够尽可能多地回馈它可以读取或写入的内容。 posix 替代品 _write 和 _read 工作完美。
  • @JaveneCPPMcGowan 文档没有说,但我看到人们报告某些功能有 64 kb 的限制。由于它是宽字符,这意味着最多支持 32k 个元素。但是您绝对可以使用比可用缓冲区更大的缓冲区 - 这就是您通常的做法,就像在 posix 上一样。只需尝试一个较小的缓冲区,看看会发生什么。
  • @Adam D. Ruppe 谢谢。没有解决我的问题,但无论如何谢谢?我的问题是需要一致性。 MSDN 应该记录此类错误。而其他人不抱怨的事实意味着某些事情是不正常的。显然,posix 函数的设计没有这些限制。可能以posix模式打开终端会解决我的问题。
【解决方案2】:

你会发现这段代码很荒谬。这很可能是做到这一点的唯一方法。如果您以后必须适应使用 ReadFile,这是不消耗更多输入的唯一方法。

大多数情况下,您根本不想要 ReadConsole,而是希望在标准输入句柄上使用 ReadFile,但我离题了。

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 合同所隐含的谓词。

【讨论】:

  • ReadConsole 比 ReadFile 有一个重要的优势:它可以读取 unicode 字符。使用 ReadConsoleW 和 WriteConsoleW 可以获得宽字符,从而避免与其他代码页有关的所有麻烦。
  • @AdamD.Ruppe:根据我的经验,当出现这个问题时,标准输入通常连接到管道。在这种情况下可能不是。
  • 但是请注意,ReadFile 是一个二进制读取器,它的计数是字节,而不是 ReadConsole 的字符。
猜你喜欢
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多