public unsafe static void MoveBufferArea(int sourceLeft, int sourceTop,
int sourceWidth, int sourceHeight, int targetLeft, int targetTop,
char sourceChar, ConsoleColor sourceForeColor,
ConsoleColor sourceBackColor)
{
if (sourceForeColor < ConsoleColor.Black || sourceForeColor > ConsoleColor.White)
throw new ArgumentException(SR.Arg_InvalidConsoleColor, nameof(sourceForeColor));
if (sourceBackColor < ConsoleColor.Black || sourceBackColor > ConsoleColor.White)
throw new ArgumentException(SR.Arg_InvalidConsoleColor, nameof(sourceBackColor));
Contract.EndContractBlock();
Interop.Kernel32.CONSOLE_SCREEN_BUFFER_INFO csbi = GetBufferInfo();
Interop.Kernel32.COORD bufferSize = csbi.dwSize;
if (sourceLeft < 0 || sourceLeft > bufferSize.X)
throw new ArgumentOutOfRangeException(nameof(sourceLeft), sourceLeft, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
if (sourceTop < 0 || sourceTop > bufferSize.Y)
throw new ArgumentOutOfRangeException(nameof(sourceTop), sourceTop, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
if (sourceWidth < 0 || sourceWidth > bufferSize.X - sourceLeft)
throw new ArgumentOutOfRangeException(nameof(sourceWidth), sourceWidth, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
if (sourceHeight < 0 || sourceTop > bufferSize.Y - sourceHeight)
throw new ArgumentOutOfRangeException(nameof(sourceHeight), sourceHeight, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
// Note: if the target range is partially in and partially out
// of the buffer, then we let the OS clip it for us.
if (targetLeft < 0 || targetLeft > bufferSize.X)
throw new ArgumentOutOfRangeException(nameof(targetLeft), targetLeft, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
if (targetTop < 0 || targetTop > bufferSize.Y)
throw new ArgumentOutOfRangeException(nameof(targetTop), targetTop, SR.ArgumentOutOfRange_ConsoleBufferBoundaries);
// If we're not doing any work, bail out now (Windows will return
// an error otherwise)
if (sourceWidth == 0 || sourceHeight == 0)
return;
// Read data from the original location, blank it out, then write
// it to the new location. This will handle overlapping source and
// destination regions correctly.
// Read the old data
Interop.Kernel32.CHAR_INFO[] data = new Interop.Kernel32.CHAR_INFO[sourceWidth * sourceHeight];
bufferSize.X = (short)sourceWidth;
bufferSize.Y = (short)sourceHeight;
Interop.Kernel32.COORD bufferCoord = new Interop.Kernel32.COORD();
Interop.Kernel32.SMALL_RECT readRegion = new Interop.Kernel32.SMALL_RECT();
readRegion.Left = (short)sourceLeft;
readRegion.Right = (short)(sourceLeft + sourceWidth - 1);
readRegion.Top = (short)sourceTop;
readRegion.Bottom = (short)(sourceTop + sourceHeight - 1);
bool r;
fixed (Interop.Kernel32.CHAR_INFO* pCharInfo = data)
r = Interop.Kernel32.ReadConsoleOutput(OutputHandle, pCharInfo, bufferSize, bufferCoord, ref readRegion);
if (!r)
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
// Overwrite old section
Interop.Kernel32.COORD writeCoord = new Interop.Kernel32.COORD();
writeCoord.X = (short)sourceLeft;
Interop.Kernel32.Color c = ConsoleColorToColorAttribute(sourceBackColor, true);
c |= ConsoleColorToColorAttribute(sourceForeColor, false);
short attr = (short)c;
int numWritten;
for (int i = sourceTop; i < sourceTop + sourceHeight; i++)
{
writeCoord.Y = (short)i;
r = Interop.Kernel32.FillConsoleOutputCharacter(OutputHandle, sourceChar, sourceWidth, writeCoord, out numWritten);
Debug.Assert(numWritten == sourceWidth, "FillConsoleOutputCharacter wrote the wrong number of chars!");
if (!r)
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
r = Interop.Kernel32.FillConsoleOutputAttribute(OutputHandle, attr, sourceWidth, writeCoord, out numWritten);
if (!r)
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
}
// Write text to new location
Interop.Kernel32.SMALL_RECT writeRegion = new Interop.Kernel32.SMALL_RECT();
writeRegion.Left = (short)targetLeft;
writeRegion.Right = (short)(targetLeft + sourceWidth);
writeRegion.Top = (short)targetTop;
writeRegion.Bottom = (short)(targetTop + sourceHeight);
fixed (Interop.Kernel32.CHAR_INFO* pCharInfo = data)
Interop.Kernel32.WriteConsoleOutput(OutputHandle, pCharInfo, bufferSize, bufferCoord, ref writeRegion);
}