2017-05-07 34 views
4

我正在嘗試使用Win API ReadConsole(...),我想傳遞一個分隔字符來暫停控制檯的輸入。 下面的代碼有效,但它僅停止讀取\r\n上的輸入。 我希望它停止閱讀例如'.'上的控制檯輸入。win api readConsole()

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'; 
} 

我知道還有其他簡便的方法來做到這一點,但我只是想了解一些贏API函數,以及如何使用它們。

謝謝。

+0

如果你想完全控制控制檯輸入,並獲得關於任何按鍵的信息,只要按下(但不等待* VK_RETURN *),您需要使用* ReadConsoleInput *而不是* ReadConsole * – RbMm

+0

@RbMm謝謝,這很有幫助。 – dmaelect

回答

5

我看到這個問題,並認爲這將是微不足道的,但花了最後30分鐘試圖弄清楚,並最終有一些東西。

dwCtrlWakeupMaskCONSOLE_READCONSOLE_CONTROL中的記載很差。 MSDN說:「一個用戶定義的控制字符用於表示讀取完成。」,但爲什麼它被稱爲mask?爲什麼它是ULONG而不是TCHAR或類似的東西?我試着餵它的字符和wchars並沒有發生任何事情,所以一定有更多的故事。

我走上了網絡搜索特定的變量,發現此鏈接: https://groups.google.com/forum/#!topic/golang-codereviews/KSp37ITmcUg這是一個隨機圍棋庫編碼器尋求幫助和答案是選項卡1 << '\t'。我嘗試過了,它起作用了!

因此,對於未來的網絡搜索者,dwCtrlWakeupMask是ASCII控制字符的位掩碼,會導致ReadConsole返回。你可以像一樣多1 << ctrl_char s,只要你喜歡...但它不能是任意字符,因爲它是一個32位值的位掩碼,只有字符1-31(含)是可能的(這組btw被稱爲控制字符,它包括諸如tab,backspace,bell之類的東西;本身不代表可打印字符的東西)。

因此,該掩模:

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

會造成當按下選項卡(\t)OR當退格(0x08ReadConsole返回。

ctrl+ some_ascii_value表示的字符是英語字母表中該字母的數目,起始於== 1。因此,ctrl+d4是,並ctrl+z26

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

注意,Linux終端驅動程序也返回上read當用戶點擊ctrl+d所以這可能是一個不錯的兼容性事情:

因此,當用戶點擊ctrl+dctrl+z這將返回。

相信這種說法的點是允許在處理後的輸入模式更容易標籤完成;否則,您必須關閉已處理的輸入並逐個處理密鑰才能執行此操作。現在你不必......儘管tbh,我仍然更願意把我的輸入與ReadConsoleInput交互式程序,因爲你可以更好地控制它。

雖然有很多其他方法可以做你想做的事情,並且使用.作爲分隔符在這裏是不可能的,因爲它的值大於等於32,所以你需要自己做...理解什麼無論如何,這對我來說確實很有趣,而且網絡上的資源稀缺,所以我只是爲了將來的參考而寫這篇文章。

注意,這不會出現在wineconsole工作,所以可以肯定你是一個真正的Windows中對其進行測試。現在

dwControlKeyState實際上是設置由函數。傳入的值將被忽略(至少據我所知),但是當函數返回時,可以檢查給定的標誌。因此,例如,在調用ReadConsole並點擊該鍵後,如果您的numlock已打開,則該值爲32。當numlock被打開並且你按shift + tab(並且已經有numlock)時,它將是48。所以你在函數返回之後測試它。

我通常喜歡MSDN文檔,但他們IMO完全放棄了球上解釋這個參數!

1

你會發現這段代碼很荒謬。這很可能是唯一的方法來做到這一點。如果你必須適應稍後使用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的合同暗示哪些。