bool IsConsoleDoubleCellCP() { static bool bDBCS = false, bDBCS_Checked = false; if (!bDBCS_Checked) { bDBCS = (GetSystemMetrics(SM_DBCSENABLED) != 0); bDBCS_Checked = true; } static bool bDBCS_CP = false; static DWORD nLastCP = 0; UINT MaxCharSize = 0; DWORD nCP; #ifdef _DEBUG DWORD nCP1; //DWORD nMode; #endif if (bDBCS) { nCP = GetConsoleOutputCP(); #ifdef _DEBUG nCP1 = GetConsoleCP(); _ASSERTE(nCP1==nCP); //GetConsoleMode(hOut, &nMode); #endif if (!nLastCP || (nLastCP != nCP)) { bool bNewVal = true; if (!AreCpInfoLeads(nCP, &MaxCharSize) || MaxCharSize < 2) { // gh-879: Windows 10 (since 14931) has changed behavior for double-cell glyphs // Now they are doubled (COMMON_LVB_LEADING_BYTE/COMMON_LVB_TRAILING_BYTE) even for UTF-8 codepage if (!IsWin10()) bNewVal = false; } bDBCS_CP = bNewVal; nLastCP = nCP; } } return bDBCS_CP; }
// Консоль любит глючить, при попытках запроса более определенного количества ячеек. // MAX_CONREAD_SIZE подобрано экспериментально BOOL ReadConsoleOutputEx(HANDLE hOut, CHAR_INFO *pData, COORD bufSize, SMALL_RECT rgn) { BOOL lbRc = FALSE; DWORD nTick1 = GetTickCount(), nTick2 = 0, nTick3 = 0, nTick4 = 0, nTick5 = 0; static bool bDBCS = false, bDBCS_Checked = false; if (!bDBCS_Checked) { bDBCS = (GetSystemMetrics(SM_DBCSENABLED) != 0); bDBCS_Checked = true; } bool bDBCS_CP = bDBCS; UINT MaxCharSize = 0; DWORD nCP, nCP1, nMode; if (bDBCS) { nCP = GetConsoleOutputCP(); nCP1 = GetConsoleCP(); GetConsoleMode(hOut, &nMode); if (!AreCpInfoLeads(nCP, &MaxCharSize) || MaxCharSize < 2) { bDBCS_CP = false; } } size_t nBufWidth = bufSize.X; int nWidth = (rgn.Right - rgn.Left + 1); int nHeight = (rgn.Bottom - rgn.Top + 1); int nCurSize = nWidth * nHeight; _ASSERTE(bufSize.X >= nWidth); _ASSERTE(bufSize.Y >= nHeight); _ASSERTE(rgn.Top>=0 && rgn.Bottom>=rgn.Top); _ASSERTE(rgn.Left>=0 && rgn.Right>=rgn.Left); //MSectionLock RCS; //if (gpSrv->pReqSizeSection && !RCS.Lock(gpSrv->pReqSizeSection, TRUE, 30000)) //{ // _ASSERTE(FALSE); // SetLastError(ERROR_INVALID_PARAMETER); // return FALSE; //} COORD bufCoord = {0,0}; DWORD dwErrCode = 0; CONSOLE_SCREEN_BUFFER_INFO sbi_tmp = {}; BOOL bSbiTmp = (BOOL)-1; nTick2 = GetTickCount(); if (!bDBCS_CP && (nCurSize <= MAX_CONREAD_SIZE)) { if (ReadConsoleOutputW(hOut, pData, bufSize, bufCoord, &rgn)) lbRc = TRUE; nTick3 = GetTickCount(); } if (!lbRc) { // Придется читать построчно // Теоретически - можно и блоками, но оверхед очень маленький, а велик // шанс обломаться, если консоль "глючит". Поэтому построчно... //bufSize.X = TextWidth; bufSize.Y = 1; bufCoord.X = 0; bufCoord.Y = 0; //rgn = gpSrv->sbi.srWindow; int Y1 = rgn.Top; int Y2 = rgn.Bottom; CHAR_INFO* pLine = pData; if (!bDBCS_CP) { // Simple processing (no DBCS) - one cell == one wchar_t for (int y = Y1; y <= Y2; y++, rgn.Top++, pLine+=nBufWidth) { nTick3 = GetTickCount(); rgn.Bottom = rgn.Top; #ifdef DUMP_TEST_READS bSbiTmp = GetConsoleScreenBufferInfo(hOut, &sbi_tmp); #endif lbRc = ReadConsoleOutputW(hOut, pLine, bufSize, bufCoord, &rgn); #ifdef DUMP_TEST_READS UNREFERENCED_PARAMETER(sbi_tmp.dwSize.Y); #endif if (!lbRc) { dwErrCode = GetLastError(); _ASSERTE(FALSE && "ReadConsoleOutputW failed in MyReadConsoleOutput"); break; } nTick4 = GetTickCount(); } } else // Process on DBCS-capable systems { for (int y = Y1; y <= Y2; y++, rgn.Top++, pLine+=nBufWidth) { nTick3 = GetTickCount(); rgn.Bottom = rgn.Top; #ifdef DUMP_TEST_READS bSbiTmp = GetConsoleScreenBufferInfo(hOut, &sbi_tmp); #endif lbRc = ReadConsoleOutputW(hOut, pLine, bufSize, bufCoord, &rgn); #ifdef DUMP_TEST_READS UNREFERENCED_PARAMETER(sbi_tmp.dwSize.Y); #endif if (!lbRc) { dwErrCode = GetLastError(); _ASSERTE(FALSE && "ReadConsoleOutputW failed in MyReadConsoleOutput"); break; } // DBCS corrections (we need only glyph per hieroglyph for drawing) const CHAR_INFO* pSrc = pLine; const CHAR_INFO* pEnd = pLine + nBufWidth; CHAR_INFO* pDst = pLine; // Check first, line has hieroglyphs? while (pSrc < pEnd) { if (pSrc->Attributes & COMMON_LVB_LEADING_BYTE) break; *(pDst++) = *(pSrc++); } // If line was not fully processed (LVB was found) if (pSrc < pEnd) { // Yes, Process this line with substitutions while (pSrc < pEnd) { *pDst = *pSrc; wchar_t wc = pSrc->Char.UnicodeChar; if (pSrc->Attributes & COMMON_LVB_LEADING_BYTE) { while (((++pSrc) < pEnd) && !(pSrc->Attributes & COMMON_LVB_TRAILING_BYTE)) { // May be 2 or 4 cells if ((pDst->Char.UnicodeChar != wc) && ((pDst+1) < pEnd)) { wc = pSrc->Char.UnicodeChar; *(++pDst) = *pSrc; } } pDst->Attributes |= COMMON_LVB_TRAILING_BYTE; } pSrc++; pDst++; } // Clean rest of line WORD nLastAttr = (pEnd-1)->Attributes; while (pDst < pEnd) { pDst->Attributes = nLastAttr; pDst->Char.UnicodeChar = L' '; pDst++; } } // Line is done nTick4 = GetTickCount(); } } // End of DBCS-capable processing nTick5 = GetTickCount(); } UNREFERENCED_PARAMETER(nTick1); UNREFERENCED_PARAMETER(nTick2); UNREFERENCED_PARAMETER(nTick3); UNREFERENCED_PARAMETER(nTick4); UNREFERENCED_PARAMETER(nTick5); UNREFERENCED_PARAMETER(sbi_tmp.dwSize.Y); UNREFERENCED_PARAMETER(bSbiTmp); return lbRc; }