int getwtext(int __left, int __top, int __right, int __bottom, wchar_info *__destin) { int i; SMALL_RECT r; CHAR_INFO* buffer; COORD s, c = { 0,0 }; if (__right<__left || __bottom<__top) return 0; init_ti(); r.Left = __left - 1; r.Top = __top - 1; r.Right = __right - 1; r.Bottom = __bottom - 1; s.X = __right - __left + 1; s.Y = __bottom - __top + 1; buffer = (CHAR_INFO*)malloc(s.X * s.Y * sizeof(CHAR_INFO)); if (ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), buffer, s, c, &r)) { for (i = 0; i < s.X * s.Y; i++) { __destin[i].letter = buffer[i].Char.UnicodeChar; __destin[i].attr = buffer[i].Attributes; } } free(buffer); return 0; }
int movetext(int __left, int __top, int __right, int __bottom, int __destleft, int __desttop) { CHAR_INFO * buffer; SMALL_RECT r; COORD c = { 0,0 }, s; HANDLE con; init_ti(); s.X = __right - __left + 1; s.Y = __bottom - __top + 1; if (s.X <= 0 || s.Y <= 0) return 0; buffer = (CHAR_INFO*)malloc(s.X * s.Y * sizeof(CHAR_INFO)); con = GetStdHandle(STD_OUTPUT_HANDLE); r.Left = __left - 1; r.Top = __top - 1; r.Right = __right - 1; r.Bottom = __bottom - 1; if (ReadConsoleOutputW(con, buffer, s, c, &r)) WriteConsoleOutputW(con, buffer, s, c, &r); free(buffer); return 0; }
// Консоль любит глючить, при попытках запроса более определенного количества ячеек. // MAX_CONREAD_SIZE подобрано экспериментально BOOL ReadConsoleOutputEx(HANDLE hOut, CHAR_INFO *pData, COORD bufSize, SMALL_RECT rgn, COORD* lpCursor /*= NULL*/) { 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) { if (!IsWin10()) bDBCS_CP = false; } } */ bool bDBCS_CP = IsConsoleDoubleCellCP(); 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)) { InterlockedIncrement(&gnInReadConsoleOutput); if (ReadConsoleOutputW(hOut, pData, bufSize, bufCoord, &rgn)) lbRc = TRUE; InterlockedDecrement(&gnInReadConsoleOutput); 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 InterlockedIncrement(&gnInReadConsoleOutput); lbRc = ReadConsoleOutputW(hOut, pLine, bufSize, bufCoord, &rgn); InterlockedDecrement(&gnInReadConsoleOutput); #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 InterlockedIncrement(&gnInReadConsoleOutput); lbRc = ReadConsoleOutputW(hOut, pLine, bufSize, bufCoord, &rgn); InterlockedDecrement(&gnInReadConsoleOutput); #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) { // If caller want to know correction status CHAR_INFO* pCursorEnd = (lpCursor && (lpCursor->Y == rgn.Top)) ? (pLine + lpCursor->X) : pLine; int iCellsSkipped = 0; // Yes, Process this line with substitutions while (pSrc < pEnd) { *pDst = *pSrc; wchar_t wc = pSrc->Char.UnicodeChar; if (pSrc->Attributes & COMMON_LVB_LEADING_BYTE) { // If caller want to know correction status if ((pSrc+1) <= pCursorEnd) { iCellsSkipped++; } // Process LVB flags 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; } // If caller want to know correction status else if (pSrc <= pCursorEnd) { iCellsSkipped++; } } 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++; } // If caller want to know correction status if (lpCursor && (lpCursor->Y == rgn.Top)) { lpCursor->X = max(0, (lpCursor->X - iCellsSkipped)); } } // 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; }
// Консоль любит глючить, при попытках запроса более определенного количества ячеек. // 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; LPCSTR szLeads = NULL; UINT MaxCharSize = 0; DWORD nCP, nCP1, nMode; if (bDBCS) { nCP = GetConsoleOutputCP(); nCP1 = GetConsoleCP(); GetConsoleMode(hOut, &nMode); szLeads = GetCpInfoLeads(nCP, &MaxCharSize); if (!szLeads || !*szLeads || 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; 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) { for (int y = Y1; y <= Y2; y++, rgn.Top++, pLine+=nBufWidth) { nTick3 = GetTickCount(); rgn.Bottom = rgn.Top; lbRc = ReadConsoleOutputW(hOut, pLine, bufSize, bufCoord, &rgn); if (!lbRc) { dwErrCode = GetLastError(); _ASSERTE(FALSE && "ReadConsoleOutputW failed in MyReadConsoleOutput"); break; } nTick4 = GetTickCount(); } } else { DWORD nAttrsMax = bufSize.X; DWORD nCharsMax = nAttrsMax/* *4 */; // -- максимум там вроде на некоторых CP - 4 байта wchar_t* pszChars = (wchar_t*)malloc(nCharsMax*sizeof(*pszChars)); char* pszCharsA = (char*)malloc(nCharsMax*sizeof(*pszCharsA)); WORD* pnAttrs = (WORD*)malloc(bufSize.X*sizeof(*pnAttrs)); if (pszChars && pszCharsA && pnAttrs) { COORD crRead = {rgn.Left,Y1}; DWORD nChars, nAttrs, nCharsA; CHAR_INFO* pLine = pData; for (; crRead.Y <= Y2; crRead.Y++, pLine+=nBufWidth) { nTick3 = GetTickCount(); rgn.Bottom = rgn.Top; nChars = nCharsA = nAttrs = 0; BOOL lbRcTxt = ReadConsoleOutputCharacterA(hOut, pszCharsA, nCharsMax, crRead, &nCharsA); dwErrCode = GetLastError(); if (!lbRcTxt || !nCharsA) { nCharsA = 0; lbRcTxt = ReadConsoleOutputCharacterW(hOut, pszChars, nCharsMax, crRead, &nChars); dwErrCode = GetLastError(); } BOOL lbRcAtr = ReadConsoleOutputAttribute(hOut, pnAttrs, bufSize.X, crRead, &nAttrs); dwErrCode = GetLastError(); lbRc = lbRcTxt && lbRcAtr; if (!lbRc) { dwErrCode = GetLastError(); _ASSERTE(FALSE && "ReadConsoleOutputAttribute failed in MyReadConsoleOutput"); CHAR_INFO* p = pLine; for (size_t i = 0; i < nAttrsMax; ++i, ++p) { p->Attributes = 4; // red on black p->Char.UnicodeChar = 0xFFFE; // not a character } break; } else { if (nCharsA) { nChars = MultiByteToWideChar(nCP, 0, pszCharsA, nCharsA, pszChars, nCharsMax); } CHAR_INFO* p = pLine; wchar_t* psz = pszChars; WORD* pn = pnAttrs; //int i = nAttrsMax; //while ((i--) > 0) //{ // p->Attributes = *(pn++); // p->Char.UnicodeChar = *(psz++); // p++; //} size_t x1 = min(nChars,nAttrsMax); size_t x2 = nAttrsMax; for (size_t i = 0; i < x1; ++i, ++p) { p->Attributes = *pn; p->Char.UnicodeChar = *psz; WARNING("Некорректно! pn может указывать на начало блока DBCS/QBCS"); pn++; // += MaxCharSize; psz++; } WORD nLastAttr = pnAttrs[max(0,(int)nAttrs-1)]; for (size_t i = x1; i < x2; ++i, ++p) { p->Attributes = nLastAttr; p->Char.UnicodeChar = L' '; } } nTick4 = GetTickCount(); } } SafeFree(pszChars); SafeFree(pszCharsA); SafeFree(pnAttrs); } nTick5 = GetTickCount(); } UNREFERENCED_PARAMETER(nTick1); UNREFERENCED_PARAMETER(nTick2); UNREFERENCED_PARAMETER(nTick3); UNREFERENCED_PARAMETER(nTick4); UNREFERENCED_PARAMETER(nTick5); return lbRc; }