WriteConsoleOutput() does not write at expected position when console window is maximized/restored #21941

Open
opened 2026-01-31 07:58:56 +00:00 by claunia · 0 comments
Owner

Originally created by @tigrouind on GitHub (Jul 8, 2024).

Originally assigned to: @lhecker on GitHub.

Windows Terminal version

1.20.11381.0

Windows build number

10.0.22631.3737

Other Software

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
	internal class Program
	{
		[DllImport("Kernel32.dll")]
		static extern IntPtr CreateFile(
			string fileName,
			[MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
			[MarshalAs(UnmanagedType.U4)] FileShare fileShare,
			IntPtr securityAttributes,
			[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
			[MarshalAs(UnmanagedType.U4)] FileAttributes flags,
			IntPtr template);

		[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
		static extern bool WriteConsoleOutput(
			IntPtr hConsoleOutput,
			CharInfo[] lpBuffer,
			Coord dwBufferSize,
			Coord dwBufferCoord,
			ref SmallRect lpWriteRegion);

		const uint GENERIC_WRITE = 0x40000000;

		[StructLayout(LayoutKind.Sequential)]
		public struct Coord
		{
			public short X;
			public short Y;
		};

		[StructLayout(LayoutKind.Sequential)]
		public struct SmallRect
		{
			public short Left;
			public short Top;
			public short Right;
			public short Bottom;
		}

		[StructLayout(LayoutKind.Explicit)]
		public struct CharInfo
		{
			[FieldOffset(0)] public char Char;
			[FieldOffset(2)] public short Attributes;
		}

		static void Main()
		{
			int backgroundColor = 1;
			var handle = CreateFile("CONOUT$", (FileAccess)GENERIC_WRITE, FileShare.Write, 
						IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

			while (true)
			{
				int width = Console.WindowWidth;
				int height = Console.WindowHeight;

				CharInfo[] buffer = new CharInfo[width * height];
				for (int i = 0; i < buffer.Length; i++)
				{
					buffer[i] = new CharInfo() { 
						Char = ' ', 
						Attributes = (short)((backgroundColor % 3 + 1) << 4) 
					};
				}

				var rect = new SmallRect() { 
					Left = 0, 
					Top = 0, 
					Right = (short)(width - 1), 
					Bottom = (short)(height - 1) 
				};
				//Console.SetCursorPosition(1, 0); //fix the issue
				WriteConsoleOutput(handle, buffer, new Coord() {
					X = (short)width,
					Y = (short)height 
				}, new Coord() { X = 0, Y = 0 }, ref rect);
				Console.Beep(500, 250);
				System.Threading.Thread.Sleep(1000);
				backgroundColor++;			
			}
		}
	}
}

Steps to reproduce

Run the program above.
It outputs a new background color every second, along with a beep.
Maximize console window (eg: double click on title)
Restore console window (eg: double click on title again)

Expected Behavior

Right after console window is restored, the background color is updated.

Actual Behavior

The background color stay the same.

WriteConsoleOutput() render characters at the top of the console area, which is invisible (you have to scroll up to see it).
Instead, it should render characters in the visible portion of the console.
Setting the console cursor at a position other than (0, 0) fix the issue. That probably why some project like Terminal.Gui are not affected (eg: cursor is usually set inside active control).
The old command prompt does not have this bug. Using AtlasEngine does not fix the issue.

Originally created by @tigrouind on GitHub (Jul 8, 2024). Originally assigned to: @lhecker on GitHub. ### Windows Terminal version 1.20.11381.0 ### Windows build number 10.0.22631.3737 ### Other Software ```csharp using System; using System.IO; using System.Runtime.InteropServices; namespace ConsoleApp1 { internal class Program { [DllImport("Kernel32.dll")] static extern IntPtr CreateFile( string fileName, [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess, [MarshalAs(UnmanagedType.U4)] FileShare fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] FileAttributes flags, IntPtr template); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern bool WriteConsoleOutput( IntPtr hConsoleOutput, CharInfo[] lpBuffer, Coord dwBufferSize, Coord dwBufferCoord, ref SmallRect lpWriteRegion); const uint GENERIC_WRITE = 0x40000000; [StructLayout(LayoutKind.Sequential)] public struct Coord { public short X; public short Y; }; [StructLayout(LayoutKind.Sequential)] public struct SmallRect { public short Left; public short Top; public short Right; public short Bottom; } [StructLayout(LayoutKind.Explicit)] public struct CharInfo { [FieldOffset(0)] public char Char; [FieldOffset(2)] public short Attributes; } static void Main() { int backgroundColor = 1; var handle = CreateFile("CONOUT$", (FileAccess)GENERIC_WRITE, FileShare.Write, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); while (true) { int width = Console.WindowWidth; int height = Console.WindowHeight; CharInfo[] buffer = new CharInfo[width * height]; for (int i = 0; i < buffer.Length; i++) { buffer[i] = new CharInfo() { Char = ' ', Attributes = (short)((backgroundColor % 3 + 1) << 4) }; } var rect = new SmallRect() { Left = 0, Top = 0, Right = (short)(width - 1), Bottom = (short)(height - 1) }; //Console.SetCursorPosition(1, 0); //fix the issue WriteConsoleOutput(handle, buffer, new Coord() { X = (short)width, Y = (short)height }, new Coord() { X = 0, Y = 0 }, ref rect); Console.Beep(500, 250); System.Threading.Thread.Sleep(1000); backgroundColor++; } } } } ``` ### Steps to reproduce Run the program above. It outputs a new background color every second, along with a beep. Maximize console window (eg: double click on title) Restore console window (eg: double click on title again) ### Expected Behavior Right after console window is restored, the background color is updated. ### Actual Behavior The background color stay the same. WriteConsoleOutput() render characters at the top of the console area, which is invisible (you have to scroll up to see it). Instead, it should render characters in the visible portion of the console. Setting the console cursor at a position other than (0, 0) fix the issue. That probably why some project like [Terminal.Gui](https://github.com/gui-cs/Terminal.Gui) are not affected (eg: cursor is usually set inside active control). The old command prompt does not have this bug. Using AtlasEngine does not fix the issue.
claunia added the Product-ConhostIssue-BugNeeds-Tag-FixArea-Server labels 2026-01-31 07:58:56 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#21941