2012-08-09 43 views
5

我已經用下面的代碼禁用線路輸入:爲什麼在循環中調用ReadConsole會破壞堆棧?

DWORD dwConsoleMode; 
GetConsoleMode(hStdIn, &dwConsoleMode); 
dwConsoleMode ^= ENABLE_LINE_INPUT; 
SetConsoleMode(hStdIn, dwConsoleMode); 

然後我打電話ReadConsole在一個循環......在一個循環:

wchar_t cBuf; 

while (1) { 
    /* Display Options */ 

    do { 
     ReadConsole(hStdIn, &cBuf, 1, &dwNumRead, NULL); 
    } while (!iswdigit(cBuf)); 

    putwchar(cBuf); 

    if (cBuf == L'0') break; 
} 

如果我運行該程序,並按下0馬上,它存在乾淨。
但如果我按下一串鑰匙,然後按0,當程序存在,它與崩潰:

運行時檢查失敗#2 - 圍繞堆棧變量「CBUF」已損壞。

爲什麼會造成堆棧損壞?代碼很簡單,所以我無法弄清楚什麼是錯的。

的小程序,我可以重現該問題:

#include <windows.h> 
#include <stdio.h> 

int wmain(int argc, wchar_t *argv[]) 
{ 
    DWORD dwNumRead; 
    wchar_t cBuf; 

    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); 

    DWORD dwConsoleMode; 
    GetConsoleMode(hStdIn, &dwConsoleMode); 
    dwConsoleMode ^= ENABLE_LINE_INPUT; 
    SetConsoleMode(hStdIn, dwConsoleMode); 

    while (true) 
    { 
     wprintf(L"\nEnter option: "); 

     do { 
      ReadConsoleW(hStdIn, &cBuf, 1, &dwNumRead, NULL); 
     } while (!iswdigit(cBuf)); 

     putwchar(cBuf); 

     if (cBuf == L'0') break; 
    } 

    return 0; 
} 

你要那種搗爛鍵盤運行後,再按0,這與堆棧腐敗崩潰。

我也不能每次都重現這個問題,需要一些嘗試。
在創建一個新的空控制檯項目並添加一個帶有該代碼的文件後,我在Visual Studio 2010下運行它。

+0

我無法重現該問題。您能否發佈一個小而完整的程序來展示這個問題? – 2012-08-09 03:06:27

+0

我會建議檢查'ReadConsoleW'的返回值,並在需要時檢查'GetLastError'。否則,我沒有線索! – 2012-08-09 04:14:08

+0

檢查了'ReadConsoleW'的返回值,但是每次返回值都不爲零,因此沒有錯誤。此外,當它與堆棧損壞崩潰時,調試器是在程序的末尾。 – Josh 2012-08-09 04:26:29

回答

7

據我所知,這是Windows中的一個錯誤。這裏有一個稍微簡單的程序演示該問題:

#include <windows.h> 
#include <crtdbg.h> 

int wmain(int argc, wchar_t *argv[]) 
{ 
    DWORD dwNumRead; 
    wchar_t cBuf[2]; 

    cBuf[0] = cBuf[1] = 65535; 

    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); 
    SetConsoleMode(hStdIn, 0); 

    while (true) 
    { 
     _ASSERT(ReadConsoleW(hStdIn, &cBuf[0], 1, &dwNumRead, NULL)); 
     _ASSERT(dwNumRead == 1); 
     _ASSERT(cBuf[1] == 65535); 
     Sleep(5000); 
    } 
} 

睡眠使它有點容易引發問題,當一個以上的字符是在你撥打ReadConsoleW的時間等待發生。

在相關斷言失敗時查看cBuf[1]的內容,看起來ReadConsoleW在緩衝區的末尾寫入了一個額外的字節。

解決方法很簡單:確保您的緩衝區至少有一個額外的字節。在你的情況下,使用雙字符數組的第一個字符。

+1

有趣。只是好奇,如果我刪除睡眠功能,我不能讓它觸發一個斷點?它有5秒鐘的睡眠時間來完成什麼? – Josh 2012-08-09 12:16:24

+1

如果沒有睡眠,儘管使用剪切粘貼功能仍然可以正常工作,但您不可能足夠快速地按鍵以便一次讀取多個讀數。 (由於putwchar和wprintf的原因,你的原始代碼有點慢,因此睡眠不是必須的。) – 2012-08-09 20:31:07