UNPKG

6.62 kBtext/x-cView Raw
1/*
2
3Test program demonstrating a problem in Windows 15048's ReadConsoleOutput API.
4
5To compile:
6
7 cl /nologo /EHsc winbug-15048.cc shell32.lib
8
9Example of regressed input:
10
11Case 1:
12
13 > chcp 932
14 > winbug-15048 -face-gothic 3044
15
16 Correct output:
17
18 1**34 (nb: U+3044 replaced with '**' to avoid MSVC encoding warning)
19 5678
20
21 ReadConsoleOutputW (both rows, 3 cols)
22 row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007)
23 row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007)
24
25 ReadConsoleOutputW (both rows, 4 cols)
26 row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) U+0034(0007)
27 row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
28
29 ReadConsoleOutputW (second row)
30 row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
31
32 ...
33
34 Win10 15048 bad output:
35
36 1**34
37 5678
38
39 ReadConsoleOutputW (both rows, 3 cols)
40 row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0035(0007)
41 row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0000(0000)
42
43 ReadConsoleOutputW (both rows, 4 cols)
44 row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0034(0007) U+0035(0007)
45 row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) U+0000(0000)
46
47 ReadConsoleOutputW (second row)
48 row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
49
50 ...
51
52 The U+3044 character (HIRAGANA LETTER I) occupies two columns, but it only
53 fills one record in the ReadConsoleOutput output buffer, which has the
54 effect of shifting the first cell of the second row into the last cell of
55 the first row. Ordinarily, the first and second cells would also have the
56 COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE attributes set, which
57 allows winpty to detect the double-column character.
58
59Case 2:
60
61 > chcp 437
62 > winbug-15048 -face "Lucida Console" -h 4 221A
63
64 The same issue happens with U+221A (SQUARE ROOT), but only in certain
65 fonts. The console seems to think this character occupies two columns
66 if the font is sufficiently small. The Windows console properties dialog
67 doesn't allow fonts below 5 pt, but winpty tries to use 2pt and 4pt Lucida
68 Console to allow very large console windows.
69
70Case 3:
71
72 > chcp 437
73 > winbug-15048 -face "Lucida Console" -h 12 FF12
74
75 The console selection system thinks U+FF12 (FULLWIDTH DIGIT TWO) occupies
76 two columns, which happens to be correct, but it's displayed as a single
77 column unrecognized character. It otherwise behaves the same as the other
78 cases.
79
80*/
81
82#include <windows.h>
83#include <assert.h>
84#include <string.h>
85#include <stdlib.h>
86#include <stdio.h>
87#include <wchar.h>
88
89#include <string>
90
91#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
92
93// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
94const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
95const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
96const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
97const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
98
99static void set_font(const wchar_t *name, int size) {
100 const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
101 CONSOLE_FONT_INFOEX fontex {};
102 fontex.cbSize = sizeof(fontex);
103 fontex.dwFontSize.Y = size;
104 fontex.FontWeight = 400;
105 fontex.FontFamily = 0x36;
106 wcsncpy(fontex.FaceName, name, COUNT_OF(fontex.FaceName));
107 assert(SetCurrentConsoleFontEx(conout, FALSE, &fontex));
108}
109
110static void usage(const wchar_t *prog) {
111 printf("Usage: %ls [options]\n", prog);
112 printf(" -h HEIGHT\n");
113 printf(" -face FACENAME\n");
114 printf(" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
115 printf(" hhhh -- print U+hhhh\n");
116 exit(1);
117}
118
119static void dump_region(SMALL_RECT region, const char *name) {
120 const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
121
122 CHAR_INFO buf[1000];
123 memset(buf, 0xcc, sizeof(buf));
124
125 const int w = region.Right - region.Left + 1;
126 const int h = region.Bottom - region.Top + 1;
127
128 assert(ReadConsoleOutputW(
129 conout, buf, { (short)w, (short)h }, { 0, 0 },
130 &region));
131
132 printf("\n");
133 printf("ReadConsoleOutputW (%s)\n", name);
134 for (int y = 0; y < h; ++y) {
135 printf("row %d: ", region.Top + y);
136 for (int i = 0; i < region.Left * 13; ++i) {
137 printf(" ");
138 }
139 for (int x = 0; x < w; ++x) {
140 const int i = y * w + x;
141 printf("U+%04x(%04x) ", buf[i].Char.UnicodeChar, buf[i].Attributes);
142 }
143 printf("\n");
144 }
145}
146
147int main() {
148 wchar_t *cmdline = GetCommandLineW();
149 int argc = 0;
150 wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
151 const wchar_t *font_name = L"Lucida Console";
152 int font_height = 8;
153 int test_ch = 0xff12; // U+FF12 FULLWIDTH DIGIT TWO
154
155 for (int i = 1; i < argc; ++i) {
156 const std::wstring arg = argv[i];
157 const std::wstring next = i + 1 < argc ? argv[i + 1] : L"";
158 if (arg == L"-face" && i + 1 < argc) {
159 font_name = argv[i + 1];
160 i++;
161 } else if (arg == L"-face-gothic") {
162 font_name = kMSGothic;
163 } else if (arg == L"-face-simsun") {
164 font_name = kNSimSun;
165 } else if (arg == L"-face-minglight") {
166 font_name = kMingLight;
167 } else if (arg == L"-face-gulimche") {
168 font_name = kGulimChe;
169 } else if (arg == L"-h" && i + 1 < argc) {
170 font_height = _wtoi(next.c_str());
171 i++;
172 } else if (arg.c_str()[0] != '-') {
173 test_ch = wcstol(arg.c_str(), NULL, 16);
174 } else {
175 printf("Unrecognized argument: %ls\n", arg.c_str());
176 usage(argv[0]);
177 }
178 }
179
180 const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
181
182 set_font(font_name, font_height);
183
184 system("cls");
185 DWORD actual = 0;
186 wchar_t output[] = L"1234\n5678\n";
187 output[1] = test_ch;
188 WriteConsoleW(conout, output, 10, &actual, nullptr);
189
190 dump_region({ 0, 0, 3, 1 }, "both rows, 3 cols");
191 dump_region({ 0, 0, 4, 1 }, "both rows, 4 cols");
192 dump_region({ 0, 1, 4, 1 }, "second row");
193 dump_region({ 0, 0, 4, 0 }, "first row");
194 dump_region({ 1, 0, 4, 0 }, "first row, skip 1");
195 dump_region({ 2, 0, 4, 0 }, "first row, skip 2");
196 dump_region({ 3, 0, 4, 0 }, "first row, skip 3");
197
198 set_font(font_name, 14);
199
200 return 0;
201}