Skip to content

ReadConsoleOutputCharacterW behavior change in 1.22 #18748

Closed
@bradking

Description

@bradking

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
     ...

Metadata

Metadata

Assignees

Labels

Issue-BugIt either shouldn't be doing this or needs an investigation.Needs-AttentionThe core contributors need to come back around and look at this ASAP.Needs-TriageIt's a new issue that the core contributor team needs to triage at the next triage meeting

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions