ReadConsoleOutputCharacterW behavior change in 1.22 #23096

Open
opened 2026-01-31 08:32:16 +00:00 by claunia · 0 comments
Owner

Originally created by @bradking on GitHub (Mar 31, 2025).

Originally assigned to: @DHowett on GitHub.

Windows Terminal version

1.22.10731.0

Windows build number

10.0.26100.3476

Other Software

Here is sample code that writes a line of text to the console using WriteConsoleW, reads the line back using ReadConsoleOutputCharacterW, and then compares the content.

example.cxx (click to expand)
#include <cstring>
#include <iomanip>
#include <iostream>
#include <vector>
#include <wchar.h>
#include <windows.h>

int main()
{
  std::wstring const text =
    L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 " // Hindi
    L"\u03B5\u03AF\u03BD \u03B1\u03B9 "            // Greek
    L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!" // Russian
    ;

  // Write a line of text to the console.
  HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  WriteConsoleW(hOut, text.data(), text.size(), nullptr, nullptr);
  WriteConsoleW(hOut, L"\n", 1, nullptr, nullptr);

  // Read the line of text back from the console.
  std::vector<wchar_t> received;
  {
    CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
    if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) {
      std::cerr << "GetConsoleScreenBufferInfo failed\n";
      return 1;
    }
    DWORD width = screenBufferInfo.dwSize.X;
    received.resize(width);
    COORD coord{ 0, screenBufferInfo.dwCursorPosition.Y - 1 };
    DWORD charsRead = 0;
    if (!ReadConsoleOutputCharacterW(hOut, received.data(), width, coord,
                                     &charsRead) ||
        charsRead == 0) {
      std::cerr << "ReadConsoleOutputCharacterW failed\n";
      return 1;
    }
  }

  // Compare the line we read to the line we wrote.
  if (std::memcmp(received.data(), text.data(),
                  text.size() * sizeof(wchar_t)) == 0) {
    std::cerr << "Console has expected content" << std::endl;
  } else {
    std::cerr << "Expected output | Received output" << std::endl;
    for (size_t i = 0; i < text.size(); i++) {
      std::cerr << std::setbase(16) << std::setfill('0') << "     "
                << "0x" << std::setw(8) << static_cast<unsigned int>(text[i])
                << " | "
                << "0x" << std::setw(8)
                << static_cast<unsigned int>(received[i]);
      if (static_cast<unsigned int>(text[i]) !=
          static_cast<unsigned int>(received[i])) {
        std::cerr << "   MISMATCH!";
      }
      std::cerr << std::endl;
    }
    std::cerr << std::endl;
    return 1;
  }

  return 0;
}

Steps to reproduce

Compile the above example.cxx sample code and run it in a Windows Terminal.

>cl -EHsc example.cxx
>example

Expected Behavior

ReadConsoleOutputCharacterW recovers what WriteConsoleW wrote, as it did in Windows Terminal 1.21 and always has in Windows Console Host:

>example
यूनिकोड είν αι здорово!
Console has expected content

Actual Behavior

ReadConsoleOutputCharacterW receives text partially replaced by 0xFFFD replacement characters.

>example
यूनिकोड είν αι здорово!
Expected output | Received output
     0x0000092f | 0x0000fffd   MISMATCH!
     0x00000942 | 0x0000fffd   MISMATCH!
     0x00000928 | 0x0000fffd   MISMATCH!
     0x0000093f | 0x00000921   MISMATCH!
     0x00000915 | 0x00000020   MISMATCH!
     0x0000094b | 0x000003b5   MISMATCH!
     0x00000921 | 0x000003af   MISMATCH!
     0x00000020 | 0x000003bd   MISMATCH!
     0x000003b5 | 0x00000020   MISMATCH!
     0x000003af | 0x000003b1   MISMATCH!
     0x000003bd | 0x000003b9   MISMATCH!
     0x00000020 | 0x00000020
     ...
Originally created by @bradking on GitHub (Mar 31, 2025). Originally assigned to: @DHowett on GitHub. ### Windows Terminal version 1.22.10731.0 ### Windows build number 10.0.26100.3476 ### Other Software Here is sample code that writes a line of text to the console using `WriteConsoleW`, reads the line back using `ReadConsoleOutputCharacterW`, and then compares the content. <details><summary>example.cxx (click to expand)</summary> ```c++ #include <cstring> #include <iomanip> #include <iostream> #include <vector> #include <wchar.h> #include <windows.h> int main() { std::wstring const text = L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 " // Hindi L"\u03B5\u03AF\u03BD \u03B1\u03B9 " // Greek L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!" // Russian ; // Write a line of text to the console. HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleW(hOut, text.data(), text.size(), nullptr, nullptr); WriteConsoleW(hOut, L"\n", 1, nullptr, nullptr); // Read the line of text back from the console. std::vector<wchar_t> received; { CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) { std::cerr << "GetConsoleScreenBufferInfo failed\n"; return 1; } DWORD width = screenBufferInfo.dwSize.X; received.resize(width); COORD coord{ 0, screenBufferInfo.dwCursorPosition.Y - 1 }; DWORD charsRead = 0; if (!ReadConsoleOutputCharacterW(hOut, received.data(), width, coord, &charsRead) || charsRead == 0) { std::cerr << "ReadConsoleOutputCharacterW failed\n"; return 1; } } // Compare the line we read to the line we wrote. if (std::memcmp(received.data(), text.data(), text.size() * sizeof(wchar_t)) == 0) { std::cerr << "Console has expected content" << std::endl; } else { std::cerr << "Expected output | Received output" << std::endl; for (size_t i = 0; i < text.size(); i++) { std::cerr << std::setbase(16) << std::setfill('0') << " " << "0x" << std::setw(8) << static_cast<unsigned int>(text[i]) << " | " << "0x" << std::setw(8) << static_cast<unsigned int>(received[i]); if (static_cast<unsigned int>(text[i]) != static_cast<unsigned int>(received[i])) { std::cerr << " MISMATCH!"; } std::cerr << std::endl; } std::cerr << std::endl; return 1; } return 0; } ``` </details> ### Steps to reproduce Compile the above `example.cxx` sample code and run it in a Windows Terminal. ``` >cl -EHsc example.cxx >example ``` ### Expected Behavior `ReadConsoleOutputCharacterW` recovers what `WriteConsoleW` wrote, as it did in Windows Terminal 1.21 and always has in Windows Console Host: ``` >example यूनिकोड είν αι здорово! Console has expected content ``` ### Actual Behavior `ReadConsoleOutputCharacterW` receives text partially replaced by `0xFFFD` replacement characters. ``` >example यूनिकोड είν αι здорово! Expected output | Received output 0x0000092f | 0x0000fffd MISMATCH! 0x00000942 | 0x0000fffd MISMATCH! 0x00000928 | 0x0000fffd MISMATCH! 0x0000093f | 0x00000921 MISMATCH! 0x00000915 | 0x00000020 MISMATCH! 0x0000094b | 0x000003b5 MISMATCH! 0x00000921 | 0x000003af MISMATCH! 0x00000020 | 0x000003bd MISMATCH! 0x000003b5 | 0x00000020 MISMATCH! 0x000003af | 0x000003b1 MISMATCH! 0x000003bd | 0x000003b9 MISMATCH! 0x00000020 | 0x00000020 ... ```
claunia added the Needs-TriageIssue-BugNeeds-Attention labels 2026-01-31 08:32:17 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23096