in DbgShell/ConsoleControl.cs [1419:1622]
private static void WriteConsoleOutputCJK(ConsoleHandle consoleHandle, Coordinates origin, Rectangle contentsRegion, BufferCell[,] contents, BufferCellArrayRowType rowType)
{
Util.Assert(origin.X >= 0 && origin.Y >= 0,
"origin must be within the output buffer");
int rows = contentsRegion.Bottom - contentsRegion.Top + 1;
int cols = contentsRegion.Right - contentsRegion.Left + 1;
CONSOLE_FONT_INFO_EX fontInfo = GetConsoleFontInfo(consoleHandle);
int fontType = fontInfo.FontFamily & NativeMethods.FontTypeMask;
bool trueTypeInUse = (fontType & NativeMethods.TrueTypeFont) == NativeMethods.TrueTypeFont;
int bufferLimit = 2 * 1024; // Limit is 8K bytes as each CHAR_INFO takes 4 bytes
COORD bufferCoord;
bufferCoord.X = 0;
bufferCoord.Y = 0;
// keeps track of which screen area write
SMALL_RECT writeRegion;
writeRegion.Top = (short)origin.Y;
int rowsRemaining = rows;
while (rowsRemaining > 0)
{
// Iteration of columns is nested inside iteration of rows.
// If the size of contents exceeds the buffer limit, writing is
// done in blocks of size equal to the bufferlimit from left to right
// then top to bottom.
// For each iteration of rows,
// - writeRegion.Left and bufferSize.X are reset
// - rowsRemaining, writeRegion.Top, writeRegion.Bottom, and bufferSize.Y
// are updated
// For each iteration of columns,
// - writeRegion.Left, writeRegion.Right and bufferSize.X are updated
writeRegion.Left = (short)origin.X;
COORD bufferSize;
bufferSize.X = (short)Math.Min(cols, bufferLimit);
bufferSize.Y = (short)Math.Min
(
rowsRemaining,
bufferLimit / bufferSize.X
);
writeRegion.Bottom = (short)(writeRegion.Top + bufferSize.Y - 1);
// atRow is at which row of contents a particular iteration is operating
int atRow = rows - rowsRemaining + contentsRegion.Top;
// number of columns yet to be written
int colsRemaining = cols;
while (colsRemaining > 0)
{
writeRegion.Right = (short)(writeRegion.Left + bufferSize.X - 1);
// atCol is at which column of contents a particular iteration is operating
int atCol = cols - colsRemaining + contentsRegion.Left;
// if this is not the last column iteration &&
// the leftmost BufferCell is a leading cell, don't write that cell
if (colsRemaining > bufferSize.X &&
contents[atRow, atCol + bufferSize.X - 1].BufferCellType == BufferCellType.Leading)
{
bufferSize.X--;
writeRegion.Right--;
}
CHAR_INFO[] characterBuffer = new CHAR_INFO[bufferSize.Y * bufferSize.X];
// copy characterBuffer to contents;
int characterBufferIndex = 0;
bool lastCharIsLeading = false;
BufferCell lastLeadingCell = new BufferCell();
for (int r = atRow; r < bufferSize.Y + atRow; r++)
{
for (int c = atCol; c < bufferSize.X + atCol; c++, characterBufferIndex++)
{
if (contents[r, c].BufferCellType == BufferCellType.Complete)
{
characterBuffer[characterBufferIndex].UnicodeChar =
(ushort)contents[r, c].Character;
characterBuffer[characterBufferIndex].Attributes =
(ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor));
lastCharIsLeading = false;
}
else if (contents[r, c].BufferCellType == BufferCellType.Leading)
{
characterBuffer[characterBufferIndex].UnicodeChar =
(ushort)contents[r, c].Character;
characterBuffer[characterBufferIndex].Attributes =
(ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor)
| (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_LEADING_BYTE);
lastCharIsLeading = true;
lastLeadingCell = contents[r, c];
}
else if (contents[r, c].BufferCellType == BufferCellType.Trailing)
{
// The FontFamily is a 8-bit integer. The low-order bit (bit 0) specifies the pitch of the font.
// If it is 1, the font is variable pitch (or proportional). If it is 0, the font is fixed pitch
// (or monospace). Bits 1 and 2 specify the font type. If both bits are 0, the font is a raster font;
// if bit 1 is 1 and bit 2 is 0, the font is a vector font; if bit 1 is 0 and bit 2 is set, or if both
// bits are 1, the font is true type. Bit 3 is 1 if the font is a device font; otherwise, it is 0.
// We only care about the bit 1 and 2, which indicate the font type.
// There are only two font type defined for the Console, at
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\.
// Console\Nls --- national language supports
// Console\RasterFonts --- raster type font
// Console\TrueTypeFont --- true type font
// For CJK characters, if it's TrueType, we need to output the trailing character marked with "Trailing_byte"
// attribute. But if it's RasterFont, we ignore the trailing character, and the "Leading_byte"/"Trailing_byte"
// attributes are not effective at all when reading the character from the console buffer.
if (lastCharIsLeading && trueTypeInUse)
{
// For TrueType Font, we output the trailing byte with "Trailing_byte" attribute
characterBuffer[characterBufferIndex].UnicodeChar = lastLeadingCell.Character;
characterBuffer[characterBufferIndex].Attributes =
(ushort)(ColorToWORD(contents[r, c].ForegroundColor, contents[r, c].BackgroundColor)
| (ushort)NativeMethods.CHAR_INFO_Attributes.COMMON_LVB_TRAILING_BYTE);
}
else
{
// We don't output anything for this cell if Raster font is in use, or if the last cell is not a leading byte
characterBufferIndex--;
}
lastCharIsLeading = false;
}
}
}
// Now writeRegion, bufferSize and characterBuffer are updated.
// Call NativeMethods.WriteConsoleOutput
bool result;
if ((rowType & BufferCellArrayRowType.RightLeading) != 0 &&
colsRemaining == bufferSize.X)
{
COORD bSize = bufferSize;
bSize.X++;
SMALL_RECT wRegion = writeRegion;
wRegion.Right++;
// Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
// get the error code.
#pragma warning disable 56523
result = NativeMethods.WriteConsoleOutput(
consoleHandle.DangerousGetHandle(),
characterBuffer,
bSize,
bufferCoord,
ref wRegion);
}
else
{
// Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
// get the error code.
#pragma warning disable 56523
result = NativeMethods.WriteConsoleOutput(
consoleHandle.DangerousGetHandle(),
characterBuffer,
bufferSize,
bufferCoord,
ref writeRegion);
}
if (result == false)
{
// When WriteConsoleOutput fails, half bufferLimit
if (bufferLimit < 2)
{
int err = Marshal.GetLastWin32Error();
HostException e = CreateHostException(err, "WriteConsoleOutput",
ErrorCategory.WriteError, "The Win32 internal error \"{0}\" 0x{1:X} occurred while writing to the console output buffer. Contact Microsoft Customer Support Services." ); //ConsoleControlStrings.WriteConsoleOutputExceptionTemplate);
throw e;
}
bufferLimit /= 2;
if (cols == colsRemaining)
{
// if cols == colsRemaining, nothing is guaranteed written in this pass and
// the unwritten area is still rectangular
bufferSize.Y = 0;
break;
}
else
{
// some areas have been written. This could only happen when the number of columns
// to write is larger than bufferLimit. In that case, the algorithm writes one row
// at a time => bufferSize.Y == 1. Then, we can safely leave bufferSize.Y unchanged
// to retry with a smaller bufferSize.X.
Util.Assert(bufferSize.Y == 1, string.Format(CultureInfo.InvariantCulture, "bufferSize.Y should be 1, but is {0}", bufferSize.Y));
bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
continue;
}
}
colsRemaining -= bufferSize.X;
writeRegion.Left += bufferSize.X;
bufferSize.X = (short)Math.Min(colsRemaining, bufferLimit);
} // column iteration
rowsRemaining -= bufferSize.Y;
writeRegion.Top += bufferSize.Y;
} // row iteration
}