Compare commits

..

209 Commits

Author SHA1 Message Date
Ryan Prichard
7e59fe2d09 Remove tea-ci drone.yml and switch status badge to Appveyor 2018-12-19 00:40:19 -08:00
Ryan Prichard
a6174e8a4d
Merge pull request #163 from jackyzy823/master
Disable outputing title escape sequences when WINPTY_FLAG_PLAIN_OUTPUT sets
2018-12-19 00:28:51 -08:00
jackyzy823
b60ded1cf2 Do not output title escape sequences when WINPTY_FLAG_PLAIN_OUTPUT sets 2018-12-14 14:16:31 +08:00
Ryan Prichard
afacf7792f Use Appveyor's MSYS2/Cygwin installation rather than my own snapshots
It's simpler to just rely on what Appveyor already provides in its system
images. Appveyor doesn't provide a 32-bit msys2 build, but AFAIK, anyone
needing that can compile it themselves.

Avoid listing gcc-g++ in the Cygwin setup commands because it apparently
breaks the Cygwin toolchain (i.e. missing stddef.h error):
https://ci.appveyor.com/project/rprichard/winpty/builds/19875792
2018-10-29 04:20:06 -07:00
Ryan Prichard
d49712c548 Switch to GitHub prebuilts and add SHA256 checking 2018-10-14 23:53:23 -07:00
Ryan Prichard
784b812663 Add an appveyor config file 2018-10-14 23:08:04 -07:00
Ryan Prichard
ceecad1551 Actually use an XP toolset for XP builds 2018-10-14 22:43:15 -07:00
Ryan Prichard
dcb2df4a4e ship.py: build packages using cygwin/msys2 prebuilts 2018-10-14 22:43:03 -07:00
Ryan Prichard
6aae341b74 ship.py: remove obsolete msys 1.0 mingw32-make code 2018-10-14 19:25:46 -07:00
Ryan Prichard
498a9b1a7f Add util exercising [GS]etConsoleScreenBufferInfoEx
Specifically, allow getting and setting window/buffer size/position.
2018-09-16 20:59:52 -07:00
Ryan Prichard
850661d02b Merge pull request #130 from the-ress/get-console-process-list
Add winpty_get_console_process_list
2017-10-07 20:34:18 -07:00
Tereza Tomcova
f678472b51 Replaced overflow check 2017-10-07 20:33:06 +02:00
Tereza Tomcova
39fe32b15d Fix for edge case when process list changes between GetConsoleProcessList calls 2017-10-04 00:28:05 +02:00
Tereza Tomcova
1424696a6d Add winpty_get_console_process_list 2017-09-16 14:29:23 +02:00
Ryan Prichard
ce9239af5d Update release notes 2017-05-25 20:01:39 -05:00
Ryan Prichard
7d16530ea7 For IntelliJ/JediTerm, allow kBareMod input escapes for navigation keys
Fixes https://github.com/rprichard/winpty/issues/118
2017-05-25 19:49:36 -05:00
Ryan Prichard
4978cf94b6 Go back to using GenerateConsoleCtrlEvent for processed-mode Ctrl-C
This change reverts the first part of the GH-116 fix.

See https://github.com/rprichard/winpty/issues/116
2017-05-19 03:14:13 -05:00
Ryan Prichard
0c86e4f78e Add an entry for Ctrl-C in DefaultInputMap's SimpleEncoding table
Fixes https://github.com/rprichard/winpty/issues/116
2017-05-18 05:04:59 -05:00
Ryan Prichard
c9ce3ad1e3 Use Window messages rather than GenerateConsoleCtrlEvent for Ctrl-C
Fixes https://github.com/rprichard/winpty/issues/116
2017-05-17 06:03:17 -05:00
Ryan Prichard
096f978c7a Flush INPUT_RECORD objects prior to SendMessage/GenerateConsoleCtrlEvent
Fixes https://github.com/rprichard/winpty/issues/117
2017-05-17 05:29:07 -05:00
Ryan Prichard
faf75f74b7 Bump version to 0.4.4-dev 2017-05-17 02:01:42 -05:00
Ryan Prichard
3e1ab962d5 Version 0.4.3 2017-05-17 01:47:20 -05:00
Ryan Prichard
e75bd9cbdd Explicitly use the Unicode function WriteConsoleInputW 2017-05-17 01:47:19 -05:00
Ryan Prichard
6765a41c5b Change the modified-Home/End escape sequence from H/F to 1/4
For unmodified Home/End, winpty instead sends WM_KEYDOWN / WM_KEYUP
messages to the console window so that the console can decide what escape
to use based on the VT terminal "alternate mode".

Fixes https://github.com/rprichard/winpty/issues/114
2017-04-27 21:17:03 -07:00
Ryan Prichard
5814705546 Update release notes 2017-04-24 00:47:14 -07:00
Ryan Prichard
b4db322010 Stop using fonts smaller than 5px in height
The console must decide whether characters are half-vs-full width, and it
seems to make poorer decisions with fonts that are atypically small.  The
console properties dialog doesn't allow fonts smaller than 5px.

For example, (U+2026 HORIZONTAL ELLIPSIS) is half-width in Lucida Console
and Consolas, but with Lucida Console, 2px or 4px height, the console
thinks it's full-width.

Disallowing smaller fonts will force winpty to use larger windows, which
makes it more likely to hit the maximum window size constraint.

See https://github.com/Microsoft/vscode/issues/19665.
2017-04-24 00:38:05 -07:00
Ryan Prichard
af4bc8b78a Update release notes 2017-04-22 23:51:06 -07:00
Ryan Prichard
5b99238fca Don't show the cursor if it is outside the console window
Fixes https://github.com/rprichard/winpty/issues/113
2017-04-22 23:41:22 -07:00
Ryan Prichard
bae43674b1 Update release notes 2017-04-22 22:09:36 -07:00
Ryan Prichard
ac33b66172 Direct mode: invalidate m_bufferData on resize
Also, for efficieny in direct mode, only output the lines that have
changed, rather than reprinting all of the lines after the first changed
line.

Fixes https://github.com/rprichard/winpty/issues/112
2017-04-22 22:05:16 -07:00
Ryan Prichard
d636a1de86 Synthesize a WINDOW_BUFFER_SIZE_EVENT event after winpty is resized
Fixes https://github.com/rprichard/winpty/issues/110.
2017-04-22 14:57:35 -07:00
Ryan Prichard
838d959579 Update the release notes 2017-04-22 03:11:58 -05:00
Ryan Prichard
201d8aeb3a Fix handling of AltGr keys
* Respect the result of VkKeyScan when it returns multiple modifiers.  I
   *think* the old if-else behavior was a typo?

 * Refactor the code to restrict the scope of the Alt-implies-NUL behavior.
   On my US keyboard, if I press either Alt-P or Alt-Ctrl-P, the console
   reports a VirtualKey of 'P', and a NUL codepoint for KeyUp.  For KeyDown,
   though, Alt-P reports a 'P' codepoint, but Alt-Ctrl-P reports a NUL
   codepoint.  winpty previously implemented this logic in appendKeyPress, but
   now it's factored out.

   If the terminal sends us U+20AC (EURO SIGN), VkKeyScan decides that it must
   have been typed using both Ctrl and Alt.  (Pressing Ctrl-LeftAlt-E on my US
   keyboard produces a Euro, as does RightAlt-E; AltGr/RightAlt seems to imply
   Ctrl.)  If the Alt keypress only comes from VkKeyScan, then continue using
   the proper codepoint.

 * Avoid changing the VT/reencoding behavior.  In appendUtf8Char, pass the
   unadjusted codepoint and Alt-escape to appendKeyPress, which will pass it
   to reencodeEscapedKeyPress if the console is using VT input.

Fixes https://github.com/rprichard/winpty/issues/109
2017-04-22 03:11:58 -05:00
Ryan Prichard
6360ec6a5b Add test case demonstrating a Win10 15048 ReadConsoleOutput bug 2017-03-16 20:57:35 -05:00
Ryan Prichard
47e7e005e8 Change program execution: resolve symlinks and do PATH search explicitly
Fixes https://github.com/rprichard/winpty/issues/81
Fixes https://github.com/rprichard/winpty/issues/98
2017-02-01 20:48:43 -06:00
Ryan Prichard
75301a2703 Stop building the MSYS package
See https://github.com/rprichard/winpty/issues/97
2017-02-01 17:53:21 -06:00
Ryan Prichard
9f900cc152 Debug output: when virtualkey is 0, print 0 rather than 0x0 2017-01-31 22:06:06 -06:00
Ryan Prichard
ed9d5ad158 Use hex for scan code --show-input debug output 2017-01-31 22:05:24 -06:00
Ryan Prichard
bf98ff3c20 Add a test program that dumps console input via _getch. 2017-01-31 22:03:52 -06:00
Ryan Prichard
84016539f3 Set the ENHANCED_KEY key state for these keys:
* Arrow keys (up, down, left, right)
 * The block of 6 keys (insert, delete, home, end, pgup, pgdn)

This change fixes arrow keys in the Ruby REPL (irb)

Fixes https://github.com/rprichard/winpty/issues/99
2017-01-31 21:53:26 -06:00
Ryan Prichard
db874f4a70 Bump version 2017-01-18 05:35:39 -06:00
Ryan Prichard
410d634987 Set version to 0.4.2 2017-01-18 05:02:24 -06:00
Ryan Prichard
a703349883 Put the commit hash into the msvc binary package 2017-01-18 04:57:11 -06:00
Ryan Prichard
9fcad3a144 ship: make it easier to spot-check packages by leaving package dirs around 2017-01-18 04:55:55 -06:00
Ryan Prichard
1ad7e4eed8 Release notes for 0.4.2 2017-01-18 02:42:52 -06:00
Ryan Prichard
a674301a4b Fix a build problem with MSYS2 by adding a missing header 2017-01-18 01:29:39 -06:00
Ryan Prichard
eed0b3bd71 Get the code compiling with the latest MinGW.org toolchain.
(MinGW-w64 works already -- only the old MinGW toolchain had trouble.)

 * We need to build the test case with -std=g++11 to provide vsnprintf,
   even though vsnprintf is in C99 and is provided by both MSVC and
   MinGW-w64.

 * The old MinGW provides a non-conforming swprintf.
2017-01-18 01:25:10 -06:00
Ryan Prichard
67a34b6c03 Build system: redo how version number tracking works
* Remove the "version suffix" and BUILD_INFO.txt mechanisms, which I
   believe no one uses except winpty itself.  Instead, a suffix can be
   added in the VERSION.txt file.

 * Instead of passing the version and commit info as a preprocessor macro
   when building every C++ file, write the info into a GenVersion.h
   header that is only included by WinptyVersion.cc.

 * Instead of writing a BUILD_INFO.txt in ship.py, pass COMMIT_HASH=<hash>
   to make.

These changes accomplish two things:

 * People who build a tag from source won't see a "<ver>-dev" suffix
   anymore.

 * Changing the version or the commit will correctly rebuild the version
   object files (and only those object files).  It will also relink every
   binary.

Fixes https://github.com/rprichard/winpty/issues/72
2017-01-17 23:39:14 -06:00
Ryan Prichard
643e14894a Remove an obsolete comment. 2017-01-17 21:50:34 -06:00
Ryan Prichard
36d6a4d82e Support the COMMON_LVB_REVERSE_VIDEO and COMMON_LVB_UNDERSCORE flags
These flags are used with WSL (Bash on Windows) and are commonly used.
(e.g. a pager status bar, man pages, etc.)

The most difficult part of this commit is figuring out when Windows
respects the flags versus ignores them.  Apparently it deliberately
ignores them for backwards-compatibility with some programs.
2017-01-15 06:36:18 -06:00
Ryan Prichard
40d5b72c43 Pass certain keys to the console using windows messages.
If the terminal is placed in an alternate mode using DECCKM, then we
need to generate different input escape sequences for WSL (and other
programs using ENABLE_VIRTUAL_TERMINAL_INPUT).  AFAIK, there is no way
for winpty to detect whether an alternate mode is enabled, but it only
seems to affect a small number of keys, so send those keys as window
messages.

We don't send *all* keys as window messages, because the console may try
to interpret some of them.  It doesn't interpret the arrow keys and
Home/End, AFAICT, and those seem to be the only keys affected by DECCKM.
(The console's line-input mode *does* care about these navigation keys,
but in that case, WriteConsoleInput and window messages work equally well.)

DECCKM only seems to affect the keys when there is no modifier key.

I believe that by using SendMessage, winpty-agent will block until the
keys are appended to the console input buffer.  I'm not sure how to verify
it.  If this *weren't* the case, there could be a danger of key input
being transposed.  It seems unlikely to be an issue.

Fixes https://github.com/rprichard/winpty/issues/90
2017-01-15 04:20:05 -06:00
Ryan Prichard
27dfaaefb6 Bump version to 0.4.2 2017-01-03 23:08:15 -06:00
Ryan Prichard
ff8e67d313 Update release notes for 0.4.1. 2017-01-03 22:16:54 -06:00
Ryan Prichard
a850639a87 Fix various MSVC 2015 /W3 warnings
* The compiler doesn't like implicit conversions to bool.
   (it emits a "performance warning")

 * It also doesn't like an unused argument in an exception catch block.
2017-01-03 20:51:29 -06:00
Ryan Prichard
9f0d7e6b4e Add an assertion check in readAll
If the assertion could fail somehow, we could have memory unsafety.
2017-01-03 20:18:29 -06:00
Ryan Prichard
91fce49ab1 Respect the pos parameter in WriteBuffer::replaceRawData
This omission didn't actually break anything, because `pos` is always
0.
2016-10-19 23:05:37 -05:00
Ryan Prichard
68c0c140b2 Fix a read-from-end-of-buffer issue
Fixes https://github.com/rprichard/winpty/issues/94
2016-10-19 23:04:45 -05:00
Ryan Prichard
d02e42d815 Largely cosmetic: avoid a 0-size WriteConsoleInput call 2016-08-14 00:48:55 -05:00
Ryan Prichard
d9935eb21f Improve Ctrl-D detection for winpty-agent --show-input test mode. 2016-08-13 22:59:34 -05:00
Ryan Prichard
4a2ff5da03 Add new test programs: ConinMode, ConoutMode, WriteConsole 2016-08-13 22:12:18 -05:00
Ryan Prichard
6c994460a3 Stylistic change: Add C++11 override keywords everywhere possible.
GCC 5.1 added a -Wsuggest-override option that reports missing `override`
keywords, which I used to prepare this commit.  I'm not turning the option
on yet, though, because my Cygwin environment is still using GCC 4.9.
2016-07-24 00:21:10 -05:00
Ryan Prichard
30d59523ed Stylistic change: put a space between a macro and an opening double-quotes
Initially, some of this code omitted a space between a closing
double-quotes and a macro, which broke with C++11:

    m_output.write(CSI"0m"CSI"1;1H"CSI"2J");

To make it work with C++11, it changed to:

    m_output.write(CSI"0m" CSI"1;1H" CSI"2J");

Now I'm fully separating them:

    m_output.write(CSI "0m" CSI "1;1H" CSI "2J");

I don't expect C++ to change again, breaking my code, but it seems prudent
to avoid that possibility.

I searched the codebase for matches using this Sublime/Perl regex:

    ^([^"'\n]*("([^\\"\n]|\\.)*"|'([^\\'\n]|\\.)*'))*[^"'\n]*(?<!^)(?<!\bL)(?<=[A-Za-z0-9_])("([^\\"\n]|\\.)*"|'([^\\'\n]|\\.)*')
2016-07-23 23:49:45 -05:00
Ryan Prichard
18d71a73f2 Bump the version to 0.4.1 2016-06-28 23:54:45 -05:00
Ryan Prichard
c66253e782 Update release notes for 0.4.0. 2016-06-28 22:37:09 -05:00
Ryan Prichard
a0e3f337c0 Add a misc/IdentifyConsoleWindow.ps1 test script
It determines:
 - whether the program has a console
 - whether that console has a window
 - whether that window is on the current window station

It's motivated by the IntelliJ clipboard/winpty bug.
2016-06-20 00:15:53 -05:00
Ryan Prichard
fb8fb49c46 Update API documentation 2016-06-15 14:03:34 -05:00
Ryan Prichard
d7290359d5 Minor tweaks in ship/make_msvc_package.py
- Fix a typo in the 7-Zip default paths.

 - Change env["Path"] to env["PATH"].  Python is case-sensitive, and "Path"
   isn't working.  Windows itself seems to prefer "Path".  I'm guessing
   Python is converting "Path" to "PATH" for platform interoperability.
2016-06-14 05:38:09 -05:00
Ryan Prichard
c6603f72f5 Replace an ASSERT(false) with an exception
This code path only occurs if the start-process reply packet has an
unexpected value in it, so it should never occur.  If it somehow did,
though, it would be inconsistent (and poor behavior) to crash the process
using libwinpty.
2016-06-14 02:59:52 -05:00
Ryan Prichard
ac99610234 Put AgentDesktop into an anonymous namespace 2016-06-14 02:59:52 -05:00
Ryan Prichard
86d47df74b On RPC I/O failure, close the control pipe (which kills the agent)
- Also: ensure that the duplication of the process/thread handles can't
   leak a handle.
2016-06-14 02:59:33 -05:00
Ryan Prichard
fab4f7bb62 Code cleanup: remove a bunch of obsolete TODO comments 2016-06-14 01:07:32 -05:00
Ryan Prichard
b542453ce6 Code cleanup: propagate the LastError info more explicitly 2016-06-14 00:42:23 -05:00
Ryan Prichard
37624c05f4 Add a tea-ci badge to the README 2016-06-14 00:03:47 -05:00
Ryan Prichard
f872ded72c DebugClient improvements: preserve LastError and add TRACE() macro
TRACE() is like trace() but only evaluates its arguments when tracing is
enabled.

In various places on the default code path, where arguments are expensive
to evaluate, switch trace() to TRACE().
2016-06-13 23:50:38 -05:00
Ryan Prichard
913e8cbc71 Merge pull request #83 from fracting/tea-ci
Continuous Integration: Add build configure for Tea CI.
2016-06-13 23:04:26 -05:00
Qian Hong
22b42e29ee Continuous Integration: Add build configure for Tea CI. 2016-06-14 11:50:59 +08:00
Ryan Prichard
ac5d8e95e7 GetConsolePos: also print the cursor position 2016-06-10 01:15:22 -05:00
Ryan Prichard
e7c63efbbd Always use the default attribute (ltgray-on-black) to clear lines
I'm not sure this actually *fixes* anything, but it arguably makes more
sense than using the current color, which is not guaranteed to match the
prevailing background color.

Aside: for scrolling output, console apps generally don't try to change
the background color, and it doesn't work well, even in Unix.

Fixes https://github.com/rprichard/winpty/issues/52
2016-06-09 03:53:16 -05:00
Ryan Prichard
3388c5449f Make width/height limits consistent and cap out-of-bounds values
When resizing, ensure that the replacement sync marker does not become
visible if the buffer height is increased by more than the marker's margin
(currently 200 rows).
2016-06-09 03:22:36 -05:00
Ryan Prichard
fa6ba9c725 Windows 10 new console: try to avoid freezing the console while scraping
Fixes https://github.com/rprichard/winpty/issues/53
2016-06-09 02:34:17 -05:00
Ryan Prichard
b8c2c41eb7 Pass keyboard escapes to the WSL bash.exe wrapper
The wrapper enables the undocumented mode flag 0x200 on the CONIN handle,
which directs the Windows console to replace most KeyDown input records
with sequences of plain bytes, which are escape sequences for control and
navigation keys.

This particular solution reencodes escape sequences and looks roughly
similar to what the Windows console generates, with a few differences:
 * For Alt-Backspace, winpty generates ^[ ^?, whereras Windows generates
   ^[ ^H.
 * For Shift/Alt/Ctrl-modified sequences, winpty encodes the mask of
   modifiers into the escape sequences.
 * winpty generates ^[ Z for Shift-Tab, rather than ^I.
 * winpty generates ^[ E for the NumPad "clear/5" key, rather than nothing.

I'm still not sure winpty *ought* to do this reencoding.  I don't think
generating KeyUp events is necessary.  In principle, it might be
needed/useful for dealing with various terminals.  WSL bash.exe always
sets TERM to xterm, and even if changing it is correct w.r.t. input, it's
not necessarily correct for output -- the Windows console system is
expecting to see TERM=xterm output, which it uses to updates it screen
buffer.  (I'm not sure that matters, much, though.  I suppose
TERM=xterm-256color could be a problem?)

Fixes https://github.com/rprichard/winpty/issues/82
2016-06-08 03:36:02 -05:00
Ryan Prichard
6837b929e1 Refactor UTF-8 input handling: use code points rather than UTF-16 units
Discard bytes that don't decode as UTF-8.

Stop calling VkKeyScan on the two halves of a surrogate pair.  It's
impossible for VkKeyScan to return a useful result.
2016-06-07 23:31:02 -05:00
Ryan Prichard
448186dcb1 Stop encoding the surrogate pair region into UTF-8 and UTF-16 2016-06-07 18:42:47 -07:00
Ryan Prichard
6ebbe4e3cf build-pty4j-libpty.bat: abort if VsDevCmd.bat fails (or doesn't exist) 2016-06-07 18:10:48 -07:00
Ryan Prichard
2d54d78e19 Explicitly specify the Unicode version of two APIs 2016-06-07 18:04:40 -07:00
Ryan Prichard
78911a1546 Update the release notes. 2016-06-07 03:39:06 -07:00
Ryan Prichard
e11e229edf detectNewWindows10Console: rewrite the 1x1 special case
Also: if we're on Win7 or earlier, skip this check.  We'd like to skip
on Win8 and 8.1, too, but doing a version check for those versions is
complicated (e.g. requires a correct manifest or hacks).
2016-06-07 02:02:29 -07:00
Ryan Prichard
572759519b Accommodate very narrow consoles better
If SetConsoleScreenBufferSize fails with a width under 20, keep trying
with wider widths until the API succeeds.
2016-06-07 00:40:25 -07:00
Ryan Prichard
85d737795b Initialize FontFamily (always to 0x36) for SetCurrentConsoleFontEx
On Windows 10 build 14342, with the new console, the Japanese system
locale, and the 437 code page, calling SetCurrentConsoleFontEx to set
the Consolas font breaks if FontFamily is 0, but it works if FontFamily
is 0x36.

See misc/Font-Report-June2016/Windows10SetFontBugginess.txt.
2016-06-07 00:08:26 -07:00
Ryan Prichard
cda0b02660 Adjust the font size according to the number of columns
Additionally, if it is impossible to fit the entire screen buffer on the
current monitor, detect this condition uising the
GetLargestConsoleWindowSize API and make the console window as large as
possible.  This is suboptimal for full-screen console programs (e.g. Far
Manager), but because the scrolling-mode scraper scrapes the screen buffer
rather than the visble window, the smaller-than-desired window frequently
has no noticeable effect.

Revert the use of MARK to freeze the new Windows 10 console.  Use
SELECT_ALL again.

Fixes https://github.com/rprichard/winpty/issues/61
Fixes https://github.com/rprichard/winpty/issues/79
Breaks https://github.com/rprichard/winpty/issues/53 again
2016-06-07 00:08:24 -07:00
Ryan Prichard
b67841911f Improve debugging output in Win32ConsoleBuffer::read
The reads were failing when running wmic.exe under winpty on Windows
10 build 14342.  In that build, after running wmic.exe, the console
buffer was resized to (1500,300), but the window was not moved upward,
so it was hundreds to thousands of lines below the actual buffer.

This is obviously a Windows 10 bug, because even running wmic.exe in a
normal console demonstrates the issue.  Simply fill the console with more
than 300 lines of junk, then try to run wmic.exe, and none of the
wmic.exe output is visible, even after hitting Return repeatedly.
2016-06-07 00:08:18 -07:00
Ryan Prichard
ec4558e0ec Add a test result from Win10/10586 regarding the JP locale + 437 codepage 2016-06-07 00:08:15 -07:00
Ryan Prichard
54d599d4ab Update notes -- correct a typo and comment on the 14342 bug with wmic.exe. 2016-06-06 22:27:35 -07:00
Ryan Prichard
f334c73b34 Make m_syncCounter unsigned to avoid UB on overflow 2016-06-05 23:32:44 -07:00
Ryan Prichard
33fae530ec Add notes about SetCurrentConsoleFontEx. 2016-06-05 23:04:33 -07:00
Ryan Prichard
661094b401 Add font information from various OS versions 2016-06-05 22:01:37 -07:00
Ryan Prichard
8bb13c6849 FontSurvey: automatically classify HHFFFF=GOOD and HHHFFF=OK 2016-06-05 19:23:26 -07:00
Ryan Prichard
111d0c67b1 Add a FontSurvey program for automatically testing 100 font sizes 2016-06-05 19:07:03 -07:00
Ryan Prichard
f7545a8499 Enhancements to misc/SetFont test program 2016-06-05 17:36:23 -07:00
Ryan Prichard
bdf3e0ec4c misc/Utf16Echo: Suppress some warnings 2016-06-05 17:05:41 -07:00
Ryan Prichard
f57ed5bede winpty-agent --show-input: add --escape-input flag
Improve debugging for the case where SetConsoleMode fails.  Apparently
on Windows 7, the call fails, but the 0x200 flag is still enabled.  A
later GetConsoleMode call succeeds, but of course, further calls to
SetConsoleMode will still fail unless they unset 0x200.

Given that the 0x200 flag is undocumented and only respected in preview
releases, I'm hesistant to clear the flag when --escape-input isn't set.

Related to https://github.com/rprichard/winpty/issues/82
2016-06-05 02:32:12 -05:00
Ryan Prichard
33c5687d4a More updates to test programs for font/resizing work 2016-06-05 01:47:05 -05:00
Ryan Prichard
ebefb91e80 Update test programs for font/resizing work
* Rename QueryFont.exe to GetFont.exe -- it's shorted.  Make font table
   output shorter.  Make the program work on XP.

 * Add IsNewConsole.exe

 * Add GetBufferInfo.exe

 * Clear the executable bit on some .cc files
2016-06-04 21:10:19 -05:00
Ryan Prichard
d0ae4cbb28 Add a missing header include to OsModule.h 2016-06-04 21:07:12 -05:00
Ryan Prichard
52abfbb908 Add misc/MoveConsoleWindow test program 2016-06-04 02:48:11 -05:00
Ryan Prichard
3854d04282 Add misc/SetBufferSize.exe and misc/SetWindowRect.exe test programs 2016-06-04 02:35:03 -05:00
Ryan Prichard
0f6de50111 Get a bunch of misc test programs compiling again 2016-06-03 22:17:37 -05:00
Ryan Prichard
71405ba1d4 Revert the requirement that pipes have been opened by winpty_spawn time.
The immediate problem is that the implementation has a race condition.  We
service the control pipe before the other pipes, so we can see the
winpty_spawn RPC request before noticing that the I/O pipes are connected.
(This situation actually happened and caused a pty4j test to fail.)

There are a few ways to fix this problem, such as by adding special calls
to service the I/O pipes in handleStartProcessPacket.

It occurred to me, though, that ensuring that pipes are connected before
calling winpty_spawn might be difficult in some environments that provide
high-level I/O systems.  I'm specifically thinking of nodejs/libuv, where,
IIRC, it was difficult to guarantee that the CreateFile API would be called
synchronously.

It turns out to be easy to relax the restriction, anyway, so just do that.

I also think that CONIN and CONOUT/CONERR are sufficiently different that
perhaps CONIN should have been exempted.
2016-06-02 01:52:46 -05:00
Ryan Prichard
139a4dfe53 Add WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN to guard against leaking agents 2016-06-02 00:52:59 -05:00
Ryan Prichard
e0cbe70600 Add a script specifically for building pty4j's libpty hierarchy. 2016-06-01 23:31:48 -05:00
Ryan Prichard
7c9a7c8745 Factor some code out of make_msvc_package.py into a simpler vcbuild.bat 2016-06-01 23:09:36 -05:00
Ryan Prichard
ce5c95cd4f make_msvc_package.py: clear the "-dev" version suffix
With MSVC, converting an empty macro value with STRINGIFY doesn't seem to
work.  (Maybe it's producing nothing rather than the blank literal, ""?)
Work around it by allowing __none__ as a substitute for blank.
2016-06-01 21:42:36 -05:00
Ryan Prichard
4130c88619 Also build for 64-bit XP.
I verified that the normal binaries did not work on a Windows Server 2003
R2 64-bit VM from AWS EC2.
2016-06-01 20:42:49 -05:00
Ryan Prichard
3539b806e9 Build/package winpty for embedding using gyp and MSVC 2016-06-01 20:01:37 -05:00
Ryan Prichard
bd58e14b17 Check in a forgotten file: misc/EnableExtendedFlags.txt 2016-06-01 19:48:11 -05:00
Ryan Prichard
24a1548ab7 Fix trivial test: open the CONIN pipe too
Also: close the I/O handles
2016-06-01 17:57:39 -05:00
Ryan Prichard
f9046dac82 Remove the redundant "-data" from the I/O pipe names
e.g. replace

\\.\pipe\winpty-data-conin-7752-1-1d1bbc851f1b21e-03b22efd16c217582b421ec555292c69

with

\\.\pipe\winpty-conin-7752-1-1d1bbc851f1b21e-03b22efd16c217582b421ec555292c69
2016-06-01 00:43:21 -05:00
Ryan Prichard
8e4810c5e1 Require that all I/O pipes are connected before calling winpty_spawn. 2016-06-01 00:40:35 -05:00
Ryan Prichard
a6fa61c17d Zero-initialize the EXPLICIT_ACCESSW objects.
This may have fixed a segfault in winpty_open.

Fixes https://github.com/rprichard/winpty/issues/80
2016-05-31 19:34:57 -05:00
Ryan Prichard
87436ca8ae Improvement to the ship/ship.py script
* Reorder ship targets to prefer MSYS, then MSYS2, then Cygwin

   The MSYS build is much more likely to fail, both because its Cygwin
   environment is poor (e.g. no C++11, no std::wstring) and because its
   MinGW environment is poor (e.g. missing/broken SDDL APIs)

 * Use PowerShell instead of the pefile module, because it's much faster,
   even with the extra overhead of starting PowerShell.

 * For MSYS, instead of disabling parallel make, use mingw32-make.exe.
   Apparently, with this change, it's necessary to use forward slashes when
   passing the PREFIX to make, so change that as well.
2016-05-30 15:09:28 -05:00
Ryan Prichard
bfa441f30c Only activate the terminal's mouse mode if ENABLE_EXTENDED_FLAGS is set
Previously, starting any Cygwin program (e.g. bash.exe, python2.7.exe)
would activate the terminal's mouse mode.

Move the ENABLE_EXTENDED_FLAGS note into a separate file.
2016-05-30 05:43:56 -05:00
Ryan Prichard
5974ed1895 Disable QuickEdit mode with WINPTY_MOUSE_MODE_NONE
There's really no reason to leave the mode ON, and disabling it makes
it easier to make mouse input work.  WINPTY_MOUSE_MODE_NONE doesn't mean,
"stop mouse input"; it means, "the agent will not configure your
terminal's mouse output".
2016-05-30 05:00:15 -05:00
Ryan Prichard
4815d6f9f2 Add a missing header to BackgroundDesktop.cc 2016-05-30 04:19:40 -05:00
Ryan Prichard
e60ef0101a Change the default way winpty enters the terminal's mouse mode.
By default, winpty now puts a new console into QuickEdit mode.  If the
agents detects that the console has disabled QuickEdit and enabled
ENABLE_MOUSE_INPUT, then it puts the terminal into mouse mode.

This convention doesn't work with all Windows programs -- it does work
with Far Manager and "winpty-agent.exe --show-input --with-mouse".  The
practice is described in one of Raymond Chen's posts[1].

For other programs, winpty.exe still has a --mouse option, and I wrote a
script in misc/ConinMode.ps1 that controls the QuickEdit (and InsertMode)
flags from the command-line, which is useful, because the console
properties dialog is inaccessible from within winpty.

[1] https://blogs.msdn.microsoft.com/oldnewthing/20130506-00/?p=4453
2016-05-30 04:10:49 -05:00
Ryan Prichard
cf39c31ff6 Create a PowerShell script for controlling the console input mode. 2016-05-30 02:18:13 -05:00
Ryan Prichard
8015db36e0 Implement a special agent invocation for creating background desktops
Calling SetProcessWindowStation in winpty_open isn't thread-safe -- another
thread may be doing something with the window station.  The call also
appears to interfere with the clipboard, which has affected IntelliJ.

A previous commit disabled the use of the background desktop for Windows 7
and up, where it seems unnecessary.  This commit fixes the issue for XP
and Vista by spawning the agent twice in winpty_open:
 1. winpty_open first spawns an agent with no attached console, which
    creates the background desktop and sends its name back on the control
    pipe.
 2. Then it spawns the primary agent invocation as usual.
 3. Once the primary agent's control pipe is connected, winty_open allows
    the first agent to exit.

Fixes https://github.com/rprichard/winpty/issues/58
2016-05-29 00:20:03 -05:00
Ryan Prichard
75ad04155f Fix PendingIo: CancelIo should be passed the HANDLE, not the OVERLAPPED 2016-05-28 22:18:13 -05:00
Ryan Prichard
982e8051a5 Reduce redundant terminal output and make plain+color flags work 2016-05-27 21:36:08 -05:00
Ryan Prichard
d1b9522444 Get misc/SetCursorPos.exe compiling again 2016-05-27 20:19:49 -05:00
Ryan Prichard
d4856b26a8 Fix memory unsafety bug 2016-05-27 02:19:15 -05:00
Ryan Prichard
222ecb9f44 Add undocumented -X<flag> switches to winpty.exe for testing
-Xallow-non-tty:    allow stdin/stdout to not be ttys
-Xconerr:           test the new CONERR mode (connect it to STDERR_FILENO)
-Xplain:            enter plain, unescaped, mode
-Xcolor:            force generation of color escapes, even with -Xplain

Also: with "winpty.exe --showkey", stop trying to put stdout into raw
terminal mode, and ignore whether it is a tty.
2016-05-26 21:34:20 -05:00
Ryan Prichard
5b57d37bdc Simplify the API: remove error params from 2 winpty_config_set_XXX setters 2016-05-26 21:29:11 -05:00
Ryan Prichard
ccb11afd62 Split out Scraper class from Agent; add a "CONERR" mode
If the WINPTY_FLAG_CONERR flag is specified when starting the agent, the
agent creates a separate, inactive console buffer to use for collecting
error output.  The buffer is passed to children using
STARTUPINFO.hStdError.  The agent scrapes from both the initial STDOUT
screen buffer and the new error buffer using two Scraper objects, two
Terminal objects, and two NamedPipe objects.

Clients connect to the CONERR pipe just as they would connect to the CONOUT
pipe.  There is a winpty_conerr_name function for querying the CONERR
pipe's name.

Console frozenness is a property of the entire console, rather than a
screen buffer, so it is consolidated into the Win32Console class.  During a
typical output poll, the console is frozen, then both buffers are scraped,
then the console is unfrozen.

Related: previously CONOUT$ was reopening at each poll timeout.  Now, the
buffer is only open for the duration is is needed.  (i.e. It is closed at
the end of the resizing/scraping operation.)  This new behavior might be
more correct in scenarios where programs change the active screen buffer.
If a program activates its own screen buffer, then exits, the screen buffer
is destroyed because no program references it.  When it is destroyed, a
different buffer is activated.  By opening CONOUT$, winpty can accidentally
prevent a screen buffer from being destroyed, at least temporarily.
2016-05-26 21:26:58 -05:00
Ryan Prichard
f453c9ca1d Remove wstringToUtf8String; it's redundant with utf8FromWide. 2016-05-26 20:56:03 -05:00
Ryan Prichard
265fc67ff0 Split Win32Console into Win32Console and Win32ConsoleBuffer 2016-05-26 20:56:02 -05:00
Ryan Prichard
6a253ee5a8 Factor some input handling out of Agent/Win32Console into ConsoleInput 2016-05-26 20:56:02 -05:00
Ryan Prichard
ab26c8b0d0 Remove some dead code 2016-05-26 20:56:01 -05:00
Ryan Prichard
880c00c69e Replace the libwinpty API.
The new API improves upon the old API in a number of ways:

 * The old API provided a single data pipe for input and output, and it
   provided the pipe in the form of a HANDLE opened with
   FILE_FLAG_OVERLAPPED.  Using a bare HANDLE is difficult in higher-level
   languages, and overlapped/asynchronous I/O is hard to get right.
   winpty_close closed the data pipe, which also didn't help things, though
   I think the handle could have been duplicated.

   Using a single handle for input and output complicates shutdown.  When
   the child process exits, the agent scrapes the final output, then closes
   the data pipe once its written.  It's possible that a winpty client will
   first detect the closed pipe when it's writing *input* rather than
   reading output, which seems wrong.  (On the other hand, the agent
   doesn't implement "backpressure" for either input or output (yet), and
   if it did, it's possible that post-exit shutdown should interrupt a
   blocked write into the console input queue.  I need to think about it
   more.)

   The new API splits the data pipe into CONIN and CONOUT pipes, which are
   accessed by filename.  After `winpty_open` returns, the client queries
   pipe names using `winpty_conin_name` and `winpty_conout_name`, then
   opens them using any mechanism, low-level or high-level, blocking or
   overlapped.

 * The old libwinpty handled errors by killing the process.  The new
   libwinpty uses C++ exceptions internally and translates exceptions at
   API boundaries using:

    - a boolean error result (e.g. BOOL, NULL-vs-non-NULL)
    - a potentially heap-allocated winpty_error_t object returned via an
      optional winpty_error_ptr_t parameter.  That parameter can be NULL.
      If it isn't, then an error code and message can be queried from the
      error object.  The winpty_error_t object must be freed with
      winpty_error_free.

 * winpty_start_process is renamed to winpty_spawn.  winpty_open and
   winpty_spawn accept a "config" object which holds parameters.  New
   configuration options can be added without disturbing the source or
   binary API.

 * The winpty_get_exit_code and winpty_get_process_id APIs are removed.
   The winpty_spawn function has an out parameter providing the child's
   process and thread HANDLEs (duplicated from the agent via
   DuplicateHandle).  The winpty client can call GetExitCodeProcess and
   GetProcessId (as well as the WaitForXXX APIs to wait for child exit).
2016-05-26 20:56:01 -05:00
Ryan Prichard
5e9325f7b2 Stylistic changes in agent: s/socket/pipe/, use C++11 language features 2016-05-25 17:50:45 -05:00
Ryan Prichard
215885fc44 Split out libwinpty/AgentLocation.cc from libwinpty/winpty.cc 2016-05-25 17:50:45 -05:00
Ryan Prichard
6d9dda19af Harmonize use of ASSERT over assert in libwinpty 2016-05-25 17:50:44 -05:00
Ryan Prichard
3f42e83207 Ensure that the agent aborts if the console window is NULL
It should guard against a recurrence of
https://youtrack.jetbrains.com/issue/IDEA-156228.
2016-05-25 17:50:44 -05:00
Ryan Prichard
21eaf31141 Reduce the likelihood of collision for some src/shared header guards 2016-05-25 17:50:44 -05:00
Ryan Prichard
97c4253cfb Bump the version 2016-05-25 17:50:43 -05:00
Ryan Prichard
ee20ae4731 Update the release notes for 0.3.0 2016-05-20 18:05:59 -05:00
Ryan Prichard
0648342e42 Convert U+001B (ESCAPE) to a '?' when it appears in a screen buffer cell
Fixes https://github.com/rprichard/winpty/issues/47.
2016-05-20 17:49:35 -05:00
Ryan Prichard
de8f7b512a Add a note about using the correct MinGW-w64 shortcut for MSYS2
Fixes https://github.com/rprichard/winpty/issues/75.
2016-05-20 17:18:37 -05:00
Ryan Prichard
b7cfafcbb9 Improve the error message when winpty.exe can't start its child process
Fixes https://github.com/rprichard/winpty/issues/76.
2016-05-20 17:18:37 -05:00
Ryan Prichard
179d1af900 In winpty.exe, clear the TERM environment variable
Fixes https://github.com/rprichard/winpty/issues/43.
2016-05-20 17:18:37 -05:00
Ryan Prichard
671360a539 Change the initial console title to a single space.
Fixes https://github.com/rprichard/winpty/issues/74
2016-04-21 02:30:22 -07:00
Ryan Prichard
5a65fdc11c Use more sensible executable flags for source files
My motivation at the moment is that I'm trying to share a
git checkout between multiple VMs using VirtualBox's Shared
Folders feature.  git in the guest VM isn't able to see the
executable bits from the host due to the VirtualBox/SMB/CIFS
layer.  Instead, it thinks text files are non-executable,
unless they have a shebang line.  That's a sensible way to
set the flags anyway, so set them like that.

With this commit, there's still one file that isn't handled:
src/shared/GetCommitHash.cmd.  It's still marked executable,
but it lacks a shebang line, so the guest thinks it's
non-executable.  I'm not sure it should be changed.
2016-04-20 21:44:44 -07:00
Ryan Prichard
2e661f15fd Add a test case exploring freeze behavior on an inactive console buffer
So far, I've only tested it on Windows 7.  The test passed on that OS:
creating a screen buffer isn't blocked by selection, but writing to an
inactive buffer *is* blocked, regardless of whether the buffer was created
before or after selection began.  The use of Mark or SelectAll doesn't
affect behavior.
2016-04-19 15:00:39 -07:00
Ryan Prichard
71795edf77 Update the release notes. 2016-04-10 18:14:21 -05:00
Ryan Prichard
6fba69d602 Also recognize ERROR_RESOURCE_DATA_NOT_FOUND for ModuleNotFound. 2016-04-10 17:52:59 -05:00
Ryan Prichard
8a769b19f5 Avoid infinite loop in readFontTable when ConEmu has adopted the console
If we're only examining the table for debugging purposes, then read only
first 1000 fonts and dump them.

If we're reading the font table on XP, then use the undocumented
GetNumberOfConsoleFonts API.  Avoid this API on Vista and up, because it's
undocumented.  It ought to be stable enough on XP.

When ConEmu has adopted the agent console (e.g. there is a ConEmu tab for
the agent's console), then every font in the table will have the same size.
(For now, anyway.  Obviously, the ConEmu author may decide to change the
behavior.)

When `WINPTY_SHOW_CONSOLE=1` is set, and when ConEmu's "Process 'start'"
flag is set, ConEmu will still adopt the winpty-agent console.  It seems
that this case is still broken (on Vista and up), because winpty.dll
notices that its pipe clients have the wrong PID.  I'm not sure what to do
about this.  `WINPTY_SHOW_CONSOLE=1` is strictly intended for debugging
winpty, though, so there's no useful need for ConEmu to adopt the console.
There's no way for me to opt out.  I could disable the security check when
I detect ConEmu, but that's hazardous, and I'm not a fan of the API hooking
anyway.

winpty prints a content-free error, "Error creating winpty." in the above
case.  The error report could be improved to include the traced() error,
"Security check failed: pipe client pid (7492) does not match agent pid (7416)".

Second part of a fix for
https://github.com/rprichard/winpty/issues/70
2016-04-10 17:35:53 -05:00
Ryan Prichard
edc979225b Specify SW_HIDE and avoid creating a background desktop for Win7 and up
The SW_HIDE change fixes part of a ConEmu<->winpty incompatibility.  If
WINPTY_SHOW_CONSOLE=1 is set, and ConEmu's "start cmd" flag is on, then
readFontTable can still run forever.

Avoiding a background desktop would fix a ConEmu<->winpty incompatibility
if not for the previous fix.  It fixes a clipboard issue and
thread-unsafety issue, but only on Win7 and up.  (More work is needed for
XP/Vista.)

See https://github.com/rprichard/winpty/issues/58.
See https://github.com/rprichard/winpty/issues/70.
2016-04-10 17:08:37 -05:00
Ryan Prichard
ef339dcdd1 Short-circuit hasDebugFlag to make it a bit faster in the common case 2016-04-10 17:02:32 -05:00
Ryan Prichard
3e75255e6b Trace winpty version info, move agent trace output as early as possible 2016-04-10 16:22:32 -05:00
Ryan Prichard
c677b8dd0a Dump Windows version and arch information (including ConEmu hook version) 2016-04-10 16:06:56 -05:00
Ryan Prichard
f647952a34 Forbid copy-assignment on WakeupFd and Event. 2016-04-07 18:05:34 -05:00
Ryan Prichard
25076e51a7 Call Unicode versions of Windows APIs explicitly 2016-04-06 23:34:58 -05:00
Ryan Prichard
cf380b39bc Output a line each time a directory is created.
This might be helpful in diagnosing the directory creation logic in the
Makefile, which is obscure.

*Might* be related to https://github.com/rprichard/winpty/issues/71
2016-04-06 18:53:31 -05:00
Ryan Prichard
94da56e041 Amend the README.md: obviously people want to use make install!
When I initially wrote this section, there was no `install` target at all,
and I just ran `build/console.exe` right out of my checkout.  That's
actually very inconvenient, though, for day-to-day use.
2016-04-05 02:50:52 -05:00
Ryan Prichard
d984413c0a Rename the UNIX adapter from console.exe to winpty.exe.
There are three reasons for this change:

 * It's consistent with MSYS2, which already renamed console.exe to
   winpty.exe.

 * winpty.exe is less likely to clash with another program.

 * winpty.exe is easier to search for online.  If someone unfamiliar with
   winpty is asked to run console.exe, they might have a hard time figuring
   out where console.exe comes from.

The old behavior can be restored by passing UNIX_ADAPTER_EXE=console.exe
to `make`, or by simply renaming the binary after-the-fact, or with
`alias`, etc.
2016-04-05 02:43:13 -05:00
Ryan Prichard
bb23f63b34 Install dev files (importlib, headers) and docs, localize PHONY deps 2016-04-05 02:43:12 -05:00
Ryan Prichard
cc8577e3fc Update the changelog 2016-04-04 06:41:42 -05:00
Ryan Prichard
3733682d2f Fixes to the gyp files.
* Harmonize the subsystem of every binary to CONSOLE.  It apparently
   doesn't matter for DLLs, and it's easier if they're all the same.  I
   examined various DLLs on my system, and they're very inconsistent.
   (e.g. kernel32.dll is CONSOLE, but shell32.dll is GUI.)

 * When using a recent XP-targeting toolset (v120_xp or v140_xp), it's
   necessary to explicitly specify the subsystem, so list it in
   configurations.gypi.

 * node.js changed its common.gypi to disable exception handling[1], so
   explicitly turn it back on in winpty.gyp.  Placing the setting in
   winpty.gyp's 'target_defaults' block doesn't seem to override a setting
   in an included file's 'target_defaults' block, so instead enable EH on
   each target.

 * For consistency with other gyp files I see, use quoted integers instead
   of bare integers for settings.  Affects the 'RuntimeLibrary' setting.

 * Provide a documented way in configurations.gypi for setting the
   XP-specific toolsets.

[1] da9eff80a3
2016-04-04 06:22:45 -05:00
Ryan Prichard
d2dd590bbf MSVC SDL compliance: remove sprintf uses from DefaultInputMap.cc
Also: Use ASSERT to ensure that the write pointer is always within range.
2016-04-04 06:22:44 -05:00
Ryan Prichard
441228eaa3 Consolidate GetVersionEx calls and silence the MSVC deprecation warning
MSVC complained that GetVersionEx was deprecated when the /SDL checks were
enabled.

Comment added to WindowsVersion.cc:

Allow use of deprecated functions (i.e. GetVersionEx).  We need to use
GetVersionEx for the old MinGW toolchain and with MSVC when it targets XP.
Having two code paths makes code harder to test, and it's not obvious how
to detect the presence of a new enough SDK.  (Including ntverp.h and
examining VER_PRODUCTBUILD apparently works, but even then, MinGW-w64 and
MSVC seem to use different version numbers.)
2016-04-04 06:22:43 -05:00
Ryan Prichard
ab5f08dfff MSVC SDL compliance: avoid calls to strcpy and wcsncpy 2016-04-04 06:22:43 -05:00
Ryan Prichard
6bea21d48c Use GetCommandLineW/CommandLineToArgvW for winpty-agent.exe's command line
This change avoids the unnecessary round-trip to the ANSI (or OEM?) code
page, and it also allows removing the mbstowcs calls, which simplifies the
code and fixes MSVC /SDL compliance.
2016-04-04 06:22:42 -05:00
Ryan Prichard
3d466694e0 MSVC SDL compliance: Use __pragma(disable:4146) to allow unsigned negation 2016-04-04 06:22:41 -05:00
Ryan Prichard
1bb530f0a1 Specify SECURITY_IDENTIFICATION for the agent->libwinpty pipe too.
This change seems less interesting than the change specifying the flag in
DebugClient.cc.
2016-04-04 06:22:41 -05:00
Ryan Prichard
a9661c8f52 Improve trace() security: restrict debugserver impersonation
In certain situations (e.g. when it has the SeImpersonatePrivilege
privilege), a named pipe server can impersonate a named pipe client and
execute in its security context (e.g. open the client user's files).
There's no reason for a winpty debugserver to need this ability, so
restrict it in the client.

It seems unlikely to me that this commit fixes a genuine security issue,
but it also seems like a decent precaution to take.  Clients using winpty
don't connect to the DebugServer named pipe unless the WINPTY_DEBUG
environment variable is set, and Windows restricts the ability to
impersonate named clients (at least as of XP SP2).
2016-04-04 06:22:35 -05:00
Ryan Prichard
977019785c Pass USE_PCH=0 to both "make all tests" and "make install"
If we don't, then "make install" recompiles the project.
2016-04-03 18:35:57 -05:00
Ryan Prichard
a22f493982 Update README to mention MSVC/gyp and WinXP support 2016-03-30 03:20:55 -05:00
Ryan Prichard
0711d64cce Update RELEASES.md. 2016-03-30 02:59:45 -05:00
Ryan Prichard
61029c3e4d Convert README.rst to README.md. 2016-03-30 02:59:32 -05:00
Ryan Prichard
de9f54da29 Fix https://github.com/rprichard/winpty/issues/67
The str array need an extra element for the NUL terminator.  The previous
code didn't suffer from memory-unsafety, but winpty_snprintf truncated the
last character of the marker, and trying to write NUL to the WinXP console
didn't work.  (I suspect WinXP turned the NUL into a space, so
findSyncMarker couldn't find it later.)
2016-03-30 02:13:02 -05:00
Ryan Prichard
44848914fc winpty-debugserver: add an --everyone flag to let other users log messages
* In general, harmonize the debugserver named pipe with the other named
   pipe instances: don't let remote users log messages and fail if the
   pipe already exists.
2016-03-30 02:06:06 -05:00
Ryan Prichard
825483fa99 Add a GetNamedPipeClientProcessId security check on supported OSs (Vista+)
Also:

 * Removed some obsoleted comments.

 * Factor out the Handle class.  Rename to OwnedHandle.  Merge some
   improvements from libwinpty-rewrite, including fixing move-assignment
   so that it closes the existing handle, if one exists.
2016-03-29 22:52:01 -05:00
Ryan Prichard
383138a0cc Improve named pipe security
* Set the PIPE_REJECT_REMOTE_CLIENTS flag on Vista and up.

 * Set a DACL on named pipes that gives full control to the built-in
   administrators, the local system account, and the current security
   token's owner SID.  This is the same DACL that is used by default,
   except that the default also grants read access to the Everyone group
   and the anonymous account.

 * The createSecurityDescriptorOwnerFullControlEveryoneWrite function is
   not currently used (or tested), but I think I'll use it in the debug
   server to allow collecting trace output from other accounts on the
   machine.  (I think I'll make that behavior optional.)
2016-03-29 21:41:21 -05:00
Ryan Prichard
63fa213594 Fix WinptyException: make the dtor virtual and mark what() noexcept. 2016-03-29 21:41:15 -05:00
Ryan Prichard
8a899bee05 uniqueName: Increase the random string from 12 bytes to 16 bytes 2016-03-29 21:09:36 -05:00
Ryan Prichard
636a14eb55 Remove the unused ReadBuffer::getRawInt32() function. 2016-03-29 21:09:35 -05:00
Ryan Prichard
0c6a0c2a53 Forbid outputting the wrong kind of character type to [W]StringBuilder.
Previously, outputting the wrong character type tended to call one of the
other overloads, such as the int overload.
2016-03-25 04:02:09 -05:00
Ryan Prichard
1a590ce04c Strengthen unique pipe name generation.
* PIDs are recycled, so include the system time.

 * A program running on the same machine could predict the names of pipes
   winpty uses and block winpty, causing a denial-of-service.  I'm not sure
   this is really a concern, but I suppose in principle it is?  Try to
   guard against it by appending random bytes to the pipe name.  The
   CreatePrivateNamespace API looks relevant, but I'm not sure it applies
   to named pipes, and it's Vista only.
2016-03-25 04:02:08 -05:00
Ryan Prichard
87cfa4b83e Refactor mutable CreateProcess argument workarounds
* Rename modifiableWString to vectorWithNulFromString.

 * Create a vectorFromString that omits the NUL terminator.

 * Stop triply-terminated non-empty environment blocks.

 * Stop adding an extra NUL terminator to environment blocks in
   handleStartProcessPacket.  (env.data() didn't necessarily add a
   terminator in C++03, but it does in C++11.)
2016-03-24 22:41:23 -05:00
Ryan Prichard
7baffb85b8 Consistently convert UTF-16 to UTF-8 for trace output.
* Previously, most places in the codebase used "%ls", which relies on the
   CRT to do a wide-to-mbcs conversion, and I think(?) that uses locale
   settings?

 * Do the conversion explicitly using narrowString, which is renamed to
   utf8FromWide and moved to a new file, src/shared/StringUtil.cc.
2016-03-24 22:19:26 -05:00
Ryan Prichard
c2756d8a79 Move the setlocale call to the very top.
This change shouldn't affect the behavior at all, because the code in
between does not do MB<->WC string conversion.
2016-03-24 22:19:25 -05:00
Ryan Prichard
4dddde3562 Switch from std::[w]stringstream/std::cout to [W]StringBuilder/fwrite
Remove all references to the iostream and sstream headers from code in the
src directory.

This change reduces the sizes of winpty.dll and winpty-agent.exe.  For
example:

Cygwin's x86_64-w64-mingw32-g++ 4.9.2 compiler:
 - winpty.dll:       622 KiB to 513 KiB
 - winpty-agent.exe: 742 KiB to 572 KiB

MSVC 2015, using gyp + configurations.gypi targeting x64:
 - winpty.dll:       985 KiB to 545 KiB
 - winpty-agent.exe: 1108 KiB to 642 KiB
2016-03-24 22:19:25 -05:00
Ryan Prichard
ba63e95af2 Add a StringBuilder class that resembles an efficient std::stringstream 2016-03-24 22:19:24 -05:00
Ryan Prichard
9839b8581a Improvements to {Read,Write}Buffer classes and NamedPipe
* Avoid std::stringstream because it's inefficient/bloated.

 * Explicitly handle decode errors, by throwing ReadBuffer::DecodeError.
   For the time being, this exception can only be thrown in the agent, but
   I anticipate it being thrown in the libwinpty code eventually.

 * Catch encoding errors a bit better.

 * Be more precise about the integer types used for length.  e.g.
   Use 64 bits on the wire, but otherwise use size_t/uint64_t where
   appropriate.

 * Replace memcpy with std::copy to make MSVC's /SDL security checks happy.
   It's a somewhat silly/arbitrary change.  I don't think it will affect
   performance, but I haven't benchmarked it.

 * This commit changes the RPC protocol that libwinpty uses to communicate
   with winpty-agent.
2016-03-24 22:19:23 -05:00
Ryan Prichard
51893bad84 Move the toString functions into Coord.h and SmallRect.h.
I don't think these functions are actually called anywhere -- they only
exist for debugging.  Inlining them will probably reduce build times.
2016-03-02 02:56:39 -06:00
Ryan Prichard
6c7eac63b8 Replace c99_[v]snprintf with winpty_[v]snprintf and improve trace() checks
The existing snprintf functions were not C99-conformant:

 - The _vsnprintf* functions do not accept all of the C99 specifiers,
   like %zu for size_t or (on WinXP) %lld for long long.

 - MinGW always provides vsnprintf, but the implementation sometimes just
   calls MSVCRT's _vsnprintf*.  In that case, it won't handle all C99
   specifiers and the return value won't be conformant.

Changes:

 - winpty_snprintf accepts C90(?) specifiers, returns -1 on error
   (including truncation) and should work with the inttypes.h printf
   macros.

 - Enable __attribute__((printf)) on the winpty_[v]snprintf functions and
   also on trace().  Fix a HANDLE(void*) <-> unsigned int mismatch in
   NamedPipe.cc.

 - Switch most sprintf() call sites to winpty_snprintf for improved safety
   guarantees.  (This change gets the code closer to compiling with MSVC's
   /sdl flag, which forbids "unsafe" memory/string functions.)
2016-03-02 02:56:38 -06:00
Ryan Prichard
adc1a5adbf Use precompiled headers when compiling with GCC.
The unix-adapter doesn't use PCH, because MSYS1's compiler doesn't support
them.
2016-02-29 04:33:42 -06:00
Ryan Prichard
6c65edb0e4 Make WinptyAssert.h behave more sensibly, both inside and outside the agent
* In agent: if the console is frozen, then calling abort hangs forever,
   because abort tries to write to stderr, which blocks.  Instead,
   use WM_CLOSE to close the console window.  If that doesn't work after
   waiting a while, then exit.

   We need to do the same thing doing normal agent shutdown, so factor out
   an agentShutdown function.  It's a bit unhappy living in
   WinptyAssert.cc, but it works.

 * Outside agent: trace an assertion failure message, then delegate to the
   ordinary assert() function.  If that's somehow turned off, then call
   abort().
2016-02-29 04:16:05 -06:00
Ryan Prichard
fd270915c1 Compile each target's source files separately from other targets.
The gyp file already worked this way, but now the Makefile does too.

The motivation is to let the shared source files have different error
handling behavior (e.g. assert/ASSERT/stderr-print/abort/exception) in
winpty.dll and the other targets.
2016-02-29 04:16:04 -06:00
Ryan Prichard
bce511725a Add a clean-msvs target for cleaning up after gyp and VisualStudio/msbuild 2016-02-29 04:16:03 -06:00
Ryan Prichard
e390c8775c Try to fix an issue where make fails if a header file disappears
Currently, when a header file is removed (or renamed), there is a stale
dependency file still referring to the header, and GNU make aborts, because
the header is missing.  Attempt to work around this by adding a pattern
rule to generate any header in `src/`.  The rule prints a warning message.

The C source file whose header was missing will be recompiled because a
dependency was rebuilt.  Once it is, the removed header should be removed
from the dependency file.

(Aside: FWIW, the "ninja" build tool knows directly about depfiles, and it
does not have this problem.)
2016-02-29 02:58:30 -06:00
Ryan Prichard
483aeffee6 Makefile: stop reading environ variables and avoid spawning processes
The variables can be specified on the make command-line instead, and the
user could have unrelated variables that happen to have the name winpty
is using.

I verified that MSYS2 is passing UNIX_ADAPTER_EXE and PREFIX on the
command-line.  ship/ship.py already passes PREFIX on the command-line.

Previously, for each object file, the Makefile spawned a subprocess for
each of: echo, mkdir, dirname, and git.  Most of these can be eliminated:

 - echo: The gmake function $(info ...) is mostly good enough -- it
   affects the --dry-run/-n output, which might be undesired.

 - mkdir: We only need to invoke mkdir once per directory.  It's tricky
   to get the optimal behavior, but it's possible to do better than before.

 - dirname: The gmake function $(dir ...) is good enough.

 - git: only needed once per build, to get the commit hash.
2016-02-26 04:21:22 -06:00
Ryan Prichard
97e1bd2661 Enable C++11 mode and re-enable C++ exception handling. 2016-02-26 01:20:31 -06:00
Ryan Prichard
ec3eae8df5 Remove support for the MinGW compiler packaged with Cygwin.
MinGW-w64 is still supported, as is the MinGW compiler distributed by
mingw.org.

Cygwin's MinGW toolchain uses the 4.0-1 mingw-runtime package by
default, which appears to correspond to the "WSL-4" 4.x version of the
MinGW project's mingwrt packages.  (The version numbers in the
/usr/i686-pc-mingw32/sys-root/mingw/include/_mingw.h file are also
consistent with this theory.)

The WSL-4 branch was repudiated by the MinGW project as a mistake [1].
The MinGW project claims no responsibility for Cygwin's MinGW packages
[2].  There were several releases in short succession [3]:
  4.0.3 2013-09-22
  4.0.2 2013-09-15
  4.0.1 2013-09-14
  4.0.0 2013-08-23
There have been no 4.x releases since then, though there have been more
3.x releases (3.21 and 3.21.1).  The FILE_FLAG_FIRST_PIPE_INSTANCE bug
that this project has worked around was fixed in 4.0.3 [4], which was
never propagated to the Cygwin package.

The MinGW packages were apparently orphaned on 2014-09-18 [5] and are
still orphaned [6].

The g++ compiler in this package is still 4.7.3 (released on 2013-04-11),
which is almost three three years old.  While writing the "GenRandom"
module, I hit a bug where a non-static data member initializer of
pointer-to-function-type segfaulted the compiler.  (gcc seemed to have
confused the field with a pure virtual method.)

[1] http://sourceforge.net/p/mingw/bugs/1673/#5260/41ec
[2] http://sourceforge.net/p/mingw/bugs/1673/#2556/35d4
[3] http://sourceforge.net/projects/mingw/files/MinGW/Base/mingwrt/
[4] http://sourceforge.net/p/mingw/bugs/2050/
[5] https://cygwin.com/ml/cygwin/2014-09/msg00289.html
[6] https://cygwin.com/cygwin-pkg-maint
2016-02-26 00:12:24 -06:00
Ryan Prichard
11b3bbf66e Link statically to the MSVC runtime library in configurations.gypi. 2016-02-26 00:12:23 -06:00
Ryan Prichard
6d589b7e14 Bump the version 2016-02-26 00:12:22 -06:00
Ryan Prichard
58fa5f19ae Update the LICENSE copyright year 2016-02-25 21:27:47 -06:00
Ryan Prichard
73ec93d638 Update release notes for 0.2.2. 2016-02-25 21:21:22 -06:00
Ryan Prichard
a4ba2420e7 Split out misc/{buffer-tests,console-handles.md} to a new repo
See https://github.com/rprichard/win32-console-docs
2016-02-24 20:44:58 -06:00
Ryan Prichard
e235706503 Rename font-notes: this file isn't really Markdown. 2016-02-24 19:54:09 -06:00
215 changed files with 12720 additions and 9203 deletions

3
.gitattributes vendored
View File

@ -1,9 +1,11 @@
* text=auto
*.bat text eol=crlf
*.c text
*.cc text
*.gyp text
*.gypi text
*.h text
*.ps1 text eol=crlf
*.rst text
*.sh text
*.txt text
@ -15,4 +17,3 @@ configure text
*.sh eol=lf
configure eol=lf
VERSION.txt eol=lf
shared/GetCommitHash.cmd text eol=crlf

5
.gitignore vendored
View File

@ -2,10 +2,15 @@
*.suo
*.vcxproj
*.vcxproj.filters
*.pyc
winpty.sdf
winpty.opensdf
/config.mk
/build
/build-gyp
/build-libpty
/ship/packages
/ship/tmp
/src/Default
/src/Release
/src/gen

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2011-2015 Ryan Prichard
Copyright (c) 2011-2016 Ryan Prichard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

142
Makefile
View File

@ -20,35 +20,42 @@
# Use make -n to see the actual command-lines make would run.
# The default "make install" prefix is /usr/local. Pass PREFIX=<path> on the
# command-line to override the default.
.SECONDEXPANSION :
.PHONY : default
default : all
USE_PCH ?= 1
PREFIX ?= /usr/local
UNIX_ADAPTER_EXE ?= console.exe
PREFIX := /usr/local
UNIX_ADAPTER_EXE := winpty.exe
MINGW_ENABLE_CXX11_FLAG := -std=c++11
USE_PCH := 1
COMMON_CXXFLAGS :=
UNIX_CXXFLAGS :=
MINGW_CXXFLAGS :=
MINGW_LDFLAGS :=
UNIX_LDFLAGS :=
# Include config.mk but complain if it hasn't been created yet.
ifeq "$(wildcard config.mk)" ""
$(error config.mk does not exist. Please run ./configure)
endif
MINGW_ENABLE_CXX11_FLAG ?= -std=c++11
include config.mk
COMMON_CXXFLAGS := $(COMMON_CXXFLAGS) \
-DWINPTY_VERSION=$(shell cat VERSION.txt | tr -d '\r\n') \
-DWINPTY_VERSION_SUFFIX=$(VERSION_SUFFIX) \
-DWINPTY_COMMIT_HASH=$(COMMIT_HASH) \
COMMON_CXXFLAGS += \
-MMD -Wall \
-DUNICODE \
-D_UNICODE \
-DWINVER=0x0501 \
-D_WIN32_WINNT=0x0501
-D_WIN32_WINNT=0x0501 \
-Ibuild/gen
UNIX_CXXFLAGS := $(UNIX_CXXFLAGS) \
UNIX_CXXFLAGS += \
$(COMMON_CXXFLAGS)
MINGW_CXXFLAGS := $(MINGW_CXXFLAGS) \
MINGW_CXXFLAGS += \
$(COMMON_CXXFLAGS) \
-O2 \
$(MINGW_ENABLE_CXX11_FLAG)
@ -56,67 +63,104 @@ MINGW_CXXFLAGS := $(MINGW_CXXFLAGS) \
MINGW_LDFLAGS += -static -static-libgcc -static-libstdc++
UNIX_LDFLAGS += $(UNIX_LDFLAGS_STATIC)
ifeq "$(USE_PCH)" "1"
MINGW_CXXFLAGS += -include build/mingw/PrecompiledHeader.h
PCH_DEP := build/mingw/PrecompiledHeader.h.gch
else
PCH_DEP :=
endif
build/gen/GenVersion.h : VERSION.txt $(COMMIT_HASH_DEP) | $$(@D)/.mkdir
$(info Updating build/gen/GenVersion.h)
@echo "const char GenVersion_Version[] = \"$(shell cat VERSION.txt | tr -d '\r\n')\";" > build/gen/GenVersion.h
@echo "const char GenVersion_Commit[] = \"$(COMMIT_HASH)\";" >> build/gen/GenVersion.h
build/mingw/PrecompiledHeader.h : src/shared/PrecompiledHeader.h | $$(@D)/.mkdir
$(info Copying $< to $@)
@cp $< $@
build/mingw/PrecompiledHeader.h.gch : build/mingw/PrecompiledHeader.h | $$(@D)/.mkdir
$(info Compiling $<)
@$(MINGW_CXX) $(MINGW_CXXFLAGS) -c -o $@ $<
-include build/mingw/PrecompiledHeader.h.d
define def_unix_target
build/$1/%.o : src/%.cc VERSION.txt
@echo Compiling $$<
@mkdir -p $$(dir $$@)
build/$1/%.o : src/%.cc | $$$$(@D)/.mkdir
$$(info Compiling $$<)
@$$(UNIX_CXX) $$(UNIX_CXXFLAGS) $2 -I src/include -c -o $$@ $$<
endef
ifeq "$(USE_PCH)" "1"
PCH_INCLUDE := -include
else
PCH_INCLUDE :=
endif
define def_mingw_target
ifeq "$$(USE_PCH)" "1"
H_$(1) := build/$1/PrecompiledHeader.h
H_GCH_$(1) := build/$1/PrecompiledHeader.h.gch
$$(H_$(1)) : src/shared/PrecompiledHeader.h
@echo Copying $$< to $$@
@mkdir -p $$(dir $$@)
@cp $$< $$@
$$(H_GCH_$(1)) : $$(H_$(1))
@echo Compiling $$<
@mkdir -p $$(dir $$@)
@$$(MINGW_CXX) $$(MINGW_CXXFLAGS) $2 -c -o $$@ $$<
endif
build/$1/%.o : src/%.cc VERSION.txt $$(H_GCH_$(1))
@echo Compiling $$<
@mkdir -p $$(dir $$@)
@$$(MINGW_CXX) $$(MINGW_CXXFLAGS) $2 $$(PCH_INCLUDE) $$(H_$(1)) -I src/include -c -o $$@ $$<
build/$1/%.o : src/%.cc $$(PCH_DEP) | $$$$(@D)/.mkdir
$$(info Compiling $$<)
@$$(MINGW_CXX) $$(MINGW_CXXFLAGS) $2 -I src/include -c -o $$@ $$<
endef
include src/subdir.mk
.PHONY : all
all : $(ALL_TARGETS)
.PHONY : tests
tests : $(TEST_PROGRAMS)
install : all
.PHONY : install-bin
install-bin : all
mkdir -p $(PREFIX)/bin
install -m 755 -p -s build/$(UNIX_ADAPTER_EXE) $(PREFIX)/bin
install -m 755 -p -s build/winpty.dll $(PREFIX)/bin
install -m 755 -p -s build/winpty-agent.exe $(PREFIX)/bin
.PHONY : install-debugserver
install-debugserver : all
mkdir -p $(PREFIX)/bin
install -m 755 -p -s build/winpty-debugserver.exe $(PREFIX)/bin
.PHONY : install-lib
install-lib : all
mkdir -p $(PREFIX)/lib
install -m 644 -p build/winpty.lib $(PREFIX)/lib
.PHONY : install-doc
install-doc :
mkdir -p $(PREFIX)/share/doc/winpty
install -m 644 -p LICENSE $(PREFIX)/share/doc/winpty
install -m 644 -p README.md $(PREFIX)/share/doc/winpty
install -m 644 -p RELEASES.md $(PREFIX)/share/doc/winpty
.PHONY : install-include
install-include :
mkdir -p $(PREFIX)/include/winpty
install -m 644 -p src/include/winpty.h $(PREFIX)/include/winpty
install -m 644 -p src/include/winpty_constants.h $(PREFIX)/include/winpty
.PHONY : install
install : \
install-bin \
install-debugserver \
install-lib \
install-doc \
install-include
.PHONY : clean
clean :
rm -fr build
clean-msvs :
rm -fr src/Default
rm -f src/*.vcxproj
rm -f src/*.vcxproj.filters
rm -f src/*.sln
.PHONY : clean-msvc
clean-msvc :
rm -fr src/Default src/Release src/.vs src/gen
rm -f src/*.vcxproj src/*.vcxproj.filters src/*.sln src/*.sdf
.PHONY : distclean
distclean : clean
rm -f config.mk
.PHONY : default all tests install clean clean-msvs distclean
.PRECIOUS : %.mkdir
%.mkdir :
$(info Creating directory $(dir $@))
@mkdir -p $(dir $@)
@touch $@
src/%.h :
@echo "Missing header file $@ (stale dependency file?)"

151
README.md Normal file
View File

@ -0,0 +1,151 @@
# winpty
[![Build Status](https://ci.appveyor.com/api/projects/status/69tb9gylsph1ee1x/branch/master?svg=true)](https://ci.appveyor.com/project/rprichard/winpty/branch/master)
winpty is a Windows software package providing an interface similar to a Unix
pty-master for communicating with Windows console programs. The package
consists of a library (libwinpty) and a tool for Cygwin and MSYS for running
Windows console programs in a Cygwin/MSYS pty.
The software works by starting the `winpty-agent.exe` process with a new,
hidden console window, which bridges between the console API and terminal
input/output escape codes. It polls the hidden console's screen buffer for
changes and generates a corresponding stream of output.
The Unix adapter allows running Windows console programs (e.g. CMD, PowerShell,
IronPython, etc.) under `mintty` or Cygwin's `sshd` with
properly-functioning input (e.g. arrow and function keys) and output (e.g. line
buffering). The library could be also useful for writing a non-Cygwin SSH
server.
## Supported Windows versions
winpty runs on Windows XP through Windows 10, including server versions. It
can be compiled into either 32-bit or 64-bit binaries.
## Cygwin/MSYS adapter (`winpty.exe`)
### Prerequisites
You need the following to build winpty:
* A Cygwin or MSYS installation
* GNU make
* A MinGW g++ toolchain capable of compiling C++11 code to build `winpty.dll`
and `winpty-agent.exe`
* A g++ toolchain targeting Cygwin or MSYS to build `winpty.exe`
Winpty requires two g++ toolchains as it is split into two parts. The
`winpty.dll` and `winpty-agent.exe` binaries interface with the native
Windows command prompt window so they are compiled with the native MinGW
toolchain. The `winpty.exe` binary interfaces with the MSYS/Cygwin terminal so
it is compiled with the MSYS/Cygwin toolchain.
MinGW appears to be split into two distributions -- MinGW (creates 32-bit
binaries) and MinGW-w64 (creates both 32-bit and 64-bit binaries). Either
one is generally acceptable.
#### Cygwin packages
The default g++ compiler for Cygwin targets Cygwin itself, but Cygwin also
packages MinGW-w64 compilers. As of this writing, the necessary packages are:
* Either `mingw64-i686-gcc-g++` or `mingw64-x86_64-gcc-g++`. Select the
appropriate compiler for your CPU architecture.
* `gcc-g++`
* `make`
As of this writing (2016-01-23), only the MinGW-w64 compiler is acceptable.
The MinGW compiler (e.g. from the `mingw-gcc-g++` package) is no longer
maintained and is too buggy.
#### MSYS packages
For the original MSYS, use the `mingw-get` tool (MinGW Installation Manager),
and select at least these components:
* `mingw-developer-toolkit`
* `mingw32-base`
* `mingw32-gcc-g++`
* `msys-base`
* `msys-system-builder`
When running `./configure`, make sure that `mingw32-g++` is in your
`PATH`. It will be in the `C:\MinGW\bin` directory.
#### MSYS2 packages
For MSYS2, use `pacman` and install at least these packages:
* `msys/gcc`
* `mingw32/mingw-w64-i686-gcc` or `mingw64/mingw-w64-x86_64-gcc`. Select
the appropriate compiler for your CPU architecture.
* `make`
MSYS2 provides three start menu shortcuts for starting MSYS2:
* MinGW-w64 Win32 Shell
* MinGW-w64 Win64 Shell
* MSYS2 Shell
To build winpty, use the MinGW-w64 {Win32,Win64} shortcut of the architecture
matching MSYS2. These shortcuts will put the g++ compiler from the
`{mingw32,mingw64}/mingw-w64-{i686,x86_64}-gcc` packages into the `PATH`.
Alternatively, instead of installing `mingw32/mingw-w64-i686-gcc` or
`mingw64/mingw-w64-x86_64-gcc`, install the `mingw-w64-cross-gcc` and
`mingw-w64-cross-crt-git` packages. These packages install cross-compilers
into `/opt/bin`, and then any of the three shortcuts will work.
### Building the Unix adapter
In the project directory, run `./configure`, then `make`, then `make install`.
By default, winpty is installed into `/usr/local`. Pass `PREFIX=<path>` to
`make install` to override this default.
### Using the Unix adapter
To run a Windows console program in `mintty` or Cygwin `sshd`, prepend
`winpty` to the command-line:
$ winpty powershell
Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.
PS C:\rprichard\proj\winpty> 10 + 20
30
PS C:\rprichard\proj\winpty> exit
## Embedding winpty / MSVC compilation
See `src/include/winpty.h` for the prototypes of functions exported by
`winpty.dll`.
Only the `winpty.exe` binary uses Cygwin; all the other binaries work without
it and can be compiled with either MinGW or MSVC. To compile using MSVC,
download gyp and run `gyp -I configurations.gypi` in the `src` subdirectory.
This will generate a `winpty.sln` and associated project files. See the
`src/winpty.gyp` and `src/configurations.gypi` files for notes on dealing with
MSVC versions and different architectures.
Compiling winpty with MSVC currently requires MSVC 2013 or newer.
## Debugging winpty
winpty comes with a tool for collecting timestamped debugging output. To use
it:
1. Run `winpty-debugserver.exe` on the same computer as winpty.
2. Set the `WINPTY_DEBUG` environment variable to `trace` for the
`winpty.exe` process and/or the process using `libwinpty.dll`.
winpty also recognizes a `WINPTY_SHOW_CONSOLE` environment variable. Set it
to 1 to prevent winpty from hiding the console window.
## Copyright
This project is distributed under the MIT license (see the `LICENSE` file in
the project root).
By submitting a pull request for this project, you agree to license your
contribution under the MIT license to this project.

View File

@ -1,127 +0,0 @@
======
winpty
======
winpty is a Windows software package providing an interface similar to a Unix
pty-master for communicating with Windows console programs. The package
consists of a library (libwinpty) and a tool for Cygwin and MSYS for running
Windows console programs in a Cygwin/MSYS pty.
The software works by starting the ``winpty-agent.exe`` process with a new,
hidden console window, which bridges between the console API and terminal
input/output escape codes. It polls the hidden console's screen buffer for
changes and generates a corresponding stream of output.
The Unix adapter allows running Windows console programs (e.g. CMD, PowerShell,
IronPython, etc.) under ``mintty`` or Cygwin's ``sshd`` with
properly-functioning input (e.g. arrow and function keys) and output (e.g. line
buffering). The library could be also useful for writing a non-Cygwin SSH
server.
Prerequisites
=============
You need the following to build winpty:
* A Cygwin or MSYS installation
* GNU make
* A MinGW g++ toolchain capable of compiling C++11 code to build ``winpty.dll``
and ``winpty-agent.exe``
* A g++ toolchain targeting Cygwin or MSYS to build ``console.exe``
Winpty requires two g++ toolchains as it is split into two parts. The
``winpty.dll`` and ``winpty-agent.exe`` binaries interface with the native
Windows command prompt window so they are compiled with the native MinGW
toolchain. The console.exe binary interfaces with the MSYS/Cygwin terminal so
it is compiled with the MSYS/Cygwin toolchain.
MinGW appears to be split into two distributions -- MinGW (creates 32-bit
binaries) and MinGW-w64 (creates both 32-bit and 64-bit binaries). Either
one is generally acceptable.
Cygwin packages
---------------
The default g++ compiler for Cygwin targets Cygwin itself, but Cygwin also
packages MinGW-w64 compilers. As of this writing, the necessary packages are:
* Either ``mingw64-i686-gcc-g++`` or ``mingw64-x86_64-gcc-g++``. Select the
appropriate compiler for your CPU architecture.
* ``gcc-g++``
* ``make``
As of this writing (2016-01-23), only the MinGW-w64 compiler is acceptable.
The MinGW compiler (e.g. from the ``mingw-gcc-g++`` package) is no longer
maintained and is too buggy.
MSYS packages
-------------
For the original MSYS, use the ``mingw-get`` tool (MinGW Installation Manager),
and select at least these components:
* ``mingw-developer-toolkit``
* ``mingw32-base``
* ``mingw32-gcc-g++``
* ``msys-base``
* ``msys-system-builder``
When running ``./configure``, make sure that ``mingw32-g++`` is in your
``PATH``. It will be in the ``C:\MinGW\bin`` directory.
MSYS2 packages
--------------
For MSYS2, use ``pacman`` and install at least these packages:
* ``msys/gcc``
* ``mingw32/mingw-w64-i686-gcc`` or ``mingw64/mingw-w64-x86_64-gcc``. Select
the appropriate compiler for your CPU architecture.
* ``make``
Build
=====
In the project directory, run ``./configure``, then ``make``.
This will produce three binaries:
* ``build/winpty.dll``
* ``build/winpty-agent.exe``
* ``build/console.exe``
Using the Unix adapter
======================
To run a Windows console program in ``mintty`` or Cygwin ``sshd``, prepend
``console.exe`` to the command-line::
$ build/console.exe c:/Python27/python.exe
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 10 + 20
30
>>> exit()
$
Debugging winpty
================
winpty comes with a tool for collecting timestamped debugging output. To use
it:
1. Run ``winpty-debugserver.exe`` on the same computer as winpty.
2. Set the ``WINPTY_DEBUG`` environment variable to ``trace`` for the
``console.exe`` process and/or the process using ``libwinpty.dll``.
winpty also recognizes a ``WINPTY_SHOW_CONSOLE`` environment variable. Set it
to 1 to prevent winpty from hiding the console window.
Copyright
=========
This project is distributed under the MIT license (see the ``LICENSE`` file in
the project root).
By submitting a pull request for this project, you agree to license your
contribution under the MIT license to this project.

173
RELEASES.md Executable file → Normal file
View File

@ -1,7 +1,174 @@
# Next version
# Next Version
* Fix a bug that could have generated spurious mouse input records when an
incomplete mouse escape sequence was seen.
Input handling changes:
* Improve Ctrl-C handling with programs that use unprocessed input. (e.g.
Ctrl-C now cancels input with PowerShell on Windows 10.)
[#116](https://github.com/rprichard/winpty/issues/116)
* Fix a theoretical issue with input event ordering.
[#117](https://github.com/rprichard/winpty/issues/117)
* Ctrl/Shift+{Arrow,Home,End} keys now work with IntelliJ.
[#118](https://github.com/rprichard/winpty/issues/118)
# Version 0.4.3 (2017-05-17)
Input handling changes:
* winpty sets `ENHANCED_KEY` for arrow and navigation keys. This fixes an
issue with the Ruby REPL.
[#99](https://github.com/rprichard/winpty/issues/99)
* AltGr keys are handled better now.
[#109](https://github.com/rprichard/winpty/issues/109)
* In `ENABLE_VIRTUAL_TERMINAL_INPUT` mode, when typing Home/End with a
modifier (e.g. Ctrl), winpty now generates an H/F escape sequence like
`^[[1;5F` rather than a 1/4 escape like `^[[4;5~`.
[#114](https://github.com/rprichard/winpty/issues/114)
Resizing and scraping fixes:
* winpty now synthesizes a `WINDOW_BUFFER_SIZE_EVENT` event after resizing
the console to better propagate window size changes to console programs.
In particular, this affects WSL and Cygwin.
[#110](https://github.com/rprichard/winpty/issues/110)
* Better handling of resizing for certain full-screen programs, like
WSL less.
[#112](https://github.com/rprichard/winpty/issues/112)
* Hide the cursor if it's currently outside the console window. This change
fixes an issue with Far Manager.
[#113](https://github.com/rprichard/winpty/issues/113)
* winpty now avoids using console fonts smaller than 5px high to improve
half-vs-full-width character handling. See
https://github.com/Microsoft/vscode/issues/19665.
[b4db322010](https://github.com/rprichard/winpty/commit/b4db322010d2d897e6c496fefc4f0ecc9b84c2f3)
Cygwin/MSYS adapter fix:
* The way the `winpty` Cygwin/MSYS2 adapter searches for the program to
launch changed. It now resolves symlinks and searches the PATH explicitly.
[#81](https://github.com/rprichard/winpty/issues/81)
[#98](https://github.com/rprichard/winpty/issues/98)
This release does not include binaries for the old MSYS1 project anymore.
MSYS2 will continue to be supported. See
https://github.com/rprichard/winpty/issues/97.
# Version 0.4.2 (2017-01-18)
This release improves WSL support (i.e. Bash-on-Windows):
* winpty generates more correct input escape sequences for WSL programs that
enable an alternate input mode using DECCKM. This bug affected arrow keys
and Home/End in WSL programs such as `vim`, `mc`, and `less`.
[#90](https://github.com/rprichard/winpty/issues/90)
* winpty now recognizes the `COMMON_LVB_REVERSE_VIDEO` and
`COMMON_LVB_UNDERSCORE` text attributes. The Windows console uses these
attributes to implement the SGR.4(Underline) and SGR.7(Negative) modes in
its VT handling. This change affects WSL pager status bars, man pages, etc.
The build system no longer has a "version suffix" mechanism, so passing
`VERSION_SUFFIX=<suffix>` to make or `-D VERSION_SUFFIX=<suffix>` to gyp now
has no effect. AFAIK, the mechanism was never used publicly.
[67a34b6c03](https://github.com/rprichard/winpty/commit/67a34b6c03557a5c2e0a2bdd502c2210921d8f3e)
# Version 0.4.1 (2017-01-03)
Bug fixes:
* This version fixes a bug where the `winpty-agent.exe` process could read
past the end of a buffer.
[#94](https://github.com/rprichard/winpty/issues/94)
# Version 0.4.0 (2016-06-28)
The winpty library has a new API that should be easier for embedding.
[880c00c69e](https://github.com/rprichard/winpty/commit/880c00c69eeca73643ddb576f02c5badbec81f56)
User-visible changes:
* winpty now automatically puts the terminal into mouse mode when it detects
that the console has left QuickEdit mode. The `--mouse` option still forces
the terminal into mouse mode. In principle, an option could be added to
suppress terminal mode, but hopefully it won't be necessary. There is a
script in the `misc` subdirectory, `misc/ConinMode.ps1`, that can change
the QuickEdit mode from the command-line.
* winpty now passes keyboard escapes to `bash.exe` in the Windows Subsystem
for Linux.
[#82](https://github.com/rprichard/winpty/issues/82)
Bug fixes:
* By default, `winpty.dll` avoids calling `SetProcessWindowStation` within
the calling process.
[#58](https://github.com/rprichard/winpty/issues/58)
* Fixed an uninitialized memory bug that could have crashed winpty.
[#80](https://github.com/rprichard/winpty/issues/80)
* winpty now works better with very large and very small terminal windows.
It resizes the console font according to the number of columns.
[#61](https://github.com/rprichard/winpty/issues/61)
* winpty no longer uses Mark to freeze the console on Windows 10. The Mark
command could interfere with the cursor position, corrupting the data in
the screen buffer.
[#79](https://github.com/rprichard/winpty/issues/79)
# Version 0.3.0 (2016-05-20)
User-visible changes:
* The UNIX adapter is renamed from `console.exe` to `winpty.exe` to be
consistent with MSYS2. The name `winpty.exe` is less likely to conflict
with another program and is easier to search for online (e.g. for someone
unfamiliar with winpty).
* The UNIX adapter now clears the `TERM` variable.
[#43](https://github.com/rprichard/winpty/issues/43)
* An escape character appearing in a console screen buffer cell is converted
to a '?'.
[#47](https://github.com/rprichard/winpty/issues/47)
Bug fixes:
* A major bug affecting XP users was fixed.
[#67](https://github.com/rprichard/winpty/issues/67)
* Fixed an incompatibility with ConEmu where winpty hung if ConEmu's
"Process 'start'" feature was enabled.
[#70](https://github.com/rprichard/winpty/issues/70)
* Fixed a bug where `cmd.exe` sometimes printed the message,
`Not enough storage is available to process this command.`.
[#74](https://github.com/rprichard/winpty/issues/74)
Many changes internally:
* The codebase is switched from C++03 to C++11 and uses exceptions internally.
No exceptions are thrown across the C APIs defined in `winpty.h`.
* This version drops support for the original MinGW compiler packaged with
Cygwin (`i686-pc-mingw32-g++`). The MinGW-w64 compiler is still supported,
as is the MinGW distributed at mingw.org. Compiling with MSVC now requires
MSVC 2013 or newer. Windows XP is still supported.
[ec3eae8df5](https://github.com/rprichard/winpty/commit/ec3eae8df5bbbb36d7628d168b0815638d122f37)
* Pipe security is improved. winpty works harder to produce unique pipe names
and includes a random component in the name. winpty secures pipes with a
DACL that prevents arbitrary users from connecting to its pipes. winpty now
passes `PIPE_REJECT_REMOTE_CLIENTS` on Vista and up, and it verifies that
the pipe client PID is correct, again on Vista and up. When connecting to a
named pipe, winpty uses the `SECURITY_IDENTIFICATION` flag to restrict
impersonation. Previous versions *should* still be secure.
* `winpty-debugserver.exe` now has an `--everyone` flag that allows capturing
debug output from other users.
* The code now compiles cleanly with MSVC's "Security Development Lifecycle"
(`/SDL`) checks enabled.
# Version 0.2.2 (2016-02-25)
Minor bug fixes and enhancements:
* Fix a bug that generated spurious mouse input records when an incomplete
mouse escape sequence was seen.
* Fix a buffer overflow bug in `winpty-debugserver.exe` affecting messages of
exactly 4096 bytes.
* For MSVC builds, add a `src/configurations.gypi` file that can be included
on the gyp command-line to enable 32-bit and 64-bit builds.
* `winpty-agent --show-input` mode: Flush stdout after each line.
* Makefile builds: generate a `build/winpty.lib` import library to accompany
`build/winpty.dll`.
# Version 0.2.1 (2015-12-19)

2
VERSION.txt Executable file → Normal file
View File

@ -1 +1 @@
0.2.2
0.4.4-dev

16
appveyor.yml Executable file
View File

@ -0,0 +1,16 @@
image: Visual Studio 2015
init:
- C:\msys64\usr\bin\bash --login -c "pacman -S --needed --noconfirm --noprogressbar msys/make msys/tar msys/gcc mingw-w64-cross-toolchain"
- C:\cygwin\setup-x86 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
- C:\cygwin64\setup-x86_64 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
build_script:
- C:\Python27-x64\python.exe ship\ship.py --kind msys2 --arch x64 --syspath C:\msys64
- C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch ia32 --syspath C:\cygwin
- C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch x64 --syspath C:\cygwin64
- C:\Python27-x64\python.exe ship\make_msvc_package.py
artifacts:
- path: ship\packages\*.tar.gz
- path: ship\packages\*.zip

30
configure vendored
View File

@ -149,29 +149,19 @@ echo MINGW_CXX=$MINGW_CXX >> config.mk
if test $IS_MSYS1 = 1; then
echo UNIX_CXXFLAGS += -DWINPTY_TARGET_MSYS1 >> config.mk
# The MSYS1 MinGW compiler has a bug that prevents inclusion of algorithm
# and math.h in normal C++11 mode. The workaround is to enable the g++11
# mode instead. The bug was fixed on 2015-07-31, but as of 2016-01-05, the
# and math.h in normal C++11 mode. The workaround is to enable the gnu++11
# mode instead. The bug was fixed on 2015-07-31, but as of 2016-02-26, the
# fix apparently hasn't been released. See
# http://ehc.ac/p/mingw/bugs/2250/.
echo MINGW_ENABLE_CXX11_FLAG = -std=gnu++11 >> config.mk
echo MINGW_ENABLE_CXX11_FLAG := -std=gnu++11 >> config.mk
fi
# Figure out how to embed build info (e.g. git commit) into the binary.
if test -f BUILD_INFO.txt; then
echo "Build info: source package"
eval $(grep '^VERSION_SUFFIX=' BUILD_INFO.txt | tr -d '\r')
eval $(grep '^COMMIT_HASH=' BUILD_INFO.txt | tr -d '\r')
echo "VERSION_SUFFIX ?= ${VERSION_SUFFIX}" >> config.mk
echo "COMMIT_HASH ?= ${COMMIT_HASH}" >> config.mk
echo "BUILD_INFO_DEP = config.mk" >> config.mk
elif test -d .git && git rev-parse HEAD >&/dev/null; then
echo "Build info: git"
echo 'VERSION_SUFFIX ?= -dev' >> config.mk
echo 'COMMIT_HASH ?= $$(git rev-parse HEAD)' >> config.mk
echo 'BUILD_INFO_DEP = config.mk .git/HEAD' >> config.mk
if test -d .git -a -f .git/HEAD -a -f .git/index && git rev-parse HEAD >&/dev/null; then
echo "Commit info: git"
echo 'COMMIT_HASH = $(shell git rev-parse HEAD)' >> config.mk
echo 'COMMIT_HASH_DEP := config.mk .git/HEAD .git/index' >> config.mk
else
echo "Build info: none"
echo 'VERSION_SUFFIX ?= -dev' >> config.mk
echo 'COMMIT_HASH ?= none' >> config.mk
echo 'BUILD_INFO_DEP = config.mk' >> config.mk
echo "Commit info: none"
echo 'COMMIT_HASH := none' >> config.mk
echo 'COMMIT_HASH_DEP := config.mk' >> config.mk
fi

1
misc/BufferResizeTests.cc Executable file → Normal file
View File

@ -1,7 +1,6 @@
#include <windows.h>
#include <cassert>
#include "../src/shared/DebugClient.cc"
#include "TestUtil.cc"
void dumpInfoToTrace() {

1
misc/ChangeScreenBuffer.cc Executable file → Normal file
View File

@ -7,7 +7,6 @@
#include <io.h>
#include <cassert>
#include "../src/shared/DebugClient.cc"
#include "TestUtil.cc"
int main()

1
misc/ClearConsole.cc Executable file → Normal file
View File

@ -15,7 +15,6 @@
#include <cstdio>
#include <cstdlib>
#include "../src/shared/DebugClient.cc"
#include "TestUtil.cc"
int main(int argc, char *argv[]) {

117
misc/ConinMode.cc Executable file
View File

@ -0,0 +1,117 @@
#include <windows.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
static HANDLE getConin() {
HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
if (conin == INVALID_HANDLE_VALUE) {
fprintf(stderr, "error: cannot get stdin\n");
exit(1);
}
return conin;
}
static DWORD getConsoleMode() {
DWORD mode = 0;
if (!GetConsoleMode(getConin(), &mode)) {
fprintf(stderr, "error: GetConsoleMode failed (is stdin a console?)\n");
exit(1);
}
return mode;
}
static void setConsoleMode(DWORD mode) {
if (!SetConsoleMode(getConin(), mode)) {
fprintf(stderr, "error: SetConsoleMode failed (is stdin a console?)\n");
exit(1);
}
}
static long parseInt(const std::string &s) {
errno = 0;
char *endptr = nullptr;
long result = strtol(s.c_str(), &endptr, 0);
if (errno != 0 || !endptr || *endptr != '\0') {
fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
exit(1);
}
return result;
}
static void usage() {
printf("Usage: ConinMode [verb] [options]\n");
printf("Verbs:\n");
printf(" [info] Dumps info about mode flags.\n");
printf(" get Prints the mode DWORD.\n");
printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
printf(" set VALUE MASK\n");
printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
exit(1);
}
struct {
const char *name;
DWORD value;
} kInputFlags[] = {
"ENABLE_PROCESSED_INPUT", ENABLE_PROCESSED_INPUT, // 0x0001
"ENABLE_LINE_INPUT", ENABLE_LINE_INPUT, // 0x0002
"ENABLE_ECHO_INPUT", ENABLE_ECHO_INPUT, // 0x0004
"ENABLE_WINDOW_INPUT", ENABLE_WINDOW_INPUT, // 0x0008
"ENABLE_MOUSE_INPUT", ENABLE_MOUSE_INPUT, // 0x0010
"ENABLE_INSERT_MODE", ENABLE_INSERT_MODE, // 0x0020
"ENABLE_QUICK_EDIT_MODE", ENABLE_QUICK_EDIT_MODE, // 0x0040
"ENABLE_EXTENDED_FLAGS", ENABLE_EXTENDED_FLAGS, // 0x0080
"ENABLE_VIRTUAL_TERMINAL_INPUT", 0x0200/*ENABLE_VIRTUAL_TERMINAL_INPUT*/, // 0x0200
};
int main(int argc, char *argv[]) {
std::vector<std::string> args;
for (size_t i = 1; i < argc; ++i) {
args.push_back(argv[i]);
}
if (args.empty() || args.size() == 1 && args[0] == "info") {
DWORD mode = getConsoleMode();
printf("mode: 0x%lx\n", mode);
for (const auto &flag : kInputFlags) {
printf("%-29s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
mode &= ~flag.value;
}
for (int i = 0; i < 32; ++i) {
if (mode & (1u << i)) {
printf("Unrecognized flag: %04x\n", (1u << i));
}
}
return 0;
}
const auto verb = args[0];
if (verb == "set") {
if (args.size() == 2) {
const DWORD newMode = parseInt(args[1]);
setConsoleMode(newMode);
} else if (args.size() == 3) {
const DWORD mode = parseInt(args[1]);
const DWORD mask = parseInt(args[2]);
const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
setConsoleMode(newMode);
} else {
usage();
}
} else if (verb == "get") {
if (args.size() != 1) {
usage();
}
printf("0x%lx\n", getConsoleMode());
} else {
usage();
}
return 0;
}

116
misc/ConinMode.ps1 Executable file
View File

@ -0,0 +1,116 @@
#
# PowerShell script for controlling the console QuickEdit and InsertMode flags.
#
# Turn QuickEdit off to interact with mouse-driven console programs.
#
# Usage:
#
# powershell .\ConinMode.ps1 [Options]
#
# Options:
# -QuickEdit [on/off]
# -InsertMode [on/off]
# -Mode [integer]
#
param (
[ValidateSet("on", "off")][string] $QuickEdit,
[ValidateSet("on", "off")][string] $InsertMode,
[int] $Mode
)
$signature = @'
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint GetConsoleMode(
IntPtr hConsoleHandle,
out uint lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetConsoleMode(
IntPtr hConsoleHandle,
uint dwMode);
public const int STD_INPUT_HANDLE = -10;
public const int ENABLE_INSERT_MODE = 0x0020;
public const int ENABLE_QUICK_EDIT_MODE = 0x0040;
public const int ENABLE_EXTENDED_FLAGS = 0x0080;
'@
$WinAPI = Add-Type -MemberDefinition $signature `
-Name WinAPI -Namespace ConinModeScript `
-PassThru
function GetConIn {
$ret = $WinAPI::GetStdHandle($WinAPI::STD_INPUT_HANDLE)
if ($ret -eq -1) {
throw "error: cannot get stdin"
}
return $ret
}
function GetConsoleMode {
$conin = GetConIn
$mode = 0
$ret = $WinAPI::GetConsoleMode($conin, [ref]$mode)
if ($ret -eq 0) {
throw "GetConsoleMode failed (is stdin a console?)"
}
return $mode
}
function SetConsoleMode($mode) {
$conin = GetConIn
$ret = $WinAPI::SetConsoleMode($conin, $mode)
if ($ret -eq 0) {
throw "SetConsoleMode failed (is stdin a console?)"
}
}
$oldMode = GetConsoleMode
$newMode = $oldMode
$doingSomething = $false
if ($PSBoundParameters.ContainsKey("Mode")) {
$newMode = $Mode
$doingSomething = $true
}
if ($QuickEdit + $InsertMode -ne "") {
if (!($newMode -band $WinAPI::ENABLE_EXTENDED_FLAGS)) {
# We can't enable an extended flag without overwriting the existing
# QuickEdit/InsertMode flags. AFAICT, there is no way to query their
# existing values, so at least we can choose sensible defaults.
$newMode = $newMode -bor $WinAPI::ENABLE_EXTENDED_FLAGS
$newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
$newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
$doingSomething = $true
}
}
if ($QuickEdit -eq "on") {
$newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
$doingSomething = $true
} elseif ($QuickEdit -eq "off") {
$newMode = $newMode -band (-bnot $WinAPI::ENABLE_QUICK_EDIT_MODE)
$doingSomething = $true
}
if ($InsertMode -eq "on") {
$newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
$doingSomething = $true
} elseif ($InsertMode -eq "off") {
$newMode = $newMode -band (-bnot $WinAPI::ENABLE_INSERT_MODE)
$doingSomething = $true
}
if ($doingSomething) {
echo "old mode: $oldMode"
SetConsoleMode $newMode
$newMode = GetConsoleMode
echo "new mode: $newMode"
} else {
echo "mode: $oldMode"
}

113
misc/ConoutMode.cc Executable file
View File

@ -0,0 +1,113 @@
#include <windows.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
static HANDLE getConout() {
HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
if (conout == INVALID_HANDLE_VALUE) {
fprintf(stderr, "error: cannot get stdout\n");
exit(1);
}
return conout;
}
static DWORD getConsoleMode() {
DWORD mode = 0;
if (!GetConsoleMode(getConout(), &mode)) {
fprintf(stderr, "error: GetConsoleMode failed (is stdout a console?)\n");
exit(1);
}
return mode;
}
static void setConsoleMode(DWORD mode) {
if (!SetConsoleMode(getConout(), mode)) {
fprintf(stderr, "error: SetConsoleMode failed (is stdout a console?)\n");
exit(1);
}
}
static long parseInt(const std::string &s) {
errno = 0;
char *endptr = nullptr;
long result = strtol(s.c_str(), &endptr, 0);
if (errno != 0 || !endptr || *endptr != '\0') {
fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
exit(1);
}
return result;
}
static void usage() {
printf("Usage: ConoutMode [verb] [options]\n");
printf("Verbs:\n");
printf(" [info] Dumps info about mode flags.\n");
printf(" get Prints the mode DWORD.\n");
printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
printf(" set VALUE MASK\n");
printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
exit(1);
}
struct {
const char *name;
DWORD value;
} kOutputFlags[] = {
"ENABLE_PROCESSED_OUTPUT", ENABLE_PROCESSED_OUTPUT, // 0x0001
"ENABLE_WRAP_AT_EOL_OUTPUT", ENABLE_WRAP_AT_EOL_OUTPUT, // 0x0002
"ENABLE_VIRTUAL_TERMINAL_PROCESSING", 0x0004/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/, // 0x0004
"DISABLE_NEWLINE_AUTO_RETURN", 0x0008/*DISABLE_NEWLINE_AUTO_RETURN*/, // 0x0008
"ENABLE_LVB_GRID_WORLDWIDE", 0x0010/*ENABLE_LVB_GRID_WORLDWIDE*/, //0x0010
};
int main(int argc, char *argv[]) {
std::vector<std::string> args;
for (size_t i = 1; i < argc; ++i) {
args.push_back(argv[i]);
}
if (args.empty() || args.size() == 1 && args[0] == "info") {
DWORD mode = getConsoleMode();
printf("mode: 0x%lx\n", mode);
for (const auto &flag : kOutputFlags) {
printf("%-34s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
mode &= ~flag.value;
}
for (int i = 0; i < 32; ++i) {
if (mode & (1u << i)) {
printf("Unrecognized flag: %04x\n", (1u << i));
}
}
return 0;
}
const auto verb = args[0];
if (verb == "set") {
if (args.size() == 2) {
const DWORD newMode = parseInt(args[1]);
setConsoleMode(newMode);
} else if (args.size() == 3) {
const DWORD mode = parseInt(args[1]);
const DWORD mask = parseInt(args[2]);
const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
setConsoleMode(newMode);
} else {
usage();
}
} else if (verb == "get") {
if (args.size() != 1) {
usage();
}
printf("0x%lx\n", getConsoleMode());
} else {
usage();
}
return 0;
}

0
misc/DebugClient.py Normal file → Executable file
View File

0
misc/DebugServer.py Normal file → Executable file
View File

46
misc/EnableExtendedFlags.txt Executable file
View File

@ -0,0 +1,46 @@
Note regarding ENABLE_EXTENDED_FLAGS (2016-05-30)
There is a complicated interaction between the ENABLE_EXTENDED_FLAGS flag
and the ENABLE_QUICK_EDIT_MODE and ENABLE_INSERT_MODE flags (presumably for
backwards compatibility?). I studied the behavior on Windows 7 and Windows
10, with both the old and new consoles, and I didn't see any differences
between versions. Here's what I seemed to observe:
- The console has three flags internally:
- QuickEdit
- InsertMode
- ExtendedFlags
- SetConsoleMode psuedocode:
void SetConsoleMode(..., DWORD mode) {
ExtendedFlags = (mode & (ENABLE_EXTENDED_FLAGS
| ENABLE_QUICK_EDIT_MODE
| ENABLE_INSERT_MODE )) != 0;
if (ExtendedFlags) {
QuickEdit = (mode & ENABLE_QUICK_EDIT_MODE) != 0;
InsertMode = (mode & ENABLE_INSERT_MODE) != 0;
}
}
- Setting QuickEdit or InsertMode from the properties dialog GUI does not
affect the ExtendedFlags setting -- it simply toggles the one flag.
- GetConsoleMode psuedocode:
GetConsoleMode(..., DWORD *result) {
if (ExtendedFlags) {
*result |= ENABLE_EXTENDED_FLAGS;
if (QuickEdit) { *result |= ENABLE_QUICK_EDIT_MODE; }
if (InsertMode) { *result |= ENABLE_INSERT_MODE; }
}
}
Effectively, the ExtendedFlags flags controls whether the other two flags
are visible/controlled by the user application. If they aren't visible,
though, there is no way for the user application to make them visible,
except by overwriting their values! Calling SetConsoleMode with just
ENABLE_EXTENDED_FLAGS would clear the extended flags we want to read.
Consequently, if a program temporarily alters the QuickEdit flag (e.g. to
enable mouse input), it cannot restore the original values of the QuickEdit
and InsertMode flags, UNLESS every other console program cooperates by
keeping the ExtendedFlags flag set.

View File

@ -0,0 +1,528 @@
==================================
Code Page 437, Consolas font
==================================
Options: -face "Consolas" -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
FontSurvey "-face \"Consolas\" -family 0x36"
Windows 7
---------
Size 1: 1,3 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 1,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 2,5 BAD (HHHHHH)
Size 6: 3,6 BAD (HHHHHH)
Size 7: 3,6 BAD (HHHHHH)
Size 8: 4,8 BAD (HHHHHH)
Size 9: 4,9 BAD (HHHHHH)
Size 10: 5,10 BAD (HHHHHH)
Size 11: 5,11 BAD (HHHHHH)
Size 12: 6,12 BAD (HHHHHH)
Size 13: 6,13 BAD (HHHHHH)
Size 14: 7,14 BAD (HHHHHH)
Size 15: 7,15 BAD (HHHHHH)
Size 16: 8,16 BAD (HHHHHH)
Size 17: 8,17 BAD (HHHHHH)
Size 18: 8,18 BAD (HHHHHH)
Size 19: 9,19 BAD (HHHHHH)
Size 20: 9,20 BAD (HHHHHH)
Size 21: 10,22 BAD (HHHHHH)
Size 22: 10,22 BAD (HHHHHH)
Size 23: 11,23 BAD (HHHHHH)
Size 24: 11,24 BAD (HHHHHH)
Size 25: 12,25 BAD (HHHHHH)
Size 26: 12,26 BAD (HHHHHH)
Size 27: 13,27 BAD (HHHHHH)
Size 28: 13,28 BAD (HHHHHH)
Size 29: 14,29 BAD (HHHHHH)
Size 30: 14,30 BAD (HHHHHH)
Size 31: 15,31 BAD (HHHHHH)
Size 32: 15,32 BAD (HHHHHH)
Size 33: 15,33 BAD (HHHHHH)
Size 34: 16,34 BAD (HHHHHH)
Size 35: 16,36 BAD (HHHHHH)
Size 36: 17,36 BAD (HHHHHH)
Size 37: 17,37 BAD (HHHHHH)
Size 38: 18,38 BAD (HHHHHH)
Size 39: 18,39 BAD (HHHHHH)
Size 40: 19,40 BAD (HHHHHH)
Size 41: 19,41 BAD (HHHHHH)
Size 42: 20,42 BAD (HHHHHH)
Size 43: 20,43 BAD (HHHHHH)
Size 44: 21,44 BAD (HHHHHH)
Size 45: 21,45 BAD (HHHHHH)
Size 46: 22,46 BAD (HHHHHH)
Size 47: 22,47 BAD (HHHHHH)
Size 48: 23,48 BAD (HHHHHH)
Size 49: 23,49 BAD (HHHHHH)
Size 50: 23,50 BAD (HHHHHH)
Size 51: 24,51 BAD (HHHHHH)
Size 52: 24,52 BAD (HHHHHH)
Size 53: 25,53 BAD (HHHHHH)
Size 54: 25,54 BAD (HHHHHH)
Size 55: 26,55 BAD (HHHHHH)
Size 56: 26,56 BAD (HHHHHH)
Size 57: 27,57 BAD (HHHHHH)
Size 58: 27,58 BAD (HHHHHH)
Size 59: 28,59 BAD (HHHHHH)
Size 60: 28,60 BAD (HHHHHH)
Size 61: 29,61 BAD (HHHHHH)
Size 62: 29,62 BAD (HHHHHH)
Size 63: 30,64 BAD (HHHHHH)
Size 64: 30,64 BAD (HHHHHH)
Size 65: 31,65 BAD (HHHHHH)
Size 66: 31,66 BAD (HHHHHH)
Size 67: 31,67 BAD (HHHHHH)
Size 68: 32,68 BAD (HHHHHH)
Size 69: 32,69 BAD (HHHHHH)
Size 70: 33,70 BAD (HHHHHH)
Size 71: 33,71 BAD (HHHHHH)
Size 72: 34,72 BAD (HHHHHH)
Size 73: 34,73 BAD (HHHHHH)
Size 74: 35,74 BAD (HHHHHH)
Size 75: 35,75 BAD (HHHHHH)
Size 76: 36,76 BAD (HHHHHH)
Size 77: 36,77 BAD (HHHHHH)
Size 78: 37,78 BAD (HHHHHH)
Size 79: 37,79 BAD (HHHHHH)
Size 80: 38,80 BAD (HHHHHH)
Size 81: 38,81 BAD (HHHHHH)
Size 82: 39,82 BAD (HHHHHH)
Size 83: 39,83 BAD (HHHHHH)
Size 84: 39,84 BAD (HHHHHH)
Size 85: 40,85 BAD (HHHHHH)
Size 86: 40,86 BAD (HHHHHH)
Size 87: 41,87 BAD (HHHHHH)
Size 88: 41,88 BAD (HHHHHH)
Size 89: 42,89 BAD (HHHHHH)
Size 90: 42,90 BAD (HHHHHH)
Size 91: 43,91 BAD (HHHHHH)
Size 92: 43,92 BAD (HHHHHH)
Size 93: 44,93 BAD (HHHHHH)
Size 94: 44,94 BAD (HHHHHH)
Size 95: 45,95 BAD (HHHHHH)
Size 96: 45,96 BAD (HHHHHH)
Size 97: 46,97 BAD (HHHHHH)
Size 98: 46,98 BAD (HHHHHH)
Size 99: 46,99 BAD (HHHHHH)
Size 100: 47,100 BAD (HHHHHH)
Windows 8
---------
Size 1: 1,3 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 1,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 2,5 BAD (HHHHHH)
Size 6: 3,6 BAD (HHHHHH)
Size 7: 3,6 BAD (HHHHHH)
Size 8: 4,8 BAD (HHHHHH)
Size 9: 4,9 BAD (HHHHHH)
Size 10: 5,10 BAD (HHHHHH)
Size 11: 5,11 BAD (HHHHHH)
Size 12: 6,12 BAD (HHHHHH)
Size 13: 6,13 BAD (HHHHHH)
Size 14: 7,14 BAD (HHHHHH)
Size 15: 7,15 BAD (HHHHHH)
Size 16: 8,16 BAD (HHHHHH)
Size 17: 8,17 BAD (HHHHHH)
Size 18: 8,18 BAD (HHHHHH)
Size 19: 9,19 BAD (HHHHHH)
Size 20: 9,20 BAD (HHHHHH)
Size 21: 10,22 BAD (HHHHHH)
Size 22: 10,22 BAD (HHHHHH)
Size 23: 11,23 BAD (HHHHHH)
Size 24: 11,24 BAD (HHHHHH)
Size 25: 12,25 BAD (HHHHHH)
Size 26: 12,26 BAD (HHHHHH)
Size 27: 13,27 BAD (HHHHHH)
Size 28: 13,28 BAD (HHHHHH)
Size 29: 14,29 BAD (HHHHHH)
Size 30: 14,30 BAD (HHHHHH)
Size 31: 15,31 BAD (HHHHHH)
Size 32: 15,32 BAD (HHHHHH)
Size 33: 15,33 BAD (HHHHHH)
Size 34: 16,34 BAD (HHHHHH)
Size 35: 16,36 BAD (HHHHHH)
Size 36: 17,36 BAD (HHHHHH)
Size 37: 17,37 BAD (HHHHHH)
Size 38: 18,38 BAD (HHHHHH)
Size 39: 18,39 BAD (HHHHHH)
Size 40: 19,40 BAD (HHHHHH)
Size 41: 19,41 BAD (HHHHHH)
Size 42: 20,42 BAD (HHHHHH)
Size 43: 20,43 BAD (HHHHHH)
Size 44: 21,44 BAD (HHHHHH)
Size 45: 21,45 BAD (HHHHHH)
Size 46: 22,46 BAD (HHHHHH)
Size 47: 22,47 BAD (HHHHHH)
Size 48: 23,48 BAD (HHHHHH)
Size 49: 23,49 BAD (HHHHHH)
Size 50: 23,50 BAD (HHHHHH)
Size 51: 24,51 BAD (HHHHHH)
Size 52: 24,52 BAD (HHHHHH)
Size 53: 25,53 BAD (HHHHHH)
Size 54: 25,54 BAD (HHHHHH)
Size 55: 26,55 BAD (HHHHHH)
Size 56: 26,56 BAD (HHHHHH)
Size 57: 27,57 BAD (HHHHHH)
Size 58: 27,58 BAD (HHHHHH)
Size 59: 28,59 BAD (HHHHHH)
Size 60: 28,60 BAD (HHHHHH)
Size 61: 29,61 BAD (HHHHHH)
Size 62: 29,62 BAD (HHHHHH)
Size 63: 30,64 BAD (HHHHHH)
Size 64: 30,64 BAD (HHHHHH)
Size 65: 31,65 BAD (HHHHHH)
Size 66: 31,66 BAD (HHHHHH)
Size 67: 31,67 BAD (HHHHHH)
Size 68: 32,68 BAD (HHHHHH)
Size 69: 32,69 BAD (HHHHHH)
Size 70: 33,70 BAD (HHHHHH)
Size 71: 33,71 BAD (HHHHHH)
Size 72: 34,72 BAD (HHHHHH)
Size 73: 34,73 BAD (HHHHHH)
Size 74: 35,74 BAD (HHHHHH)
Size 75: 35,75 BAD (HHHHHH)
Size 76: 36,76 BAD (HHHHHH)
Size 77: 36,77 BAD (HHHHHH)
Size 78: 37,78 BAD (HHHHHH)
Size 79: 37,79 BAD (HHHHHH)
Size 80: 38,80 BAD (HHHHHH)
Size 81: 38,81 BAD (HHHHHH)
Size 82: 39,82 BAD (HHHHHH)
Size 83: 39,83 BAD (HHHHHH)
Size 84: 39,84 BAD (HHHHHH)
Size 85: 40,85 BAD (HHHHHH)
Size 86: 40,86 BAD (HHHHHH)
Size 87: 41,87 BAD (HHHHHH)
Size 88: 41,88 BAD (HHHHHH)
Size 89: 42,89 BAD (HHHHHH)
Size 90: 42,90 BAD (HHHHHH)
Size 91: 43,91 BAD (HHHHHH)
Size 92: 43,92 BAD (HHHHHH)
Size 93: 44,93 BAD (HHHHHH)
Size 94: 44,94 BAD (HHHHHH)
Size 95: 45,95 BAD (HHHHHH)
Size 96: 45,96 BAD (HHHHHH)
Size 97: 46,97 BAD (HHHHHH)
Size 98: 46,98 BAD (HHHHHH)
Size 99: 46,99 BAD (HHHHHH)
Size 100: 47,100 BAD (HHHHHH)
Windows 8.1
-----------
Size 1: 1,3 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 1,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 2,5 BAD (HHHHHH)
Size 6: 3,6 BAD (HHHHHH)
Size 7: 3,6 BAD (HHHHHH)
Size 8: 4,8 BAD (HHHHHH)
Size 9: 4,9 BAD (HHHHHH)
Size 10: 5,10 BAD (HHHHHH)
Size 11: 5,11 BAD (HHHHHH)
Size 12: 6,12 BAD (HHHHHH)
Size 13: 6,13 BAD (HHHHHH)
Size 14: 7,14 BAD (HHHHHH)
Size 15: 7,15 BAD (HHHHHH)
Size 16: 8,16 BAD (HHHHHH)
Size 17: 8,17 BAD (HHHHHH)
Size 18: 8,18 BAD (HHHHHH)
Size 19: 9,19 BAD (HHHHHH)
Size 20: 9,20 BAD (HHHHHH)
Size 21: 10,22 BAD (HHHHHH)
Size 22: 10,22 BAD (HHHHHH)
Size 23: 11,23 BAD (HHHHHH)
Size 24: 11,24 BAD (HHHHHH)
Size 25: 12,25 BAD (HHHHHH)
Size 26: 12,26 BAD (HHHHHH)
Size 27: 13,27 BAD (HHHHHH)
Size 28: 13,28 BAD (HHHHHH)
Size 29: 14,29 BAD (HHHHHH)
Size 30: 14,30 BAD (HHHHHH)
Size 31: 15,31 BAD (HHHHHH)
Size 32: 15,32 BAD (HHHHHH)
Size 33: 15,33 BAD (HHHHHH)
Size 34: 16,34 BAD (HHHHHH)
Size 35: 16,36 BAD (HHHHHH)
Size 36: 17,36 BAD (HHHHHH)
Size 37: 17,37 BAD (HHHHHH)
Size 38: 18,38 BAD (HHHHHH)
Size 39: 18,39 BAD (HHHHHH)
Size 40: 19,40 BAD (HHHHHH)
Size 41: 19,41 BAD (HHHHHH)
Size 42: 20,42 BAD (HHHHHH)
Size 43: 20,43 BAD (HHHHHH)
Size 44: 21,44 BAD (HHHHHH)
Size 45: 21,45 BAD (HHHHHH)
Size 46: 22,46 BAD (HHHHHH)
Size 47: 22,47 BAD (HHHHHH)
Size 48: 23,48 BAD (HHHHHH)
Size 49: 23,49 BAD (HHHHHH)
Size 50: 23,50 BAD (HHHHHH)
Size 51: 24,51 BAD (HHHHHH)
Size 52: 24,52 BAD (HHHHHH)
Size 53: 25,53 BAD (HHHHHH)
Size 54: 25,54 BAD (HHHHHH)
Size 55: 26,55 BAD (HHHHHH)
Size 56: 26,56 BAD (HHHHHH)
Size 57: 27,57 BAD (HHHHHH)
Size 58: 27,58 BAD (HHHHHH)
Size 59: 28,59 BAD (HHHHHH)
Size 60: 28,60 BAD (HHHHHH)
Size 61: 29,61 BAD (HHHHHH)
Size 62: 29,62 BAD (HHHHHH)
Size 63: 30,64 BAD (HHHHHH)
Size 64: 30,64 BAD (HHHHHH)
Size 65: 31,65 BAD (HHHHHH)
Size 66: 31,66 BAD (HHHHHH)
Size 67: 31,67 BAD (HHHHHH)
Size 68: 32,68 BAD (HHHHHH)
Size 69: 32,69 BAD (HHHHHH)
Size 70: 33,70 BAD (HHHHHH)
Size 71: 33,71 BAD (HHHHHH)
Size 72: 34,72 BAD (HHHHHH)
Size 73: 34,73 BAD (HHHHHH)
Size 74: 35,74 BAD (HHHHHH)
Size 75: 35,75 BAD (HHHHHH)
Size 76: 36,76 BAD (HHHHHH)
Size 77: 36,77 BAD (HHHHHH)
Size 78: 37,78 BAD (HHHHHH)
Size 79: 37,79 BAD (HHHHHH)
Size 80: 38,80 BAD (HHHHHH)
Size 81: 38,81 BAD (HHHHHH)
Size 82: 39,82 BAD (HHHHHH)
Size 83: 39,83 BAD (HHHHHH)
Size 84: 39,84 BAD (HHHHHH)
Size 85: 40,85 BAD (HHHHHH)
Size 86: 40,86 BAD (HHHHHH)
Size 87: 41,87 BAD (HHHHHH)
Size 88: 41,88 BAD (HHHHHH)
Size 89: 42,89 BAD (HHHHHH)
Size 90: 42,90 BAD (HHHHHH)
Size 91: 43,91 BAD (HHHHHH)
Size 92: 43,92 BAD (HHHHHH)
Size 93: 44,93 BAD (HHHHHH)
Size 94: 44,94 BAD (HHHHHH)
Size 95: 45,95 BAD (HHHHHH)
Size 96: 45,96 BAD (HHHHHH)
Size 97: 46,97 BAD (HHHHHH)
Size 98: 46,98 BAD (HHHHHH)
Size 99: 46,99 BAD (HHHHHH)
Size 100: 47,100 BAD (HHHHHH)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,3 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 1,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 2,5 BAD (HHHHHH)
Size 6: 3,6 BAD (HHHHHH)
Size 7: 3,6 BAD (HHHHHH)
Size 8: 4,8 BAD (HHHHHH)
Size 9: 4,9 BAD (HHHHHH)
Size 10: 5,10 BAD (HHHHHH)
Size 11: 5,11 BAD (HHHHHH)
Size 12: 6,12 BAD (HHHHHH)
Size 13: 6,13 BAD (HHHHHH)
Size 14: 7,14 BAD (HHHHHH)
Size 15: 7,15 BAD (HHHHHH)
Size 16: 8,16 BAD (HHHHHH)
Size 17: 8,17 BAD (HHHHHH)
Size 18: 8,18 BAD (HHHHHH)
Size 19: 9,19 BAD (HHHHHH)
Size 20: 9,20 BAD (HHHHHH)
Size 21: 10,22 BAD (HHHHHH)
Size 22: 10,22 BAD (HHHHHH)
Size 23: 11,23 BAD (HHHHHH)
Size 24: 11,24 BAD (HHHHHH)
Size 25: 12,25 BAD (HHHHHH)
Size 26: 12,26 BAD (HHHHHH)
Size 27: 13,27 BAD (HHHHHH)
Size 28: 13,28 BAD (HHHHHH)
Size 29: 14,29 BAD (HHHHHH)
Size 30: 14,30 BAD (HHHHHH)
Size 31: 15,31 BAD (HHHHHH)
Size 32: 15,32 BAD (HHHHHH)
Size 33: 15,33 BAD (HHHHHH)
Size 34: 16,34 BAD (HHHHHH)
Size 35: 16,36 BAD (HHHHHH)
Size 36: 17,36 BAD (HHHHHH)
Size 37: 17,37 BAD (HHHHHH)
Size 38: 18,38 BAD (HHHHHH)
Size 39: 18,39 BAD (HHHHHH)
Size 40: 19,40 BAD (HHHHHH)
Size 41: 19,41 BAD (HHHHHH)
Size 42: 20,42 BAD (HHHHHH)
Size 43: 20,43 BAD (HHHHHH)
Size 44: 21,44 BAD (HHHHHH)
Size 45: 21,45 BAD (HHHHHH)
Size 46: 22,46 BAD (HHHHHH)
Size 47: 22,47 BAD (HHHHHH)
Size 48: 23,48 BAD (HHHHHH)
Size 49: 23,49 BAD (HHHHHH)
Size 50: 23,50 BAD (HHHHHH)
Size 51: 24,51 BAD (HHHHHH)
Size 52: 24,52 BAD (HHHHHH)
Size 53: 25,53 BAD (HHHHHH)
Size 54: 25,54 BAD (HHHHHH)
Size 55: 26,55 BAD (HHHHHH)
Size 56: 26,56 BAD (HHHHHH)
Size 57: 27,57 BAD (HHHHHH)
Size 58: 27,58 BAD (HHHHHH)
Size 59: 28,59 BAD (HHHHHH)
Size 60: 28,60 BAD (HHHHHH)
Size 61: 29,61 BAD (HHHHHH)
Size 62: 29,62 BAD (HHHHHH)
Size 63: 30,64 BAD (HHHHHH)
Size 64: 30,64 BAD (HHHHHH)
Size 65: 31,65 BAD (HHHHHH)
Size 66: 31,66 BAD (HHHHHH)
Size 67: 31,67 BAD (HHHHHH)
Size 68: 32,68 BAD (HHHHHH)
Size 69: 32,69 BAD (HHHHHH)
Size 70: 33,70 BAD (HHHHHH)
Size 71: 33,71 BAD (HHHHHH)
Size 72: 34,72 BAD (HHHHHH)
Size 73: 34,73 BAD (HHHHHH)
Size 74: 35,74 BAD (HHHHHH)
Size 75: 35,75 BAD (HHHHHH)
Size 76: 36,76 BAD (HHHHHH)
Size 77: 36,77 BAD (HHHHHH)
Size 78: 37,78 BAD (HHHHHH)
Size 79: 37,79 BAD (HHHHHH)
Size 80: 38,80 BAD (HHHHHH)
Size 81: 38,81 BAD (HHHHHH)
Size 82: 39,82 BAD (HHHHHH)
Size 83: 39,83 BAD (HHHHHH)
Size 84: 39,84 BAD (HHHHHH)
Size 85: 40,85 BAD (HHHHHH)
Size 86: 40,86 BAD (HHHHHH)
Size 87: 41,87 BAD (HHHHHH)
Size 88: 41,88 BAD (HHHHHH)
Size 89: 42,89 BAD (HHHHHH)
Size 90: 42,90 BAD (HHHHHH)
Size 91: 43,91 BAD (HHHHHH)
Size 92: 43,92 BAD (HHHHHH)
Size 93: 44,93 BAD (HHHHHH)
Size 94: 44,94 BAD (HHHHHH)
Size 95: 45,95 BAD (HHHHHH)
Size 96: 45,96 BAD (HHHHHH)
Size 97: 46,97 BAD (HHHHHH)
Size 98: 46,98 BAD (HHHHHH)
Size 99: 46,99 BAD (HHHHHH)
Size 100: 47,100 BAD (HHHHHH)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 1,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 2,5 BAD (HHHHHH)
Size 6: 3,6 BAD (HHHHHH)
Size 7: 3,7 BAD (HHHHHH)
Size 8: 4,8 BAD (HHHHHH)
Size 9: 4,9 BAD (HHHHHH)
Size 10: 5,10 BAD (HHHHHH)
Size 11: 5,11 BAD (HHHHHH)
Size 12: 6,12 BAD (HHHHHH)
Size 13: 6,13 BAD (HHHHHH)
Size 14: 7,14 BAD (HHHHHH)
Size 15: 7,15 BAD (HHHHHH)
Size 16: 8,16 BAD (HHHHHH)
Size 17: 8,17 BAD (HHHHHH)
Size 18: 8,18 BAD (HHHHHH)
Size 19: 9,19 BAD (HHHHHH)
Size 20: 9,20 BAD (HHHHHH)
Size 21: 10,21 BAD (HHHHHH)
Size 22: 10,22 BAD (HHHHHH)
Size 23: 11,23 BAD (HHHHHH)
Size 24: 11,24 BAD (HHHHHH)
Size 25: 12,25 BAD (HHHHHH)
Size 26: 12,26 BAD (HHHHHH)
Size 27: 13,27 BAD (HHHHHH)
Size 28: 13,28 BAD (HHHHHH)
Size 29: 14,29 BAD (HHHHHH)
Size 30: 14,30 BAD (HHHHHH)
Size 31: 15,31 BAD (HHHHHH)
Size 32: 15,32 BAD (HHHHHH)
Size 33: 15,33 BAD (HHHHHH)
Size 34: 16,34 BAD (HHHHHH)
Size 35: 16,35 BAD (HHHHHH)
Size 36: 17,36 BAD (HHHHHH)
Size 37: 17,37 BAD (HHHHHH)
Size 38: 18,38 BAD (HHHHHH)
Size 39: 18,39 BAD (HHHHHH)
Size 40: 19,40 BAD (HHHHHH)
Size 41: 19,41 BAD (HHHHHH)
Size 42: 20,42 BAD (HHHHHH)
Size 43: 20,43 BAD (HHHHHH)
Size 44: 21,44 BAD (HHHHHH)
Size 45: 21,45 BAD (HHHHHH)
Size 46: 22,46 BAD (HHHHHH)
Size 47: 22,47 BAD (HHHHHH)
Size 48: 23,48 BAD (HHHHHH)
Size 49: 23,49 BAD (HHHHHH)
Size 50: 23,50 BAD (HHHHHH)
Size 51: 24,51 BAD (HHHHHH)
Size 52: 24,52 BAD (HHHHHH)
Size 53: 25,53 BAD (HHHHHH)
Size 54: 25,54 BAD (HHHHHH)
Size 55: 26,55 BAD (HHHHHH)
Size 56: 26,56 BAD (HHHHHH)
Size 57: 27,57 BAD (HHHHHH)
Size 58: 27,58 BAD (HHHHHH)
Size 59: 28,59 BAD (HHHHHH)
Size 60: 28,60 BAD (HHHHHH)
Size 61: 29,61 BAD (HHHHHH)
Size 62: 29,62 BAD (HHHHHH)
Size 63: 30,63 BAD (HHHHHH)
Size 64: 30,64 BAD (HHHHHH)
Size 65: 31,65 BAD (HHHHHH)
Size 66: 31,66 BAD (HHHHHH)
Size 67: 31,67 BAD (HHHHHH)
Size 68: 32,68 BAD (HHHHHH)
Size 69: 32,69 BAD (HHHHHH)
Size 70: 33,70 BAD (HHHHHH)
Size 71: 33,71 BAD (HHHHHH)
Size 72: 34,72 BAD (HHHHHH)
Size 73: 34,73 BAD (HHHHHH)
Size 74: 35,74 BAD (HHHHHH)
Size 75: 35,75 BAD (HHHHHH)
Size 76: 36,76 BAD (HHHHHH)
Size 77: 36,77 BAD (HHHHHH)
Size 78: 37,78 BAD (HHHHHH)
Size 79: 37,79 BAD (HHHHHH)
Size 80: 38,80 BAD (HHHHHH)
Size 81: 38,81 BAD (HHHHHH)
Size 82: 39,82 BAD (HHHHHH)
Size 83: 39,83 BAD (HHHHHH)
Size 84: 39,84 BAD (HHHHHH)
Size 85: 40,85 BAD (HHHHHH)
Size 86: 40,86 BAD (HHHHHH)
Size 87: 41,87 BAD (HHHHHH)
Size 88: 41,88 BAD (HHHHHH)
Size 89: 42,89 BAD (HHHHHH)
Size 90: 42,90 BAD (HHHHHH)
Size 91: 43,91 BAD (HHHHHH)
Size 92: 43,92 BAD (HHHHHH)
Size 93: 44,93 BAD (HHHHHH)
Size 94: 44,94 BAD (HHHHHH)
Size 95: 45,95 BAD (HHHHHH)
Size 96: 45,96 BAD (HHHHHH)
Size 97: 46,97 BAD (HHHHHH)
Size 98: 46,98 BAD (HHHHHH)
Size 99: 46,99 BAD (HHHHHH)
Size 100: 47,100 BAD (HHHHHH)

View File

@ -0,0 +1,633 @@
==================================
Code Page 437, Lucida Console font
==================================
Options: -face "Lucida Console" -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
FontSurvey "-face \"Lucida Console\" -family 0x36"
Vista
-----
Size 1: 1,2 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,65 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)
Windows 7
---------
Size 1: 1,2 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,65 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)
Windows 8
---------
Size 1: 1,2 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,65 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)
Windows 8.1
-----------
Size 1: 1,2 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,65 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,2 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,65 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 BAD (HHHHHH)
Size 2: 1,2 BAD (HHHHHH)
Size 3: 2,3 BAD (HHHHHH)
Size 4: 2,4 BAD (HHHHHH)
Size 5: 3,5 BAD (HHHHHH)
Size 6: 4,6 BAD (HHHHHH)
Size 7: 4,7 BAD (HHHHHH)
Size 8: 5,8 BAD (HHHHHH)
Size 9: 5,9 BAD (HHHHHH)
Size 10: 6,10 BAD (HHHHHH)
Size 11: 7,11 BAD (HHHHHH)
Size 12: 7,12 BAD (HHHHHH)
Size 13: 8,13 BAD (HHHHHH)
Size 14: 8,14 BAD (HHHHHH)
Size 15: 9,15 BAD (HHHHHH)
Size 16: 10,16 BAD (HHHHHH)
Size 17: 10,17 BAD (HHHHHH)
Size 18: 11,18 BAD (HHHHHH)
Size 19: 11,19 BAD (HHHHHH)
Size 20: 12,20 BAD (HHHHHH)
Size 21: 13,21 BAD (HHHHHH)
Size 22: 13,22 BAD (HHHHHH)
Size 23: 14,23 BAD (HHHHHH)
Size 24: 14,24 BAD (HHHHHH)
Size 25: 15,25 BAD (HHHHHH)
Size 26: 16,26 BAD (HHHHHH)
Size 27: 16,27 BAD (HHHHHH)
Size 28: 17,28 BAD (HHHHHH)
Size 29: 17,29 BAD (HHHHHH)
Size 30: 18,30 BAD (HHHHHH)
Size 31: 19,31 BAD (HHHHHH)
Size 32: 19,32 BAD (HHHHHH)
Size 33: 20,33 BAD (HHHHHH)
Size 34: 20,34 BAD (HHHHHH)
Size 35: 21,35 BAD (HHHHHH)
Size 36: 22,36 BAD (HHHHHH)
Size 37: 22,37 BAD (HHHHHH)
Size 38: 23,38 BAD (HHHHHH)
Size 39: 23,39 BAD (HHHHHH)
Size 40: 24,40 BAD (HHHHHH)
Size 41: 25,41 BAD (HHHHHH)
Size 42: 25,42 BAD (HHHHHH)
Size 43: 26,43 BAD (HHHHHH)
Size 44: 27,44 BAD (HHHHHH)
Size 45: 27,45 BAD (HHHHHH)
Size 46: 28,46 BAD (HHHHHH)
Size 47: 28,47 BAD (HHHHHH)
Size 48: 29,48 BAD (HHHHHH)
Size 49: 30,49 BAD (HHHHHH)
Size 50: 30,50 BAD (HHHHHH)
Size 51: 31,51 BAD (HHHHHH)
Size 52: 31,52 BAD (HHHHHH)
Size 53: 32,53 BAD (HHHHHH)
Size 54: 33,54 BAD (HHHHHH)
Size 55: 33,55 BAD (HHHHHH)
Size 56: 34,56 BAD (HHHHHH)
Size 57: 34,57 BAD (HHHHHH)
Size 58: 35,58 BAD (HHHHHH)
Size 59: 36,59 BAD (HHHHHH)
Size 60: 36,60 BAD (HHHHHH)
Size 61: 37,61 BAD (HHHHHH)
Size 62: 37,62 BAD (HHHHHH)
Size 63: 38,63 BAD (HHHHHH)
Size 64: 39,64 BAD (HHHHHH)
Size 65: 39,65 BAD (HHHHHH)
Size 66: 40,66 BAD (HHHHHH)
Size 67: 40,67 BAD (HHHHHH)
Size 68: 41,68 BAD (HHHHHH)
Size 69: 42,69 BAD (HHHHHH)
Size 70: 42,70 BAD (HHHHHH)
Size 71: 43,71 BAD (HHHHHH)
Size 72: 43,72 BAD (HHHHHH)
Size 73: 44,73 BAD (HHHHHH)
Size 74: 45,74 BAD (HHHHHH)
Size 75: 45,75 BAD (HHHHHH)
Size 76: 46,76 BAD (HHHHHH)
Size 77: 46,77 BAD (HHHHHH)
Size 78: 47,78 BAD (HHHHHH)
Size 79: 48,79 BAD (HHHHHH)
Size 80: 48,80 BAD (HHHHHH)
Size 81: 49,81 BAD (HHHHHH)
Size 82: 49,82 BAD (HHHHHH)
Size 83: 50,83 BAD (HHHHHH)
Size 84: 51,84 BAD (HHHHHH)
Size 85: 51,85 BAD (HHHHHH)
Size 86: 52,86 BAD (HHHHHH)
Size 87: 52,87 BAD (HHHHHH)
Size 88: 53,88 BAD (HHHHHH)
Size 89: 54,89 BAD (HHHHHH)
Size 90: 54,90 BAD (HHHHHH)
Size 91: 55,91 BAD (HHHHHH)
Size 92: 55,92 BAD (HHHHHH)
Size 93: 56,93 BAD (HHHHHH)
Size 94: 57,94 BAD (HHHHHH)
Size 95: 57,95 BAD (HHHHHH)
Size 96: 58,96 BAD (HHHHHH)
Size 97: 58,97 BAD (HHHHHH)
Size 98: 59,98 BAD (HHHHHH)
Size 99: 60,99 BAD (HHHHHH)
Size 100: 60,100 BAD (HHHHHH)

View File

@ -0,0 +1,630 @@
=======================================
Code Page 932, Japanese, MS Gothic font
=======================================
Options: -face-gothic -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
Vista
-----
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,4 OK (HHHFFF)
Size 5: 3,5 OK (HHHFFF)
Size 6: 3,6 OK (HHHFFF)
Size 7: 4,7 OK (HHHFFF)
Size 8: 4,8 OK (HHHFFF)
Size 9: 5,9 OK (HHHFFF)
Size 10: 5,10 OK (HHHFFF)
Size 11: 6,11 OK (HHHFFF)
Size 12: 6,12 OK (HHHFFF)
Size 13: 7,13 OK (HHHFFF)
Size 14: 7,14 BAD (HHHFHH)
Size 15: 8,15 OK (HHHFFF)
Size 16: 8,16 BAD (HHHFHH)
Size 17: 9,17 OK (HHHFFF)
Size 18: 9,18 BAD (HHHFHH)
Size 19: 10,19 OK (HHHFFF)
Size 20: 10,20 BAD (HHHFHH)
Size 21: 11,21 OK (HHHFFF)
Size 22: 11,22 BAD (HHHFHH)
Size 23: 12,23 BAD (HHHFHH)
Size 24: 12,24 BAD (HHHFHH)
Size 25: 13,25 BAD (HHHFHH)
Size 26: 13,26 BAD (HHHFHH)
Size 27: 14,27 BAD (HHHFHH)
Size 28: 14,28 BAD (HHHFHH)
Size 29: 15,29 BAD (HHHFHH)
Size 30: 15,30 BAD (HHHFHH)
Size 31: 16,31 BAD (HHHFHH)
Size 32: 16,33 BAD (HHHFHH)
Size 33: 17,33 BAD (HHHFHH)
Size 34: 17,34 BAD (HHHFHH)
Size 35: 18,35 BAD (HHHFHH)
Size 36: 18,36 BAD (HHHFHH)
Size 37: 19,37 BAD (HHHFHH)
Size 38: 19,38 BAD (HHHFHH)
Size 39: 20,39 BAD (HHHFHH)
Size 40: 20,40 BAD (HHHFHH)
Size 41: 21,41 BAD (HHHFHH)
Size 42: 21,42 BAD (HHHFHH)
Size 43: 22,43 BAD (HHHFHH)
Size 44: 22,44 BAD (HHHFHH)
Size 45: 23,45 BAD (HHHFHH)
Size 46: 23,46 BAD (HHHFHH)
Size 47: 24,47 BAD (HHHFHH)
Size 48: 24,48 BAD (HHHFHH)
Size 49: 25,49 BAD (HHHFHH)
Size 50: 25,50 BAD (HHHFHH)
Size 51: 26,51 BAD (HHHFHH)
Size 52: 26,52 BAD (HHHFHH)
Size 53: 27,53 BAD (HHHFHH)
Size 54: 27,54 BAD (HHHFHH)
Size 55: 28,55 BAD (HHHFHH)
Size 56: 28,56 BAD (HHHFHH)
Size 57: 29,57 BAD (HHHFHH)
Size 58: 29,58 BAD (HHHFHH)
Size 59: 30,59 BAD (HHHFHH)
Size 60: 30,60 BAD (HHHFHH)
Size 61: 31,61 BAD (HHHFHH)
Size 62: 31,62 BAD (HHHFHH)
Size 63: 32,63 BAD (HHHFHH)
Size 64: 32,64 BAD (HHHFHH)
Size 65: 33,65 BAD (HHHFHH)
Size 66: 33,66 BAD (HHHFHH)
Size 67: 34,67 BAD (HHHFHH)
Size 68: 34,68 BAD (HHHFHH)
Size 69: 35,69 BAD (HHHFHH)
Size 70: 35,70 BAD (HHHFHH)
Size 71: 36,71 BAD (HHHFHH)
Size 72: 36,72 BAD (HHHFHH)
Size 73: 37,73 BAD (HHHFHH)
Size 74: 37,74 BAD (HHHFHH)
Size 75: 38,75 BAD (HHHFHH)
Size 76: 38,76 BAD (HHHFHH)
Size 77: 39,77 BAD (HHHFHH)
Size 78: 39,78 BAD (HHHFHH)
Size 79: 40,79 BAD (HHHFHH)
Size 80: 40,80 BAD (HHHFHH)
Size 81: 41,81 BAD (HHHFHH)
Size 82: 41,82 BAD (HHHFHH)
Size 83: 42,83 BAD (HHHFHH)
Size 84: 42,84 BAD (HHHFHH)
Size 85: 43,85 BAD (HHHFHH)
Size 86: 43,86 BAD (HHHFHH)
Size 87: 44,87 BAD (HHHFHH)
Size 88: 44,88 BAD (HHHFHH)
Size 89: 45,89 BAD (HHHFHH)
Size 90: 45,90 BAD (HHHFHH)
Size 91: 46,91 BAD (HHHFHH)
Size 92: 46,92 BAD (HHHFHH)
Size 93: 47,93 BAD (HHHFHH)
Size 94: 47,94 BAD (HHHFHH)
Size 95: 48,95 BAD (HHHFHH)
Size 96: 48,97 BAD (HHHFHH)
Size 97: 49,97 BAD (HHHFHH)
Size 98: 49,98 BAD (HHHFHH)
Size 99: 50,99 BAD (HHHFHH)
Size 100: 50,100 BAD (HHHFHH)
Windows 7
---------
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,4 OK (HHHFFF)
Size 5: 3,5 OK (HHHFFF)
Size 6: 3,6 OK (HHHFFF)
Size 7: 4,7 OK (HHHFFF)
Size 8: 4,8 OK (HHHFFF)
Size 9: 5,9 OK (HHHFFF)
Size 10: 5,10 OK (HHHFFF)
Size 11: 6,11 OK (HHHFFF)
Size 12: 6,12 OK (HHHFFF)
Size 13: 7,13 OK (HHHFFF)
Size 14: 7,14 BAD (FFFFFF)
Size 15: 8,15 OK (HHHFFF)
Size 16: 8,16 BAD (FFFFFF)
Size 17: 9,17 OK (HHHFFF)
Size 18: 9,18 BAD (FFFFFF)
Size 19: 10,19 OK (HHHFFF)
Size 20: 10,20 BAD (FFFFFF)
Size 21: 11,21 OK (HHHFFF)
Size 22: 11,22 BAD (FFFFFF)
Size 23: 12,23 BAD (FFFFFF)
Size 24: 12,24 BAD (FFFFFF)
Size 25: 13,25 BAD (FFFFFF)
Size 26: 13,26 BAD (FFFFFF)
Size 27: 14,27 BAD (FFFFFF)
Size 28: 14,28 BAD (FFFFFF)
Size 29: 15,29 BAD (FFFFFF)
Size 30: 15,30 BAD (FFFFFF)
Size 31: 16,31 BAD (FFFFFF)
Size 32: 16,33 BAD (FFFFFF)
Size 33: 17,33 BAD (FFFFFF)
Size 34: 17,34 BAD (FFFFFF)
Size 35: 18,35 BAD (FFFFFF)
Size 36: 18,36 BAD (FFFFFF)
Size 37: 19,37 BAD (FFFFFF)
Size 38: 19,38 BAD (FFFFFF)
Size 39: 20,39 BAD (FFFFFF)
Size 40: 20,40 BAD (FFFFFF)
Size 41: 21,41 BAD (FFFFFF)
Size 42: 21,42 BAD (FFFFFF)
Size 43: 22,43 BAD (FFFFFF)
Size 44: 22,44 BAD (FFFFFF)
Size 45: 23,45 BAD (FFFFFF)
Size 46: 23,46 BAD (FFFFFF)
Size 47: 24,47 BAD (FFFFFF)
Size 48: 24,48 BAD (FFFFFF)
Size 49: 25,49 BAD (FFFFFF)
Size 50: 25,50 BAD (FFFFFF)
Size 51: 26,51 BAD (FFFFFF)
Size 52: 26,52 BAD (FFFFFF)
Size 53: 27,53 BAD (FFFFFF)
Size 54: 27,54 BAD (FFFFFF)
Size 55: 28,55 BAD (FFFFFF)
Size 56: 28,56 BAD (FFFFFF)
Size 57: 29,57 BAD (FFFFFF)
Size 58: 29,58 BAD (FFFFFF)
Size 59: 30,59 BAD (FFFFFF)
Size 60: 30,60 BAD (FFFFFF)
Size 61: 31,61 BAD (FFFFFF)
Size 62: 31,62 BAD (FFFFFF)
Size 63: 32,63 BAD (FFFFFF)
Size 64: 32,64 BAD (FFFFFF)
Size 65: 33,65 BAD (FFFFFF)
Size 66: 33,66 BAD (FFFFFF)
Size 67: 34,67 BAD (FFFFFF)
Size 68: 34,68 BAD (FFFFFF)
Size 69: 35,69 BAD (FFFFFF)
Size 70: 35,70 BAD (FFFFFF)
Size 71: 36,71 BAD (FFFFFF)
Size 72: 36,72 BAD (FFFFFF)
Size 73: 37,73 BAD (FFFFFF)
Size 74: 37,74 BAD (FFFFFF)
Size 75: 38,75 BAD (FFFFFF)
Size 76: 38,76 BAD (FFFFFF)
Size 77: 39,77 BAD (FFFFFF)
Size 78: 39,78 BAD (FFFFFF)
Size 79: 40,79 BAD (FFFFFF)
Size 80: 40,80 BAD (FFFFFF)
Size 81: 41,81 BAD (FFFFFF)
Size 82: 41,82 BAD (FFFFFF)
Size 83: 42,83 BAD (FFFFFF)
Size 84: 42,84 BAD (FFFFFF)
Size 85: 43,85 BAD (FFFFFF)
Size 86: 43,86 BAD (FFFFFF)
Size 87: 44,87 BAD (FFFFFF)
Size 88: 44,88 BAD (FFFFFF)
Size 89: 45,89 BAD (FFFFFF)
Size 90: 45,90 BAD (FFFFFF)
Size 91: 46,91 BAD (FFFFFF)
Size 92: 46,92 BAD (FFFFFF)
Size 93: 47,93 BAD (FFFFFF)
Size 94: 47,94 BAD (FFFFFF)
Size 95: 48,95 BAD (FFFFFF)
Size 96: 48,97 BAD (FFFFFF)
Size 97: 49,97 BAD (FFFFFF)
Size 98: 49,98 BAD (FFFFFF)
Size 99: 50,99 BAD (FFFFFF)
Size 100: 50,100 BAD (FFFFFF)
Windows 8
---------
Size 1: 1,2 BAD (FFFFHH)
Size 2: 1,2 BAD (FFFFHH)
Size 3: 2,3 BAD (FFFFFF)
Size 4: 2,4 BAD (FFFFHH)
Size 5: 3,5 BAD (FFFFFF)
Size 6: 3,6 BAD (FFFFHH)
Size 7: 4,7 BAD (FFFFFF)
Size 8: 4,8 BAD (FFFFHH)
Size 9: 5,9 BAD (FFFFFF)
Size 10: 5,10 BAD (FFFFHH)
Size 11: 6,11 BAD (FFFFFF)
Size 12: 6,12 BAD (FFFFHH)
Size 13: 7,13 BAD (FFFFFF)
Size 14: 7,14 BAD (FFFFHH)
Size 15: 8,15 BAD (FFFFFF)
Size 16: 8,16 BAD (FFFFHH)
Size 17: 9,17 BAD (FFFFFF)
Size 18: 9,18 BAD (FFFFHH)
Size 19: 10,19 BAD (FFFFFF)
Size 20: 10,20 BAD (FFFFFF)
Size 21: 11,21 BAD (FFFFFF)
Size 22: 11,22 BAD (FFFFFF)
Size 23: 12,23 BAD (FFFFFF)
Size 24: 12,24 BAD (FFFFFF)
Size 25: 13,25 BAD (FFFFFF)
Size 26: 13,26 BAD (FFFFFF)
Size 27: 14,27 BAD (FFFFFF)
Size 28: 14,28 BAD (FFFFFF)
Size 29: 15,29 BAD (FFFFFF)
Size 30: 15,30 BAD (FFFFFF)
Size 31: 16,31 BAD (FFFFFF)
Size 32: 16,33 BAD (FFFFFF)
Size 33: 17,33 BAD (FFFFFF)
Size 34: 17,34 BAD (FFFFFF)
Size 35: 18,35 BAD (FFFFFF)
Size 36: 18,36 BAD (FFFFFF)
Size 37: 19,37 BAD (FFFFFF)
Size 38: 19,38 BAD (FFFFFF)
Size 39: 20,39 BAD (FFFFFF)
Size 40: 20,40 BAD (FFFFFF)
Size 41: 21,41 BAD (FFFFFF)
Size 42: 21,42 BAD (FFFFFF)
Size 43: 22,43 BAD (FFFFFF)
Size 44: 22,44 BAD (FFFFFF)
Size 45: 23,45 BAD (FFFFFF)
Size 46: 23,46 BAD (FFFFFF)
Size 47: 24,47 BAD (FFFFFF)
Size 48: 24,48 BAD (FFFFFF)
Size 49: 25,49 BAD (FFFFFF)
Size 50: 25,50 BAD (FFFFFF)
Size 51: 26,51 BAD (FFFFFF)
Size 52: 26,52 BAD (FFFFFF)
Size 53: 27,53 BAD (FFFFFF)
Size 54: 27,54 BAD (FFFFFF)
Size 55: 28,55 BAD (FFFFFF)
Size 56: 28,56 BAD (FFFFFF)
Size 57: 29,57 BAD (FFFFFF)
Size 58: 29,58 BAD (FFFFFF)
Size 59: 30,59 BAD (FFFFFF)
Size 60: 30,60 BAD (FFFFFF)
Size 61: 31,61 BAD (FFFFFF)
Size 62: 31,62 BAD (FFFFFF)
Size 63: 32,63 BAD (FFFFFF)
Size 64: 32,64 BAD (FFFFFF)
Size 65: 33,65 BAD (FFFFFF)
Size 66: 33,66 BAD (FFFFFF)
Size 67: 34,67 BAD (FFFFFF)
Size 68: 34,68 BAD (FFFFFF)
Size 69: 35,69 BAD (FFFFFF)
Size 70: 35,70 BAD (FFFFFF)
Size 71: 36,71 BAD (FFFFFF)
Size 72: 36,72 BAD (FFFFFF)
Size 73: 37,73 BAD (FFFFFF)
Size 74: 37,74 BAD (FFFFFF)
Size 75: 38,75 BAD (FFFFFF)
Size 76: 38,76 BAD (FFFFFF)
Size 77: 39,77 BAD (FFFFFF)
Size 78: 39,78 BAD (FFFFFF)
Size 79: 40,79 BAD (FFFFFF)
Size 80: 40,80 BAD (FFFFFF)
Size 81: 41,81 BAD (FFFFFF)
Size 82: 41,82 BAD (FFFFFF)
Size 83: 42,83 BAD (FFFFFF)
Size 84: 42,84 BAD (FFFFFF)
Size 85: 43,85 BAD (FFFFFF)
Size 86: 43,86 BAD (FFFFFF)
Size 87: 44,87 BAD (FFFFFF)
Size 88: 44,88 BAD (FFFFFF)
Size 89: 45,89 BAD (FFFFFF)
Size 90: 45,90 BAD (FFFFFF)
Size 91: 46,91 BAD (FFFFFF)
Size 92: 46,92 BAD (FFFFFF)
Size 93: 47,93 BAD (FFFFFF)
Size 94: 47,94 BAD (FFFFFF)
Size 95: 48,95 BAD (FFFFFF)
Size 96: 48,97 BAD (FFFFFF)
Size 97: 49,97 BAD (FFFFFF)
Size 98: 49,98 BAD (FFFFFF)
Size 99: 50,99 BAD (FFFFFF)
Size 100: 50,100 BAD (FFFFFF)
Windows 8.1
-----------
Size 1: 1,2 BAD (FFFFHH)
Size 2: 1,2 BAD (FFFFHH)
Size 3: 2,3 BAD (FFFFFF)
Size 4: 2,4 BAD (FFFFHH)
Size 5: 3,5 BAD (FFFFFF)
Size 6: 3,6 BAD (FFFFHH)
Size 7: 4,7 BAD (FFFFFF)
Size 8: 4,8 BAD (FFFFHH)
Size 9: 5,9 BAD (FFFFFF)
Size 10: 5,10 BAD (FFFFHH)
Size 11: 6,11 BAD (FFFFFF)
Size 12: 6,12 BAD (FFFFHH)
Size 13: 7,13 BAD (FFFFFF)
Size 14: 7,14 BAD (FFFFHH)
Size 15: 8,15 BAD (FFFFFF)
Size 16: 8,16 BAD (FFFFHH)
Size 17: 9,17 BAD (FFFFFF)
Size 18: 9,18 BAD (FFFFHH)
Size 19: 10,19 BAD (FFFFFF)
Size 20: 10,20 BAD (FFFFFF)
Size 21: 11,21 BAD (FFFFFF)
Size 22: 11,22 BAD (FFFFFF)
Size 23: 12,23 BAD (FFFFFF)
Size 24: 12,24 BAD (FFFFFF)
Size 25: 13,25 BAD (FFFFFF)
Size 26: 13,26 BAD (FFFFFF)
Size 27: 14,27 BAD (FFFFFF)
Size 28: 14,28 BAD (FFFFFF)
Size 29: 15,29 BAD (FFFFFF)
Size 30: 15,30 BAD (FFFFFF)
Size 31: 16,31 BAD (FFFFFF)
Size 32: 16,33 BAD (FFFFFF)
Size 33: 17,33 BAD (FFFFFF)
Size 34: 17,34 BAD (FFFFFF)
Size 35: 18,35 BAD (FFFFFF)
Size 36: 18,36 BAD (FFFFFF)
Size 37: 19,37 BAD (FFFFFF)
Size 38: 19,38 BAD (FFFFFF)
Size 39: 20,39 BAD (FFFFFF)
Size 40: 20,40 BAD (FFFFFF)
Size 41: 21,41 BAD (FFFFFF)
Size 42: 21,42 BAD (FFFFFF)
Size 43: 22,43 BAD (FFFFFF)
Size 44: 22,44 BAD (FFFFFF)
Size 45: 23,45 BAD (FFFFFF)
Size 46: 23,46 BAD (FFFFFF)
Size 47: 24,47 BAD (FFFFFF)
Size 48: 24,48 BAD (FFFFFF)
Size 49: 25,49 BAD (FFFFFF)
Size 50: 25,50 BAD (FFFFFF)
Size 51: 26,51 BAD (FFFFFF)
Size 52: 26,52 BAD (FFFFFF)
Size 53: 27,53 BAD (FFFFFF)
Size 54: 27,54 BAD (FFFFFF)
Size 55: 28,55 BAD (FFFFFF)
Size 56: 28,56 BAD (FFFFFF)
Size 57: 29,57 BAD (FFFFFF)
Size 58: 29,58 BAD (FFFFFF)
Size 59: 30,59 BAD (FFFFFF)
Size 60: 30,60 BAD (FFFFFF)
Size 61: 31,61 BAD (FFFFFF)
Size 62: 31,62 BAD (FFFFFF)
Size 63: 32,63 BAD (FFFFFF)
Size 64: 32,64 BAD (FFFFFF)
Size 65: 33,65 BAD (FFFFFF)
Size 66: 33,66 BAD (FFFFFF)
Size 67: 34,67 BAD (FFFFFF)
Size 68: 34,68 BAD (FFFFFF)
Size 69: 35,69 BAD (FFFFFF)
Size 70: 35,70 BAD (FFFFFF)
Size 71: 36,71 BAD (FFFFFF)
Size 72: 36,72 BAD (FFFFFF)
Size 73: 37,73 BAD (FFFFFF)
Size 74: 37,74 BAD (FFFFFF)
Size 75: 38,75 BAD (FFFFFF)
Size 76: 38,76 BAD (FFFFFF)
Size 77: 39,77 BAD (FFFFFF)
Size 78: 39,78 BAD (FFFFFF)
Size 79: 40,79 BAD (FFFFFF)
Size 80: 40,80 BAD (FFFFFF)
Size 81: 41,81 BAD (FFFFFF)
Size 82: 41,82 BAD (FFFFFF)
Size 83: 42,83 BAD (FFFFFF)
Size 84: 42,84 BAD (FFFFFF)
Size 85: 43,85 BAD (FFFFFF)
Size 86: 43,86 BAD (FFFFFF)
Size 87: 44,87 BAD (FFFFFF)
Size 88: 44,88 BAD (FFFFFF)
Size 89: 45,89 BAD (FFFFFF)
Size 90: 45,90 BAD (FFFFFF)
Size 91: 46,91 BAD (FFFFFF)
Size 92: 46,92 BAD (FFFFFF)
Size 93: 47,93 BAD (FFFFFF)
Size 94: 47,94 BAD (FFFFFF)
Size 95: 48,95 BAD (FFFFFF)
Size 96: 48,97 BAD (FFFFFF)
Size 97: 49,97 BAD (FFFFFF)
Size 98: 49,98 BAD (FFFFFF)
Size 99: 50,99 BAD (FFFFFF)
Size 100: 50,100 BAD (FFFFFF)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,2 BAD (FFFFHH)
Size 2: 1,2 BAD (FFFFHH)
Size 3: 2,3 BAD (FFFFFF)
Size 4: 2,4 BAD (FFFFHH)
Size 5: 3,5 BAD (FFFFFF)
Size 6: 3,6 BAD (FFFFHH)
Size 7: 4,7 BAD (FFFFFF)
Size 8: 4,8 BAD (FFFFHH)
Size 9: 5,9 BAD (FFFFFF)
Size 10: 5,10 BAD (FFFFHH)
Size 11: 6,11 BAD (FFFFFF)
Size 12: 6,12 BAD (FFFFHH)
Size 13: 7,13 BAD (FFFFFF)
Size 14: 7,14 BAD (FFFFHH)
Size 15: 8,15 BAD (FFFFFF)
Size 16: 8,16 BAD (FFFFHH)
Size 17: 9,17 BAD (FFFFFF)
Size 18: 9,18 BAD (FFFFHH)
Size 19: 10,19 BAD (FFFFFF)
Size 20: 10,20 BAD (FFFFFF)
Size 21: 11,21 BAD (FFFFFF)
Size 22: 11,22 BAD (FFFFFF)
Size 23: 12,23 BAD (FFFFFF)
Size 24: 12,24 BAD (FFFFFF)
Size 25: 13,25 BAD (FFFFFF)
Size 26: 13,26 BAD (FFFFFF)
Size 27: 14,27 BAD (FFFFFF)
Size 28: 14,28 BAD (FFFFFF)
Size 29: 15,29 BAD (FFFFFF)
Size 30: 15,30 BAD (FFFFFF)
Size 31: 16,31 BAD (FFFFFF)
Size 32: 16,33 BAD (FFFFFF)
Size 33: 17,33 BAD (FFFFFF)
Size 34: 17,34 BAD (FFFFFF)
Size 35: 18,35 BAD (FFFFFF)
Size 36: 18,36 BAD (FFFFFF)
Size 37: 19,37 BAD (FFFFFF)
Size 38: 19,38 BAD (FFFFFF)
Size 39: 20,39 BAD (FFFFFF)
Size 40: 20,40 BAD (FFFFFF)
Size 41: 21,41 BAD (FFFFFF)
Size 42: 21,42 BAD (FFFFFF)
Size 43: 22,43 BAD (FFFFFF)
Size 44: 22,44 BAD (FFFFFF)
Size 45: 23,45 BAD (FFFFFF)
Size 46: 23,46 BAD (FFFFFF)
Size 47: 24,47 BAD (FFFFFF)
Size 48: 24,48 BAD (FFFFFF)
Size 49: 25,49 BAD (FFFFFF)
Size 50: 25,50 BAD (FFFFFF)
Size 51: 26,51 BAD (FFFFFF)
Size 52: 26,52 BAD (FFFFFF)
Size 53: 27,53 BAD (FFFFFF)
Size 54: 27,54 BAD (FFFFFF)
Size 55: 28,55 BAD (FFFFFF)
Size 56: 28,56 BAD (FFFFFF)
Size 57: 29,57 BAD (FFFFFF)
Size 58: 29,58 BAD (FFFFFF)
Size 59: 30,59 BAD (FFFFFF)
Size 60: 30,60 BAD (FFFFFF)
Size 61: 31,61 BAD (FFFFFF)
Size 62: 31,62 BAD (FFFFFF)
Size 63: 32,63 BAD (FFFFFF)
Size 64: 32,64 BAD (FFFFFF)
Size 65: 33,65 BAD (FFFFFF)
Size 66: 33,66 BAD (FFFFFF)
Size 67: 34,67 BAD (FFFFFF)
Size 68: 34,68 BAD (FFFFFF)
Size 69: 35,69 BAD (FFFFFF)
Size 70: 35,70 BAD (FFFFFF)
Size 71: 36,71 BAD (FFFFFF)
Size 72: 36,72 BAD (FFFFFF)
Size 73: 37,73 BAD (FFFFFF)
Size 74: 37,74 BAD (FFFFFF)
Size 75: 38,75 BAD (FFFFFF)
Size 76: 38,76 BAD (FFFFFF)
Size 77: 39,77 BAD (FFFFFF)
Size 78: 39,78 BAD (FFFFFF)
Size 79: 40,79 BAD (FFFFFF)
Size 80: 40,80 BAD (FFFFFF)
Size 81: 41,81 BAD (FFFFFF)
Size 82: 41,82 BAD (FFFFFF)
Size 83: 42,83 BAD (FFFFFF)
Size 84: 42,84 BAD (FFFFFF)
Size 85: 43,85 BAD (FFFFFF)
Size 86: 43,86 BAD (FFFFFF)
Size 87: 44,87 BAD (FFFFFF)
Size 88: 44,88 BAD (FFFFFF)
Size 89: 45,89 BAD (FFFFFF)
Size 90: 45,90 BAD (FFFFFF)
Size 91: 46,91 BAD (FFFFFF)
Size 92: 46,92 BAD (FFFFFF)
Size 93: 47,93 BAD (FFFFFF)
Size 94: 47,94 BAD (FFFFFF)
Size 95: 48,95 BAD (FFFFFF)
Size 96: 48,97 BAD (FFFFFF)
Size 97: 49,97 BAD (FFFFFF)
Size 98: 49,98 BAD (FFFFFF)
Size 99: 50,99 BAD (FFFFFF)
Size 100: 50,100 BAD (FFFFFF)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 OK (HHHFFF)
Size 4: 2,4 OK (HHHFFF)
Size 5: 3,5 OK (HHHFFF)
Size 6: 3,6 OK (HHHFFF)
Size 7: 4,7 OK (HHHFFF)
Size 8: 4,8 OK (HHHFFF)
Size 9: 5,9 OK (HHHFFF)
Size 10: 5,10 OK (HHHFFF)
Size 11: 6,11 OK (HHHFFF)
Size 12: 6,12 OK (HHHFFF)
Size 13: 7,13 OK (HHHFFF)
Size 14: 7,14 OK (HHHFFF)
Size 15: 8,15 OK (HHHFFF)
Size 16: 8,16 OK (HHHFFF)
Size 17: 9,17 OK (HHHFFF)
Size 18: 9,18 OK (HHHFFF)
Size 19: 10,19 OK (HHHFFF)
Size 20: 10,20 OK (HHHFFF)
Size 21: 11,21 OK (HHHFFF)
Size 22: 11,22 OK (HHHFFF)
Size 23: 12,23 OK (HHHFFF)
Size 24: 12,24 OK (HHHFFF)
Size 25: 13,25 OK (HHHFFF)
Size 26: 13,26 OK (HHHFFF)
Size 27: 14,27 OK (HHHFFF)
Size 28: 14,28 OK (HHHFFF)
Size 29: 15,29 OK (HHHFFF)
Size 30: 15,30 OK (HHHFFF)
Size 31: 16,31 OK (HHHFFF)
Size 32: 16,32 OK (HHHFFF)
Size 33: 17,33 OK (HHHFFF)
Size 34: 17,34 OK (HHHFFF)
Size 35: 18,35 OK (HHHFFF)
Size 36: 18,36 OK (HHHFFF)
Size 37: 19,37 OK (HHHFFF)
Size 38: 19,38 OK (HHHFFF)
Size 39: 20,39 OK (HHHFFF)
Size 40: 20,40 OK (HHHFFF)
Size 41: 21,41 OK (HHHFFF)
Size 42: 21,42 OK (HHHFFF)
Size 43: 22,43 OK (HHHFFF)
Size 44: 22,44 OK (HHHFFF)
Size 45: 23,45 OK (HHHFFF)
Size 46: 23,46 OK (HHHFFF)
Size 47: 24,47 OK (HHHFFF)
Size 48: 24,48 OK (HHHFFF)
Size 49: 25,49 OK (HHHFFF)
Size 50: 25,50 OK (HHHFFF)
Size 51: 26,51 OK (HHHFFF)
Size 52: 26,52 OK (HHHFFF)
Size 53: 27,53 OK (HHHFFF)
Size 54: 27,54 OK (HHHFFF)
Size 55: 28,55 OK (HHHFFF)
Size 56: 28,56 OK (HHHFFF)
Size 57: 29,57 OK (HHHFFF)
Size 58: 29,58 OK (HHHFFF)
Size 59: 30,59 OK (HHHFFF)
Size 60: 30,60 OK (HHHFFF)
Size 61: 31,61 OK (HHHFFF)
Size 62: 31,62 OK (HHHFFF)
Size 63: 32,63 OK (HHHFFF)
Size 64: 32,64 OK (HHHFFF)
Size 65: 33,65 OK (HHHFFF)
Size 66: 33,66 OK (HHHFFF)
Size 67: 34,67 OK (HHHFFF)
Size 68: 34,68 OK (HHHFFF)
Size 69: 35,69 OK (HHHFFF)
Size 70: 35,70 OK (HHHFFF)
Size 71: 36,71 OK (HHHFFF)
Size 72: 36,72 OK (HHHFFF)
Size 73: 37,73 OK (HHHFFF)
Size 74: 37,74 OK (HHHFFF)
Size 75: 38,75 OK (HHHFFF)
Size 76: 38,76 OK (HHHFFF)
Size 77: 39,77 OK (HHHFFF)
Size 78: 39,78 OK (HHHFFF)
Size 79: 40,79 OK (HHHFFF)
Size 80: 40,80 OK (HHHFFF)
Size 81: 41,81 OK (HHHFFF)
Size 82: 41,82 OK (HHHFFF)
Size 83: 42,83 OK (HHHFFF)
Size 84: 42,84 OK (HHHFFF)
Size 85: 43,85 OK (HHHFFF)
Size 86: 43,86 OK (HHHFFF)
Size 87: 44,87 OK (HHHFFF)
Size 88: 44,88 OK (HHHFFF)
Size 89: 45,89 OK (HHHFFF)
Size 90: 45,90 OK (HHHFFF)
Size 91: 46,91 OK (HHHFFF)
Size 92: 46,92 OK (HHHFFF)
Size 93: 47,93 OK (HHHFFF)
Size 94: 47,94 OK (HHHFFF)
Size 95: 48,95 OK (HHHFFF)
Size 96: 48,96 OK (HHHFFF)
Size 97: 49,97 OK (HHHFFF)
Size 98: 49,98 OK (HHHFFF)
Size 99: 50,99 OK (HHHFFF)
Size 100: 50,100 OK (HHHFFF)

View File

@ -0,0 +1,630 @@
==========================================================
Code Page 936, Chinese Simplified (China/PRC), SimSun font
==========================================================
Options: -face-simsun -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
Vista
-----
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (HHHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (HHHFHH)
Size 8: 4,9 GOOD (HHFFFF)
Size 9: 5,10 BAD (HHHFHH)
Size 10: 5,11 GOOD (HHFFFF)
Size 11: 6,13 BAD (HHHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,15 BAD (HHHFHH)
Size 14: 7,16 GOOD (HHFFFF)
Size 15: 8,17 BAD (HHHFHH)
Size 16: 8,18 GOOD (HHFFFF)
Size 17: 9,19 BAD (HHHFHH)
Size 18: 9,21 GOOD (HHFFFF)
Size 19: 10,22 BAD (HHHFHH)
Size 20: 10,23 GOOD (HHFFFF)
Size 21: 11,24 BAD (HHHFHH)
Size 22: 11,25 GOOD (HHFFFF)
Size 23: 12,26 BAD (HHHFHH)
Size 24: 12,27 GOOD (HHFFFF)
Size 25: 13,29 BAD (HHHFHH)
Size 26: 13,30 GOOD (HHFFFF)
Size 27: 14,31 BAD (HHHFHH)
Size 28: 14,32 GOOD (HHFFFF)
Size 29: 15,33 BAD (HHHFHH)
Size 30: 15,34 GOOD (HHFFFF)
Size 31: 16,35 BAD (HHHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,38 BAD (HHHFHH)
Size 34: 17,39 GOOD (HHFFFF)
Size 35: 18,40 BAD (HHHFHH)
Size 36: 18,41 GOOD (HHFFFF)
Size 37: 19,42 BAD (HHHFHH)
Size 38: 19,43 GOOD (HHFFFF)
Size 39: 20,44 BAD (HHHFHH)
Size 40: 20,46 GOOD (HHFFFF)
Size 41: 21,47 BAD (HHHFHH)
Size 42: 21,48 GOOD (HHFFFF)
Size 43: 22,49 BAD (HHHFHH)
Size 44: 22,50 GOOD (HHFFFF)
Size 45: 23,51 BAD (HHHFHH)
Size 46: 23,52 GOOD (HHFFFF)
Size 47: 24,54 BAD (HHHFHH)
Size 48: 24,55 GOOD (HHFFFF)
Size 49: 25,56 BAD (HHHFHH)
Size 50: 25,57 GOOD (HHFFFF)
Size 51: 26,58 BAD (HHHFHH)
Size 52: 26,59 GOOD (HHFFFF)
Size 53: 27,60 BAD (HHHFHH)
Size 54: 27,62 GOOD (HHFFFF)
Size 55: 28,63 BAD (HHHFHH)
Size 56: 28,64 GOOD (HHFFFF)
Size 57: 29,65 BAD (HHHFHH)
Size 58: 29,66 GOOD (HHFFFF)
Size 59: 30,67 BAD (HHHFHH)
Size 60: 30,68 GOOD (HHFFFF)
Size 61: 31,70 BAD (HHHFHH)
Size 62: 31,71 GOOD (HHFFFF)
Size 63: 32,72 BAD (HHHFHH)
Size 64: 32,73 GOOD (HHFFFF)
Size 65: 33,74 GOOD (HHFFFF)
Size 66: 33,75 GOOD (HHFFFF)
Size 67: 34,76 GOOD (HHFFFF)
Size 68: 34,78 GOOD (HHFFFF)
Size 69: 35,79 GOOD (HHFFFF)
Size 70: 35,80 GOOD (HHFFFF)
Size 71: 36,81 GOOD (HHFFFF)
Size 72: 36,82 GOOD (HHFFFF)
Size 73: 37,83 GOOD (HHFFFF)
Size 74: 37,84 GOOD (HHFFFF)
Size 75: 38,86 GOOD (HHFFFF)
Size 76: 38,87 GOOD (HHFFFF)
Size 77: 39,88 GOOD (HHFFFF)
Size 78: 39,89 GOOD (HHFFFF)
Size 79: 40,90 GOOD (HHFFFF)
Size 80: 40,91 GOOD (HHFFFF)
Size 81: 41,92 GOOD (HHFFFF)
Size 82: 41,94 GOOD (HHFFFF)
Size 83: 42,95 GOOD (HHFFFF)
Size 84: 42,96 GOOD (HHFFFF)
Size 85: 43,97 GOOD (HHFFFF)
Size 86: 43,98 GOOD (HHFFFF)
Size 87: 44,99 GOOD (HHFFFF)
Size 88: 44,100 GOOD (HHFFFF)
Size 89: 45,102 GOOD (HHFFFF)
Size 90: 45,103 GOOD (HHFFFF)
Size 91: 46,104 GOOD (HHFFFF)
Size 92: 46,105 GOOD (HHFFFF)
Size 93: 47,106 GOOD (HHFFFF)
Size 94: 47,107 GOOD (HHFFFF)
Size 95: 48,108 GOOD (HHFFFF)
Size 96: 48,111 GOOD (HHFFFF)
Size 97: 49,111 GOOD (HHFFFF)
Size 98: 49,112 GOOD (HHFFFF)
Size 99: 50,113 GOOD (HHFFFF)
Size 100: 50,114 GOOD (HHFFFF)
Windows 7
---------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,9 GOOD (HHFFFF)
Size 9: 5,10 BAD (FFHFHH)
Size 10: 5,11 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,15 BAD (FFHFHH)
Size 14: 7,16 GOOD (HHFFFF)
Size 15: 8,17 BAD (FFHFHH)
Size 16: 8,18 GOOD (HHFFFF)
Size 17: 9,19 BAD (FFHFHH)
Size 18: 9,21 GOOD (HHFFFF)
Size 19: 10,22 BAD (FFHFHH)
Size 20: 10,23 GOOD (HHFFFF)
Size 21: 11,24 BAD (FFHFHH)
Size 22: 11,25 GOOD (HHFFFF)
Size 23: 12,26 BAD (FFHFHH)
Size 24: 12,27 GOOD (HHFFFF)
Size 25: 13,29 BAD (FFHFHH)
Size 26: 13,30 GOOD (HHFFFF)
Size 27: 14,31 BAD (FFHFHH)
Size 28: 14,32 GOOD (HHFFFF)
Size 29: 15,33 BAD (FFHFHH)
Size 30: 15,34 GOOD (HHFFFF)
Size 31: 16,35 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,38 BAD (FFHFHH)
Size 34: 17,39 GOOD (HHFFFF)
Size 35: 18,40 BAD (FFHFHH)
Size 36: 18,41 GOOD (HHFFFF)
Size 37: 19,42 BAD (FFHFHH)
Size 38: 19,43 GOOD (HHFFFF)
Size 39: 20,44 BAD (FFHFHH)
Size 40: 20,46 GOOD (HHFFFF)
Size 41: 21,47 BAD (FFHFHH)
Size 42: 21,48 GOOD (HHFFFF)
Size 43: 22,49 BAD (FFHFHH)
Size 44: 22,50 GOOD (HHFFFF)
Size 45: 23,51 BAD (FFHFHH)
Size 46: 23,52 GOOD (HHFFFF)
Size 47: 24,54 BAD (FFHFHH)
Size 48: 24,55 GOOD (HHFFFF)
Size 49: 25,56 BAD (FFHFHH)
Size 50: 25,57 GOOD (HHFFFF)
Size 51: 26,58 BAD (FFHFHH)
Size 52: 26,59 GOOD (HHFFFF)
Size 53: 27,60 BAD (FFHFHH)
Size 54: 27,62 GOOD (HHFFFF)
Size 55: 28,63 BAD (FFHFHH)
Size 56: 28,64 GOOD (HHFFFF)
Size 57: 29,65 BAD (FFHFHH)
Size 58: 29,66 GOOD (HHFFFF)
Size 59: 30,67 BAD (FFHFHH)
Size 60: 30,68 GOOD (HHFFFF)
Size 61: 31,70 BAD (FFHFHH)
Size 62: 31,71 GOOD (HHFFFF)
Size 63: 32,72 BAD (FFHFHH)
Size 64: 32,73 GOOD (HHFFFF)
Size 65: 33,74 GOOD (HHFFFF)
Size 66: 33,75 GOOD (HHFFFF)
Size 67: 34,76 GOOD (HHFFFF)
Size 68: 34,78 GOOD (HHFFFF)
Size 69: 35,79 GOOD (HHFFFF)
Size 70: 35,80 GOOD (HHFFFF)
Size 71: 36,81 GOOD (HHFFFF)
Size 72: 36,82 GOOD (HHFFFF)
Size 73: 37,83 GOOD (HHFFFF)
Size 74: 37,84 GOOD (HHFFFF)
Size 75: 38,86 GOOD (HHFFFF)
Size 76: 38,87 GOOD (HHFFFF)
Size 77: 39,88 GOOD (HHFFFF)
Size 78: 39,89 GOOD (HHFFFF)
Size 79: 40,90 GOOD (HHFFFF)
Size 80: 40,91 GOOD (HHFFFF)
Size 81: 41,92 GOOD (HHFFFF)
Size 82: 41,94 GOOD (HHFFFF)
Size 83: 42,95 GOOD (HHFFFF)
Size 84: 42,96 GOOD (HHFFFF)
Size 85: 43,97 GOOD (HHFFFF)
Size 86: 43,98 GOOD (HHFFFF)
Size 87: 44,99 GOOD (HHFFFF)
Size 88: 44,100 GOOD (HHFFFF)
Size 89: 45,102 GOOD (HHFFFF)
Size 90: 45,103 GOOD (HHFFFF)
Size 91: 46,104 GOOD (HHFFFF)
Size 92: 46,105 GOOD (HHFFFF)
Size 93: 47,106 GOOD (HHFFFF)
Size 94: 47,107 GOOD (HHFFFF)
Size 95: 48,108 GOOD (HHFFFF)
Size 96: 48,111 GOOD (HHFFFF)
Size 97: 49,111 GOOD (HHFFFF)
Size 98: 49,112 GOOD (HHFFFF)
Size 99: 50,113 GOOD (HHFFFF)
Size 100: 50,114 GOOD (HHFFFF)
Windows 8
---------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,9 GOOD (HHFFFF)
Size 9: 5,10 BAD (FFHFHH)
Size 10: 5,11 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,15 BAD (FFHFHH)
Size 14: 7,16 GOOD (HHFFFF)
Size 15: 8,17 BAD (FFHFHH)
Size 16: 8,18 GOOD (HHFFFF)
Size 17: 9,19 BAD (FFHFHH)
Size 18: 9,21 GOOD (HHFFFF)
Size 19: 10,22 BAD (FFHFHH)
Size 20: 10,23 GOOD (HHFFFF)
Size 21: 11,24 BAD (FFHFHH)
Size 22: 11,25 GOOD (HHFFFF)
Size 23: 12,26 BAD (FFHFHH)
Size 24: 12,27 GOOD (HHFFFF)
Size 25: 13,29 BAD (FFHFHH)
Size 26: 13,30 GOOD (HHFFFF)
Size 27: 14,31 BAD (FFHFHH)
Size 28: 14,32 GOOD (HHFFFF)
Size 29: 15,33 BAD (FFHFHH)
Size 30: 15,34 GOOD (HHFFFF)
Size 31: 16,35 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,38 BAD (FFHFHH)
Size 34: 17,39 GOOD (HHFFFF)
Size 35: 18,40 BAD (FFHFHH)
Size 36: 18,41 GOOD (HHFFFF)
Size 37: 19,42 BAD (FFHFHH)
Size 38: 19,43 GOOD (HHFFFF)
Size 39: 20,44 BAD (FFHFHH)
Size 40: 20,46 GOOD (HHFFFF)
Size 41: 21,47 BAD (FFHFHH)
Size 42: 21,48 GOOD (HHFFFF)
Size 43: 22,49 BAD (FFHFHH)
Size 44: 22,50 GOOD (HHFFFF)
Size 45: 23,51 BAD (FFHFHH)
Size 46: 23,52 GOOD (HHFFFF)
Size 47: 24,54 BAD (FFHFHH)
Size 48: 24,55 GOOD (HHFFFF)
Size 49: 25,56 BAD (FFHFHH)
Size 50: 25,57 GOOD (HHFFFF)
Size 51: 26,58 BAD (FFHFHH)
Size 52: 26,59 GOOD (HHFFFF)
Size 53: 27,60 BAD (FFHFHH)
Size 54: 27,62 GOOD (HHFFFF)
Size 55: 28,63 BAD (FFHFHH)
Size 56: 28,64 GOOD (HHFFFF)
Size 57: 29,65 BAD (FFHFHH)
Size 58: 29,66 GOOD (HHFFFF)
Size 59: 30,67 BAD (FFHFHH)
Size 60: 30,68 GOOD (HHFFFF)
Size 61: 31,70 BAD (FFHFHH)
Size 62: 31,71 GOOD (HHFFFF)
Size 63: 32,72 BAD (FFHFHH)
Size 64: 32,73 GOOD (HHFFFF)
Size 65: 33,74 GOOD (HHFFFF)
Size 66: 33,75 GOOD (HHFFFF)
Size 67: 34,76 GOOD (HHFFFF)
Size 68: 34,78 GOOD (HHFFFF)
Size 69: 35,79 GOOD (HHFFFF)
Size 70: 35,80 GOOD (HHFFFF)
Size 71: 36,81 GOOD (HHFFFF)
Size 72: 36,82 GOOD (HHFFFF)
Size 73: 37,83 GOOD (HHFFFF)
Size 74: 37,84 GOOD (HHFFFF)
Size 75: 38,86 GOOD (HHFFFF)
Size 76: 38,87 GOOD (HHFFFF)
Size 77: 39,88 GOOD (HHFFFF)
Size 78: 39,89 GOOD (HHFFFF)
Size 79: 40,90 GOOD (HHFFFF)
Size 80: 40,91 GOOD (HHFFFF)
Size 81: 41,92 GOOD (HHFFFF)
Size 82: 41,94 GOOD (HHFFFF)
Size 83: 42,95 GOOD (HHFFFF)
Size 84: 42,96 GOOD (HHFFFF)
Size 85: 43,97 GOOD (HHFFFF)
Size 86: 43,98 GOOD (HHFFFF)
Size 87: 44,99 GOOD (HHFFFF)
Size 88: 44,100 GOOD (HHFFFF)
Size 89: 45,102 GOOD (HHFFFF)
Size 90: 45,103 GOOD (HHFFFF)
Size 91: 46,104 GOOD (HHFFFF)
Size 92: 46,105 GOOD (HHFFFF)
Size 93: 47,106 GOOD (HHFFFF)
Size 94: 47,107 GOOD (HHFFFF)
Size 95: 48,108 GOOD (HHFFFF)
Size 96: 48,111 GOOD (HHFFFF)
Size 97: 49,111 GOOD (HHFFFF)
Size 98: 49,112 GOOD (HHFFFF)
Size 99: 50,113 GOOD (HHFFFF)
Size 100: 50,114 GOOD (HHFFFF)
Windows 8.1
-----------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,9 GOOD (HHFFFF)
Size 9: 5,10 BAD (FFHFHH)
Size 10: 5,11 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,15 BAD (FFHFHH)
Size 14: 7,16 GOOD (HHFFFF)
Size 15: 8,17 BAD (FFHFHH)
Size 16: 8,18 GOOD (HHFFFF)
Size 17: 9,19 BAD (FFHFHH)
Size 18: 9,21 GOOD (HHFFFF)
Size 19: 10,22 BAD (FFHFHH)
Size 20: 10,23 GOOD (HHFFFF)
Size 21: 11,24 BAD (FFHFHH)
Size 22: 11,25 GOOD (HHFFFF)
Size 23: 12,26 BAD (FFHFHH)
Size 24: 12,27 GOOD (HHFFFF)
Size 25: 13,29 BAD (FFHFHH)
Size 26: 13,30 GOOD (HHFFFF)
Size 27: 14,31 BAD (FFHFHH)
Size 28: 14,32 GOOD (HHFFFF)
Size 29: 15,33 BAD (FFHFHH)
Size 30: 15,34 GOOD (HHFFFF)
Size 31: 16,35 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,38 BAD (FFHFHH)
Size 34: 17,39 GOOD (HHFFFF)
Size 35: 18,40 BAD (FFHFHH)
Size 36: 18,41 GOOD (HHFFFF)
Size 37: 19,42 BAD (FFHFHH)
Size 38: 19,43 GOOD (HHFFFF)
Size 39: 20,44 BAD (FFHFHH)
Size 40: 20,46 GOOD (HHFFFF)
Size 41: 21,47 BAD (FFHFHH)
Size 42: 21,48 GOOD (HHFFFF)
Size 43: 22,49 BAD (FFHFHH)
Size 44: 22,50 GOOD (HHFFFF)
Size 45: 23,51 BAD (FFHFHH)
Size 46: 23,52 GOOD (HHFFFF)
Size 47: 24,54 BAD (FFHFHH)
Size 48: 24,55 GOOD (HHFFFF)
Size 49: 25,56 BAD (FFHFHH)
Size 50: 25,57 GOOD (HHFFFF)
Size 51: 26,58 BAD (FFHFHH)
Size 52: 26,59 GOOD (HHFFFF)
Size 53: 27,60 BAD (FFHFHH)
Size 54: 27,62 GOOD (HHFFFF)
Size 55: 28,63 BAD (FFHFHH)
Size 56: 28,64 GOOD (HHFFFF)
Size 57: 29,65 BAD (FFHFHH)
Size 58: 29,66 GOOD (HHFFFF)
Size 59: 30,67 BAD (FFHFHH)
Size 60: 30,68 GOOD (HHFFFF)
Size 61: 31,70 BAD (FFHFHH)
Size 62: 31,71 GOOD (HHFFFF)
Size 63: 32,72 BAD (FFHFHH)
Size 64: 32,73 GOOD (HHFFFF)
Size 65: 33,74 GOOD (HHFFFF)
Size 66: 33,75 GOOD (HHFFFF)
Size 67: 34,76 GOOD (HHFFFF)
Size 68: 34,78 GOOD (HHFFFF)
Size 69: 35,79 GOOD (HHFFFF)
Size 70: 35,80 GOOD (HHFFFF)
Size 71: 36,81 GOOD (HHFFFF)
Size 72: 36,82 GOOD (HHFFFF)
Size 73: 37,83 GOOD (HHFFFF)
Size 74: 37,84 GOOD (HHFFFF)
Size 75: 38,86 GOOD (HHFFFF)
Size 76: 38,87 GOOD (HHFFFF)
Size 77: 39,88 GOOD (HHFFFF)
Size 78: 39,89 GOOD (HHFFFF)
Size 79: 40,90 GOOD (HHFFFF)
Size 80: 40,91 GOOD (HHFFFF)
Size 81: 41,92 GOOD (HHFFFF)
Size 82: 41,94 GOOD (HHFFFF)
Size 83: 42,95 GOOD (HHFFFF)
Size 84: 42,96 GOOD (HHFFFF)
Size 85: 43,97 GOOD (HHFFFF)
Size 86: 43,98 GOOD (HHFFFF)
Size 87: 44,99 GOOD (HHFFFF)
Size 88: 44,100 GOOD (HHFFFF)
Size 89: 45,102 GOOD (HHFFFF)
Size 90: 45,103 GOOD (HHFFFF)
Size 91: 46,104 GOOD (HHFFFF)
Size 92: 46,105 GOOD (HHFFFF)
Size 93: 47,106 GOOD (HHFFFF)
Size 94: 47,107 GOOD (HHFFFF)
Size 95: 48,108 GOOD (HHFFFF)
Size 96: 48,111 GOOD (HHFFFF)
Size 97: 49,111 GOOD (HHFFFF)
Size 98: 49,112 GOOD (HHFFFF)
Size 99: 50,113 GOOD (HHFFFF)
Size 100: 50,114 GOOD (HHFFFF)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,9 GOOD (HHFFFF)
Size 9: 5,10 BAD (FFHFHH)
Size 10: 5,11 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,15 BAD (FFHFHH)
Size 14: 7,16 GOOD (HHFFFF)
Size 15: 8,17 BAD (FFHFHH)
Size 16: 8,18 GOOD (HHFFFF)
Size 17: 9,19 BAD (FFHFHH)
Size 18: 9,21 GOOD (HHFFFF)
Size 19: 10,22 BAD (FFHFHH)
Size 20: 10,23 GOOD (HHFFFF)
Size 21: 11,24 BAD (FFHFHH)
Size 22: 11,25 GOOD (HHFFFF)
Size 23: 12,26 BAD (FFHFHH)
Size 24: 12,27 GOOD (HHFFFF)
Size 25: 13,29 BAD (FFHFHH)
Size 26: 13,30 GOOD (HHFFFF)
Size 27: 14,31 BAD (FFHFHH)
Size 28: 14,32 GOOD (HHFFFF)
Size 29: 15,33 BAD (FFHFHH)
Size 30: 15,34 GOOD (HHFFFF)
Size 31: 16,35 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,38 BAD (FFHFHH)
Size 34: 17,39 GOOD (HHFFFF)
Size 35: 18,40 BAD (FFHFHH)
Size 36: 18,41 GOOD (HHFFFF)
Size 37: 19,42 BAD (FFHFHH)
Size 38: 19,43 GOOD (HHFFFF)
Size 39: 20,44 BAD (FFHFHH)
Size 40: 20,46 GOOD (HHFFFF)
Size 41: 21,47 BAD (FFHFHH)
Size 42: 21,48 GOOD (HHFFFF)
Size 43: 22,49 BAD (FFHFHH)
Size 44: 22,50 GOOD (HHFFFF)
Size 45: 23,51 BAD (FFHFHH)
Size 46: 23,52 GOOD (HHFFFF)
Size 47: 24,54 BAD (FFHFHH)
Size 48: 24,55 GOOD (HHFFFF)
Size 49: 25,56 BAD (FFHFHH)
Size 50: 25,57 GOOD (HHFFFF)
Size 51: 26,58 BAD (FFHFHH)
Size 52: 26,59 GOOD (HHFFFF)
Size 53: 27,60 BAD (FFHFHH)
Size 54: 27,62 GOOD (HHFFFF)
Size 55: 28,63 BAD (FFHFHH)
Size 56: 28,64 GOOD (HHFFFF)
Size 57: 29,65 BAD (FFHFHH)
Size 58: 29,66 GOOD (HHFFFF)
Size 59: 30,67 BAD (FFHFHH)
Size 60: 30,68 GOOD (HHFFFF)
Size 61: 31,70 BAD (FFHFHH)
Size 62: 31,71 GOOD (HHFFFF)
Size 63: 32,72 BAD (FFHFHH)
Size 64: 32,73 GOOD (HHFFFF)
Size 65: 33,74 GOOD (HHFFFF)
Size 66: 33,75 GOOD (HHFFFF)
Size 67: 34,76 GOOD (HHFFFF)
Size 68: 34,78 GOOD (HHFFFF)
Size 69: 35,79 GOOD (HHFFFF)
Size 70: 35,80 GOOD (HHFFFF)
Size 71: 36,81 GOOD (HHFFFF)
Size 72: 36,82 GOOD (HHFFFF)
Size 73: 37,83 GOOD (HHFFFF)
Size 74: 37,84 GOOD (HHFFFF)
Size 75: 38,86 GOOD (HHFFFF)
Size 76: 38,87 GOOD (HHFFFF)
Size 77: 39,88 GOOD (HHFFFF)
Size 78: 39,89 GOOD (HHFFFF)
Size 79: 40,90 GOOD (HHFFFF)
Size 80: 40,91 GOOD (HHFFFF)
Size 81: 41,92 GOOD (HHFFFF)
Size 82: 41,94 GOOD (HHFFFF)
Size 83: 42,95 GOOD (HHFFFF)
Size 84: 42,96 GOOD (HHFFFF)
Size 85: 43,97 GOOD (HHFFFF)
Size 86: 43,98 GOOD (HHFFFF)
Size 87: 44,99 GOOD (HHFFFF)
Size 88: 44,100 GOOD (HHFFFF)
Size 89: 45,102 GOOD (HHFFFF)
Size 90: 45,103 GOOD (HHFFFF)
Size 91: 46,104 GOOD (HHFFFF)
Size 92: 46,105 GOOD (HHFFFF)
Size 93: 47,106 GOOD (HHFFFF)
Size 94: 47,107 GOOD (HHFFFF)
Size 95: 48,108 GOOD (HHFFFF)
Size 96: 48,111 GOOD (HHFFFF)
Size 97: 49,111 GOOD (HHFFFF)
Size 98: 49,112 GOOD (HHFFFF)
Size 99: 50,113 GOOD (HHFFFF)
Size 100: 50,114 GOOD (HHFFFF)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 GOOD (HHFFFF)
Size 4: 2,4 GOOD (HHFFFF)
Size 5: 3,5 GOOD (HHFFFF)
Size 6: 3,6 GOOD (HHFFFF)
Size 7: 4,7 GOOD (HHFFFF)
Size 8: 4,8 GOOD (HHFFFF)
Size 9: 5,9 GOOD (HHFFFF)
Size 10: 5,10 GOOD (HHFFFF)
Size 11: 6,11 GOOD (HHFFFF)
Size 12: 6,12 GOOD (HHFFFF)
Size 13: 7,13 GOOD (HHFFFF)
Size 14: 7,14 GOOD (HHFFFF)
Size 15: 8,15 GOOD (HHFFFF)
Size 16: 8,16 GOOD (HHFFFF)
Size 17: 9,17 GOOD (HHFFFF)
Size 18: 9,18 GOOD (HHFFFF)
Size 19: 10,19 GOOD (HHFFFF)
Size 20: 10,20 GOOD (HHFFFF)
Size 21: 11,21 GOOD (HHFFFF)
Size 22: 11,22 GOOD (HHFFFF)
Size 23: 12,23 GOOD (HHFFFF)
Size 24: 12,24 GOOD (HHFFFF)
Size 25: 13,25 GOOD (HHFFFF)
Size 26: 13,26 GOOD (HHFFFF)
Size 27: 14,27 GOOD (HHFFFF)
Size 28: 14,28 GOOD (HHFFFF)
Size 29: 15,29 GOOD (HHFFFF)
Size 30: 15,30 GOOD (HHFFFF)
Size 31: 16,31 GOOD (HHFFFF)
Size 32: 16,32 GOOD (HHFFFF)
Size 33: 17,33 GOOD (HHFFFF)
Size 34: 17,34 GOOD (HHFFFF)
Size 35: 18,35 GOOD (HHFFFF)
Size 36: 18,36 GOOD (HHFFFF)
Size 37: 19,37 GOOD (HHFFFF)
Size 38: 19,38 GOOD (HHFFFF)
Size 39: 20,39 GOOD (HHFFFF)
Size 40: 20,40 GOOD (HHFFFF)
Size 41: 21,41 GOOD (HHFFFF)
Size 42: 21,42 GOOD (HHFFFF)
Size 43: 22,43 GOOD (HHFFFF)
Size 44: 22,44 GOOD (HHFFFF)
Size 45: 23,45 GOOD (HHFFFF)
Size 46: 23,46 GOOD (HHFFFF)
Size 47: 24,47 GOOD (HHFFFF)
Size 48: 24,48 GOOD (HHFFFF)
Size 49: 25,49 GOOD (HHFFFF)
Size 50: 25,50 GOOD (HHFFFF)
Size 51: 26,51 GOOD (HHFFFF)
Size 52: 26,52 GOOD (HHFFFF)
Size 53: 27,53 GOOD (HHFFFF)
Size 54: 27,54 GOOD (HHFFFF)
Size 55: 28,55 GOOD (HHFFFF)
Size 56: 28,56 GOOD (HHFFFF)
Size 57: 29,57 GOOD (HHFFFF)
Size 58: 29,58 GOOD (HHFFFF)
Size 59: 30,59 GOOD (HHFFFF)
Size 60: 30,60 GOOD (HHFFFF)
Size 61: 31,61 GOOD (HHFFFF)
Size 62: 31,62 GOOD (HHFFFF)
Size 63: 32,63 GOOD (HHFFFF)
Size 64: 32,64 GOOD (HHFFFF)
Size 65: 33,65 GOOD (HHFFFF)
Size 66: 33,66 GOOD (HHFFFF)
Size 67: 34,67 GOOD (HHFFFF)
Size 68: 34,68 GOOD (HHFFFF)
Size 69: 35,69 GOOD (HHFFFF)
Size 70: 35,70 GOOD (HHFFFF)
Size 71: 36,71 GOOD (HHFFFF)
Size 72: 36,72 GOOD (HHFFFF)
Size 73: 37,73 GOOD (HHFFFF)
Size 74: 37,74 GOOD (HHFFFF)
Size 75: 38,75 GOOD (HHFFFF)
Size 76: 38,76 GOOD (HHFFFF)
Size 77: 39,77 GOOD (HHFFFF)
Size 78: 39,78 GOOD (HHFFFF)
Size 79: 40,79 GOOD (HHFFFF)
Size 80: 40,80 GOOD (HHFFFF)
Size 81: 41,81 GOOD (HHFFFF)
Size 82: 41,82 GOOD (HHFFFF)
Size 83: 42,83 GOOD (HHFFFF)
Size 84: 42,84 GOOD (HHFFFF)
Size 85: 43,85 GOOD (HHFFFF)
Size 86: 43,86 GOOD (HHFFFF)
Size 87: 44,87 GOOD (HHFFFF)
Size 88: 44,88 GOOD (HHFFFF)
Size 89: 45,89 GOOD (HHFFFF)
Size 90: 45,90 GOOD (HHFFFF)
Size 91: 46,91 GOOD (HHFFFF)
Size 92: 46,92 GOOD (HHFFFF)
Size 93: 47,93 GOOD (HHFFFF)
Size 94: 47,94 GOOD (HHFFFF)
Size 95: 48,95 GOOD (HHFFFF)
Size 96: 48,96 GOOD (HHFFFF)
Size 97: 49,97 GOOD (HHFFFF)
Size 98: 49,98 GOOD (HHFFFF)
Size 99: 50,99 GOOD (HHFFFF)
Size 100: 50,100 GOOD (HHFFFF)

View File

@ -0,0 +1,630 @@
=====================================
Code Page 949, Korean, GulimChe font
=====================================
Options: -face-gulimche -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
Vista
-----
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,5 OK (HHHFFF)
Size 5: 3,6 BAD (HHHFHH)
Size 6: 3,7 OK (HHHFFF)
Size 7: 4,8 BAD (HHHFHH)
Size 8: 4,9 OK (HHHFFF)
Size 9: 5,10 BAD (HHHFHH)
Size 10: 5,11 OK (HHHFFF)
Size 11: 6,13 BAD (HHHFHH)
Size 12: 6,14 OK (HHHFFF)
Size 13: 7,15 BAD (HHHFHH)
Size 14: 7,16 OK (HHHFFF)
Size 15: 8,17 BAD (HHHFHH)
Size 16: 8,18 OK (HHHFFF)
Size 17: 9,20 BAD (HHHFHH)
Size 18: 9,21 OK (HHHFFF)
Size 19: 10,22 BAD (HHHFHH)
Size 20: 10,23 OK (HHHFFF)
Size 21: 11,24 BAD (HHHFHH)
Size 22: 11,25 OK (HHHFFF)
Size 23: 12,26 BAD (HHHFHH)
Size 24: 12,28 OK (HHHFFF)
Size 25: 13,29 BAD (HHHFHH)
Size 26: 13,30 OK (HHHFFF)
Size 27: 14,31 BAD (HHHFHH)
Size 28: 14,32 OK (HHHFFF)
Size 29: 15,33 BAD (HHHFHH)
Size 30: 15,34 OK (HHHFFF)
Size 31: 16,36 BAD (HHHFHH)
Size 32: 16,37 OK (HHHFFF)
Size 33: 17,38 BAD (HHHFHH)
Size 34: 17,39 OK (HHHFFF)
Size 35: 18,40 BAD (HHHFHH)
Size 36: 18,41 OK (HHHFFF)
Size 37: 19,42 BAD (HHHFHH)
Size 38: 19,44 OK (HHHFFF)
Size 39: 20,45 BAD (HHHFHH)
Size 40: 20,46 OK (HHHFFF)
Size 41: 21,47 BAD (HHHFHH)
Size 42: 21,48 OK (HHHFFF)
Size 43: 22,49 BAD (HHHFHH)
Size 44: 22,51 OK (HHHFFF)
Size 45: 23,52 BAD (HHHFHH)
Size 46: 23,53 OK (HHHFFF)
Size 47: 24,54 BAD (HHHFHH)
Size 48: 24,55 OK (HHHFFF)
Size 49: 25,56 BAD (HHHFHH)
Size 50: 25,57 OK (HHHFFF)
Size 51: 26,59 BAD (HHHFHH)
Size 52: 26,60 OK (HHHFFF)
Size 53: 27,61 BAD (HHHFHH)
Size 54: 27,62 OK (HHHFFF)
Size 55: 28,63 BAD (HHHFHH)
Size 56: 28,64 OK (HHHFFF)
Size 57: 29,65 BAD (HHHFHH)
Size 58: 29,67 OK (HHHFFF)
Size 59: 30,68 BAD (HHHFHH)
Size 60: 30,69 OK (HHHFFF)
Size 61: 31,70 BAD (HHHFHH)
Size 62: 31,71 OK (HHHFFF)
Size 63: 32,72 BAD (HHHFHH)
Size 64: 32,74 OK (HHHFFF)
Size 65: 33,75 BAD (HHHFHH)
Size 66: 33,76 OK (HHHFFF)
Size 67: 34,77 BAD (HHHFHH)
Size 68: 34,78 OK (HHHFFF)
Size 69: 35,79 BAD (HHHFHH)
Size 70: 35,80 OK (HHHFFF)
Size 71: 36,82 BAD (HHHFHH)
Size 72: 36,83 OK (HHHFFF)
Size 73: 37,84 BAD (HHHFHH)
Size 74: 37,85 OK (HHHFFF)
Size 75: 38,86 BAD (HHHFHH)
Size 76: 38,87 OK (HHHFFF)
Size 77: 39,88 BAD (HHHFHH)
Size 78: 39,90 OK (HHHFFF)
Size 79: 40,91 BAD (HHHFHH)
Size 80: 40,92 OK (HHHFFF)
Size 81: 41,93 BAD (HHHFHH)
Size 82: 41,94 OK (HHHFFF)
Size 83: 42,95 BAD (HHHFHH)
Size 84: 42,96 OK (HHHFFF)
Size 85: 43,98 BAD (HHHFHH)
Size 86: 43,99 OK (HHHFFF)
Size 87: 44,100 BAD (HHHFHH)
Size 88: 44,101 OK (HHHFFF)
Size 89: 45,102 BAD (HHHFHH)
Size 90: 45,103 OK (HHHFFF)
Size 91: 46,105 BAD (HHHFHH)
Size 92: 46,106 OK (HHHFFF)
Size 93: 47,107 BAD (HHHFHH)
Size 94: 47,108 OK (HHHFFF)
Size 95: 48,109 BAD (HHHFHH)
Size 96: 48,110 OK (HHHFFF)
Size 97: 49,111 BAD (HHHFHH)
Size 98: 49,113 OK (HHHFFF)
Size 99: 50,114 BAD (HHHFHH)
Size 100: 50,115 OK (HHHFFF)
Windows 7
---------
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,5 OK (HHHFFF)
Size 5: 3,6 BAD (FFFFHH)
Size 6: 3,7 OK (HHHFFF)
Size 7: 4,8 BAD (FFFFHH)
Size 8: 4,9 OK (HHHFFF)
Size 9: 5,10 BAD (FFFFHH)
Size 10: 5,11 OK (HHHFFF)
Size 11: 6,13 BAD (FFFFHH)
Size 12: 6,14 OK (HHHFFF)
Size 13: 7,15 BAD (FFFFHH)
Size 14: 7,16 OK (HHHFFF)
Size 15: 8,17 BAD (FFFFHH)
Size 16: 8,18 OK (HHHFFF)
Size 17: 9,20 BAD (FFFFHH)
Size 18: 9,21 OK (HHHFFF)
Size 19: 10,22 BAD (FFFFHH)
Size 20: 10,23 OK (HHHFFF)
Size 21: 11,24 BAD (FFFFHH)
Size 22: 11,25 OK (HHHFFF)
Size 23: 12,26 BAD (FFFFHH)
Size 24: 12,28 OK (HHHFFF)
Size 25: 13,29 BAD (FFFFHH)
Size 26: 13,30 OK (HHHFFF)
Size 27: 14,31 BAD (FFFFHH)
Size 28: 14,32 OK (HHHFFF)
Size 29: 15,33 BAD (FFFFHH)
Size 30: 15,34 OK (HHHFFF)
Size 31: 16,36 BAD (FFFFHH)
Size 32: 16,37 OK (HHHFFF)
Size 33: 17,38 BAD (FFFFHH)
Size 34: 17,39 OK (HHHFFF)
Size 35: 18,40 BAD (FFFFHH)
Size 36: 18,41 OK (HHHFFF)
Size 37: 19,42 BAD (FFFFHH)
Size 38: 19,44 OK (HHHFFF)
Size 39: 20,45 BAD (FFFFHH)
Size 40: 20,46 OK (HHHFFF)
Size 41: 21,47 BAD (FFFFHH)
Size 42: 21,48 OK (HHHFFF)
Size 43: 22,49 BAD (FFFFHH)
Size 44: 22,51 OK (HHHFFF)
Size 45: 23,52 BAD (FFFFHH)
Size 46: 23,53 OK (HHHFFF)
Size 47: 24,54 BAD (FFFFHH)
Size 48: 24,55 OK (HHHFFF)
Size 49: 25,56 BAD (FFFFHH)
Size 50: 25,57 OK (HHHFFF)
Size 51: 26,59 BAD (FFFFHH)
Size 52: 26,60 OK (HHHFFF)
Size 53: 27,61 BAD (FFFFHH)
Size 54: 27,62 OK (HHHFFF)
Size 55: 28,63 BAD (FFFFHH)
Size 56: 28,64 OK (HHHFFF)
Size 57: 29,65 BAD (FFFFHH)
Size 58: 29,67 OK (HHHFFF)
Size 59: 30,68 BAD (FFFFHH)
Size 60: 30,69 OK (HHHFFF)
Size 61: 31,70 BAD (FFFFHH)
Size 62: 31,71 OK (HHHFFF)
Size 63: 32,72 BAD (FFFFHH)
Size 64: 32,74 OK (HHHFFF)
Size 65: 33,75 BAD (FFFFHH)
Size 66: 33,76 OK (HHHFFF)
Size 67: 34,77 BAD (FFFFHH)
Size 68: 34,78 OK (HHHFFF)
Size 69: 35,79 BAD (FFFFHH)
Size 70: 35,80 OK (HHHFFF)
Size 71: 36,82 BAD (FFFFHH)
Size 72: 36,83 OK (HHHFFF)
Size 73: 37,84 BAD (FFFFHH)
Size 74: 37,85 OK (HHHFFF)
Size 75: 38,86 BAD (FFFFHH)
Size 76: 38,87 OK (HHHFFF)
Size 77: 39,88 BAD (FFFFHH)
Size 78: 39,90 OK (HHHFFF)
Size 79: 40,91 BAD (FFFFHH)
Size 80: 40,92 OK (HHHFFF)
Size 81: 41,93 BAD (FFFFHH)
Size 82: 41,94 OK (HHHFFF)
Size 83: 42,95 BAD (FFFFHH)
Size 84: 42,96 OK (HHHFFF)
Size 85: 43,98 BAD (FFFFHH)
Size 86: 43,99 OK (HHHFFF)
Size 87: 44,100 BAD (FFFFHH)
Size 88: 44,101 OK (HHHFFF)
Size 89: 45,102 BAD (FFFFHH)
Size 90: 45,103 OK (HHHFFF)
Size 91: 46,105 BAD (FFFFHH)
Size 92: 46,106 OK (HHHFFF)
Size 93: 47,107 BAD (FFFFHH)
Size 94: 47,108 OK (HHHFFF)
Size 95: 48,109 BAD (FFFFHH)
Size 96: 48,110 OK (HHHFFF)
Size 97: 49,111 BAD (FFFFHH)
Size 98: 49,113 OK (HHHFFF)
Size 99: 50,114 BAD (FFFFHH)
Size 100: 50,115 OK (HHHFFF)
Windows 8
---------
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,5 OK (HHHFFF)
Size 5: 3,6 BAD (FFFFHH)
Size 6: 3,7 OK (HHHFFF)
Size 7: 4,8 BAD (FFFFHH)
Size 8: 4,9 OK (HHHFFF)
Size 9: 5,10 BAD (FFFFHH)
Size 10: 5,11 OK (HHHFFF)
Size 11: 6,13 BAD (FFFFHH)
Size 12: 6,14 OK (HHHFFF)
Size 13: 7,15 BAD (FFFFHH)
Size 14: 7,16 OK (HHHFFF)
Size 15: 8,17 BAD (FFFFHH)
Size 16: 8,18 OK (HHHFFF)
Size 17: 9,20 BAD (FFFFHH)
Size 18: 9,21 OK (HHHFFF)
Size 19: 10,22 BAD (FFFFHH)
Size 20: 10,23 OK (HHHFFF)
Size 21: 11,24 BAD (FFFFHH)
Size 22: 11,25 OK (HHHFFF)
Size 23: 12,26 BAD (FFFFHH)
Size 24: 12,28 OK (HHHFFF)
Size 25: 13,29 BAD (FFFFHH)
Size 26: 13,30 OK (HHHFFF)
Size 27: 14,31 BAD (FFFFHH)
Size 28: 14,32 OK (HHHFFF)
Size 29: 15,33 BAD (FFFFHH)
Size 30: 15,34 OK (HHHFFF)
Size 31: 16,36 BAD (FFFFHH)
Size 32: 16,37 OK (HHHFFF)
Size 33: 17,38 BAD (FFFFHH)
Size 34: 17,39 OK (HHHFFF)
Size 35: 18,40 BAD (FFFFHH)
Size 36: 18,41 OK (HHHFFF)
Size 37: 19,42 BAD (FFFFHH)
Size 38: 19,44 OK (HHHFFF)
Size 39: 20,45 BAD (FFFFHH)
Size 40: 20,46 OK (HHHFFF)
Size 41: 21,47 BAD (FFFFHH)
Size 42: 21,48 OK (HHHFFF)
Size 43: 22,49 BAD (FFFFHH)
Size 44: 22,51 OK (HHHFFF)
Size 45: 23,52 BAD (FFFFHH)
Size 46: 23,53 OK (HHHFFF)
Size 47: 24,54 BAD (FFFFHH)
Size 48: 24,55 OK (HHHFFF)
Size 49: 25,56 BAD (FFFFHH)
Size 50: 25,57 OK (HHHFFF)
Size 51: 26,59 BAD (FFFFHH)
Size 52: 26,60 OK (HHHFFF)
Size 53: 27,61 BAD (FFFFHH)
Size 54: 27,62 OK (HHHFFF)
Size 55: 28,63 BAD (FFFFHH)
Size 56: 28,64 OK (HHHFFF)
Size 57: 29,65 BAD (FFFFHH)
Size 58: 29,67 OK (HHHFFF)
Size 59: 30,68 BAD (FFFFHH)
Size 60: 30,69 OK (HHHFFF)
Size 61: 31,70 BAD (FFFFHH)
Size 62: 31,71 OK (HHHFFF)
Size 63: 32,72 BAD (FFFFHH)
Size 64: 32,74 OK (HHHFFF)
Size 65: 33,75 BAD (FFFFHH)
Size 66: 33,76 OK (HHHFFF)
Size 67: 34,77 BAD (FFFFHH)
Size 68: 34,78 OK (HHHFFF)
Size 69: 35,79 BAD (FFFFHH)
Size 70: 35,80 OK (HHHFFF)
Size 71: 36,82 BAD (FFFFHH)
Size 72: 36,83 OK (HHHFFF)
Size 73: 37,84 BAD (FFFFHH)
Size 74: 37,85 OK (HHHFFF)
Size 75: 38,86 BAD (FFFFHH)
Size 76: 38,87 OK (HHHFFF)
Size 77: 39,88 BAD (FFFFHH)
Size 78: 39,90 OK (HHHFFF)
Size 79: 40,91 BAD (FFFFHH)
Size 80: 40,92 OK (HHHFFF)
Size 81: 41,93 BAD (FFFFHH)
Size 82: 41,94 OK (HHHFFF)
Size 83: 42,95 BAD (FFFFHH)
Size 84: 42,96 OK (HHHFFF)
Size 85: 43,98 BAD (FFFFHH)
Size 86: 43,99 OK (HHHFFF)
Size 87: 44,100 BAD (FFFFHH)
Size 88: 44,101 OK (HHHFFF)
Size 89: 45,102 BAD (FFFFHH)
Size 90: 45,103 OK (HHHFFF)
Size 91: 46,105 BAD (FFFFHH)
Size 92: 46,106 OK (HHHFFF)
Size 93: 47,107 BAD (FFFFHH)
Size 94: 47,108 OK (HHHFFF)
Size 95: 48,109 BAD (FFFFHH)
Size 96: 48,110 OK (HHHFFF)
Size 97: 49,111 BAD (FFFFHH)
Size 98: 49,113 OK (HHHFFF)
Size 99: 50,114 BAD (FFFFHH)
Size 100: 50,115 OK (HHHFFF)
Windows 8.1
-----------
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,5 OK (HHHFFF)
Size 5: 3,6 BAD (FFFFHH)
Size 6: 3,7 OK (HHHFFF)
Size 7: 4,8 BAD (FFFFHH)
Size 8: 4,9 OK (HHHFFF)
Size 9: 5,10 BAD (FFFFHH)
Size 10: 5,11 OK (HHHFFF)
Size 11: 6,13 BAD (FFFFHH)
Size 12: 6,14 OK (HHHFFF)
Size 13: 7,15 BAD (FFFFHH)
Size 14: 7,16 OK (HHHFFF)
Size 15: 8,17 BAD (FFFFHH)
Size 16: 8,18 OK (HHHFFF)
Size 17: 9,20 BAD (FFFFHH)
Size 18: 9,21 OK (HHHFFF)
Size 19: 10,22 BAD (FFFFHH)
Size 20: 10,23 OK (HHHFFF)
Size 21: 11,24 BAD (FFFFHH)
Size 22: 11,25 OK (HHHFFF)
Size 23: 12,26 BAD (FFFFHH)
Size 24: 12,28 OK (HHHFFF)
Size 25: 13,29 BAD (FFFFHH)
Size 26: 13,30 OK (HHHFFF)
Size 27: 14,31 BAD (FFFFHH)
Size 28: 14,32 OK (HHHFFF)
Size 29: 15,33 BAD (FFFFHH)
Size 30: 15,34 OK (HHHFFF)
Size 31: 16,36 BAD (FFFFHH)
Size 32: 16,37 OK (HHHFFF)
Size 33: 17,38 BAD (FFFFHH)
Size 34: 17,39 OK (HHHFFF)
Size 35: 18,40 BAD (FFFFHH)
Size 36: 18,41 OK (HHHFFF)
Size 37: 19,42 BAD (FFFFHH)
Size 38: 19,44 OK (HHHFFF)
Size 39: 20,45 BAD (FFFFHH)
Size 40: 20,46 OK (HHHFFF)
Size 41: 21,47 BAD (FFFFHH)
Size 42: 21,48 OK (HHHFFF)
Size 43: 22,49 BAD (FFFFHH)
Size 44: 22,51 OK (HHHFFF)
Size 45: 23,52 BAD (FFFFHH)
Size 46: 23,53 OK (HHHFFF)
Size 47: 24,54 BAD (FFFFHH)
Size 48: 24,55 OK (HHHFFF)
Size 49: 25,56 BAD (FFFFHH)
Size 50: 25,57 OK (HHHFFF)
Size 51: 26,59 BAD (FFFFHH)
Size 52: 26,60 OK (HHHFFF)
Size 53: 27,61 BAD (FFFFHH)
Size 54: 27,62 OK (HHHFFF)
Size 55: 28,63 BAD (FFFFHH)
Size 56: 28,64 OK (HHHFFF)
Size 57: 29,65 BAD (FFFFHH)
Size 58: 29,67 OK (HHHFFF)
Size 59: 30,68 BAD (FFFFHH)
Size 60: 30,69 OK (HHHFFF)
Size 61: 31,70 BAD (FFFFHH)
Size 62: 31,71 OK (HHHFFF)
Size 63: 32,72 BAD (FFFFHH)
Size 64: 32,74 OK (HHHFFF)
Size 65: 33,75 BAD (FFFFHH)
Size 66: 33,76 OK (HHHFFF)
Size 67: 34,77 BAD (FFFFHH)
Size 68: 34,78 OK (HHHFFF)
Size 69: 35,79 BAD (FFFFHH)
Size 70: 35,80 OK (HHHFFF)
Size 71: 36,82 BAD (FFFFHH)
Size 72: 36,83 OK (HHHFFF)
Size 73: 37,84 BAD (FFFFHH)
Size 74: 37,85 OK (HHHFFF)
Size 75: 38,86 BAD (FFFFHH)
Size 76: 38,87 OK (HHHFFF)
Size 77: 39,88 BAD (FFFFHH)
Size 78: 39,90 OK (HHHFFF)
Size 79: 40,91 BAD (FFFFHH)
Size 80: 40,92 OK (HHHFFF)
Size 81: 41,93 BAD (FFFFHH)
Size 82: 41,94 OK (HHHFFF)
Size 83: 42,95 BAD (FFFFHH)
Size 84: 42,96 OK (HHHFFF)
Size 85: 43,98 BAD (FFFFHH)
Size 86: 43,99 OK (HHHFFF)
Size 87: 44,100 BAD (FFFFHH)
Size 88: 44,101 OK (HHHFFF)
Size 89: 45,102 BAD (FFFFHH)
Size 90: 45,103 OK (HHHFFF)
Size 91: 46,105 BAD (FFFFHH)
Size 92: 46,106 OK (HHHFFF)
Size 93: 47,107 BAD (FFFFHH)
Size 94: 47,108 OK (HHHFFF)
Size 95: 48,109 BAD (FFFFHH)
Size 96: 48,110 OK (HHHFFF)
Size 97: 49,111 BAD (FFFFHH)
Size 98: 49,113 OK (HHHFFF)
Size 99: 50,114 BAD (FFFFHH)
Size 100: 50,115 OK (HHHFFF)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,2 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 BAD (FFFFHH)
Size 4: 2,5 OK (HHHFFF)
Size 5: 3,6 BAD (FFFFHH)
Size 6: 3,7 OK (HHHFFF)
Size 7: 4,8 BAD (FFFFHH)
Size 8: 4,9 OK (HHHFFF)
Size 9: 5,10 BAD (FFFFHH)
Size 10: 5,11 OK (HHHFFF)
Size 11: 6,13 BAD (FFFFHH)
Size 12: 6,14 OK (HHHFFF)
Size 13: 7,15 BAD (FFFFHH)
Size 14: 7,16 OK (HHHFFF)
Size 15: 8,17 BAD (FFFFHH)
Size 16: 8,18 OK (HHHFFF)
Size 17: 9,20 BAD (FFFFHH)
Size 18: 9,21 OK (HHHFFF)
Size 19: 10,22 BAD (FFFFHH)
Size 20: 10,23 OK (HHHFFF)
Size 21: 11,24 BAD (FFFFHH)
Size 22: 11,25 OK (HHHFFF)
Size 23: 12,26 BAD (FFFFHH)
Size 24: 12,28 OK (HHHFFF)
Size 25: 13,29 BAD (FFFFHH)
Size 26: 13,30 OK (HHHFFF)
Size 27: 14,31 BAD (FFFFHH)
Size 28: 14,32 OK (HHHFFF)
Size 29: 15,33 BAD (FFFFHH)
Size 30: 15,34 OK (HHHFFF)
Size 31: 16,36 BAD (FFFFHH)
Size 32: 16,37 OK (HHHFFF)
Size 33: 17,38 BAD (FFFFHH)
Size 34: 17,39 OK (HHHFFF)
Size 35: 18,40 BAD (FFFFHH)
Size 36: 18,41 OK (HHHFFF)
Size 37: 19,42 BAD (FFFFHH)
Size 38: 19,44 OK (HHHFFF)
Size 39: 20,45 BAD (FFFFHH)
Size 40: 20,46 OK (HHHFFF)
Size 41: 21,47 BAD (FFFFHH)
Size 42: 21,48 OK (HHHFFF)
Size 43: 22,49 BAD (FFFFHH)
Size 44: 22,51 OK (HHHFFF)
Size 45: 23,52 BAD (FFFFHH)
Size 46: 23,53 OK (HHHFFF)
Size 47: 24,54 BAD (FFFFHH)
Size 48: 24,55 OK (HHHFFF)
Size 49: 25,56 BAD (FFFFHH)
Size 50: 25,57 OK (HHHFFF)
Size 51: 26,59 BAD (FFFFHH)
Size 52: 26,60 OK (HHHFFF)
Size 53: 27,61 BAD (FFFFHH)
Size 54: 27,62 OK (HHHFFF)
Size 55: 28,63 BAD (FFFFHH)
Size 56: 28,64 OK (HHHFFF)
Size 57: 29,65 BAD (FFFFHH)
Size 58: 29,67 OK (HHHFFF)
Size 59: 30,68 BAD (FFFFHH)
Size 60: 30,69 OK (HHHFFF)
Size 61: 31,70 BAD (FFFFHH)
Size 62: 31,71 OK (HHHFFF)
Size 63: 32,72 BAD (FFFFHH)
Size 64: 32,74 OK (HHHFFF)
Size 65: 33,75 BAD (FFFFHH)
Size 66: 33,76 OK (HHHFFF)
Size 67: 34,77 BAD (FFFFHH)
Size 68: 34,78 OK (HHHFFF)
Size 69: 35,79 BAD (FFFFHH)
Size 70: 35,80 OK (HHHFFF)
Size 71: 36,82 BAD (FFFFHH)
Size 72: 36,83 OK (HHHFFF)
Size 73: 37,84 BAD (FFFFHH)
Size 74: 37,85 OK (HHHFFF)
Size 75: 38,86 BAD (FFFFHH)
Size 76: 38,87 OK (HHHFFF)
Size 77: 39,88 BAD (FFFFHH)
Size 78: 39,90 OK (HHHFFF)
Size 79: 40,91 BAD (FFFFHH)
Size 80: 40,92 OK (HHHFFF)
Size 81: 41,93 BAD (FFFFHH)
Size 82: 41,94 OK (HHHFFF)
Size 83: 42,95 BAD (FFFFHH)
Size 84: 42,96 OK (HHHFFF)
Size 85: 43,98 BAD (FFFFHH)
Size 86: 43,99 OK (HHHFFF)
Size 87: 44,100 BAD (FFFFHH)
Size 88: 44,101 OK (HHHFFF)
Size 89: 45,102 BAD (FFFFHH)
Size 90: 45,103 OK (HHHFFF)
Size 91: 46,105 BAD (FFFFHH)
Size 92: 46,106 OK (HHHFFF)
Size 93: 47,107 BAD (FFFFHH)
Size 94: 47,108 OK (HHHFFF)
Size 95: 48,109 BAD (FFFFHH)
Size 96: 48,110 OK (HHHFFF)
Size 97: 49,111 BAD (FFFFHH)
Size 98: 49,113 OK (HHHFFF)
Size 99: 50,114 BAD (FFFFHH)
Size 100: 50,115 OK (HHHFFF)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 OK (HHHFFF)
Size 2: 1,2 OK (HHHFFF)
Size 3: 2,3 OK (HHHFFF)
Size 4: 2,4 OK (HHHFFF)
Size 5: 3,5 OK (HHHFFF)
Size 6: 3,6 OK (HHHFFF)
Size 7: 4,7 OK (HHHFFF)
Size 8: 4,8 OK (HHHFFF)
Size 9: 5,9 OK (HHHFFF)
Size 10: 5,10 OK (HHHFFF)
Size 11: 6,11 OK (HHHFFF)
Size 12: 6,12 OK (HHHFFF)
Size 13: 7,13 OK (HHHFFF)
Size 14: 7,14 OK (HHHFFF)
Size 15: 8,15 OK (HHHFFF)
Size 16: 8,16 OK (HHHFFF)
Size 17: 9,17 OK (HHHFFF)
Size 18: 9,18 OK (HHHFFF)
Size 19: 10,19 OK (HHHFFF)
Size 20: 10,20 OK (HHHFFF)
Size 21: 11,21 OK (HHHFFF)
Size 22: 11,22 OK (HHHFFF)
Size 23: 12,23 OK (HHHFFF)
Size 24: 12,24 OK (HHHFFF)
Size 25: 13,25 OK (HHHFFF)
Size 26: 13,26 OK (HHHFFF)
Size 27: 14,27 OK (HHHFFF)
Size 28: 14,28 OK (HHHFFF)
Size 29: 15,29 OK (HHHFFF)
Size 30: 15,30 OK (HHHFFF)
Size 31: 16,31 OK (HHHFFF)
Size 32: 16,32 OK (HHHFFF)
Size 33: 17,33 OK (HHHFFF)
Size 34: 17,34 OK (HHHFFF)
Size 35: 18,35 OK (HHHFFF)
Size 36: 18,36 OK (HHHFFF)
Size 37: 19,37 OK (HHHFFF)
Size 38: 19,38 OK (HHHFFF)
Size 39: 20,39 OK (HHHFFF)
Size 40: 20,40 OK (HHHFFF)
Size 41: 21,41 OK (HHHFFF)
Size 42: 21,42 OK (HHHFFF)
Size 43: 22,43 OK (HHHFFF)
Size 44: 22,44 OK (HHHFFF)
Size 45: 23,45 OK (HHHFFF)
Size 46: 23,46 OK (HHHFFF)
Size 47: 24,47 OK (HHHFFF)
Size 48: 24,48 OK (HHHFFF)
Size 49: 25,49 OK (HHHFFF)
Size 50: 25,50 OK (HHHFFF)
Size 51: 26,51 OK (HHHFFF)
Size 52: 26,52 OK (HHHFFF)
Size 53: 27,53 OK (HHHFFF)
Size 54: 27,54 OK (HHHFFF)
Size 55: 28,55 OK (HHHFFF)
Size 56: 28,56 OK (HHHFFF)
Size 57: 29,57 OK (HHHFFF)
Size 58: 29,58 OK (HHHFFF)
Size 59: 30,59 OK (HHHFFF)
Size 60: 30,60 OK (HHHFFF)
Size 61: 31,61 OK (HHHFFF)
Size 62: 31,62 OK (HHHFFF)
Size 63: 32,63 OK (HHHFFF)
Size 64: 32,64 OK (HHHFFF)
Size 65: 33,65 OK (HHHFFF)
Size 66: 33,66 OK (HHHFFF)
Size 67: 34,67 OK (HHHFFF)
Size 68: 34,68 OK (HHHFFF)
Size 69: 35,69 OK (HHHFFF)
Size 70: 35,70 OK (HHHFFF)
Size 71: 36,71 OK (HHHFFF)
Size 72: 36,72 OK (HHHFFF)
Size 73: 37,73 OK (HHHFFF)
Size 74: 37,74 OK (HHHFFF)
Size 75: 38,75 OK (HHHFFF)
Size 76: 38,76 OK (HHHFFF)
Size 77: 39,77 OK (HHHFFF)
Size 78: 39,78 OK (HHHFFF)
Size 79: 40,79 OK (HHHFFF)
Size 80: 40,80 OK (HHHFFF)
Size 81: 41,81 OK (HHHFFF)
Size 82: 41,82 OK (HHHFFF)
Size 83: 42,83 OK (HHHFFF)
Size 84: 42,84 OK (HHHFFF)
Size 85: 43,85 OK (HHHFFF)
Size 86: 43,86 OK (HHHFFF)
Size 87: 44,87 OK (HHHFFF)
Size 88: 44,88 OK (HHHFFF)
Size 89: 45,89 OK (HHHFFF)
Size 90: 45,90 OK (HHHFFF)
Size 91: 46,91 OK (HHHFFF)
Size 92: 46,92 OK (HHHFFF)
Size 93: 47,93 OK (HHHFFF)
Size 94: 47,94 OK (HHHFFF)
Size 95: 48,95 OK (HHHFFF)
Size 96: 48,96 OK (HHHFFF)
Size 97: 49,97 OK (HHHFFF)
Size 98: 49,98 OK (HHHFFF)
Size 99: 50,99 OK (HHHFFF)
Size 100: 50,100 OK (HHHFFF)

View File

@ -0,0 +1,630 @@
===========================================================
Code Page 950, Chinese Traditional (Taiwan), MingLight font
===========================================================
Options: -face-minglight -family 0x36
Chars: A2 A3 2014 3044 30FC 4000
Vista
-----
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,4 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (HHHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (HHHFHH)
Size 8: 4,10 GOOD (HHFFFF)
Size 9: 5,11 BAD (HHHFHH)
Size 10: 5,12 GOOD (HHFFFF)
Size 11: 6,13 BAD (HHHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,16 BAD (HHHFHH)
Size 14: 7,17 GOOD (HHFFFF)
Size 15: 8,18 BAD (HHHFHH)
Size 16: 8,19 GOOD (HHFFFF)
Size 17: 9,20 BAD (HHHFHH)
Size 18: 9,22 GOOD (HHFFFF)
Size 19: 10,23 BAD (HHHFHH)
Size 20: 10,24 GOOD (HHFFFF)
Size 21: 11,25 BAD (HHHFHH)
Size 22: 11,26 GOOD (HHFFFF)
Size 23: 12,28 BAD (HHHFHH)
Size 24: 12,29 GOOD (HHFFFF)
Size 25: 13,30 BAD (HHHFHH)
Size 26: 13,31 GOOD (HHFFFF)
Size 27: 14,32 BAD (HHHFHH)
Size 28: 14,34 GOOD (HHFFFF)
Size 29: 15,35 BAD (HHHFHH)
Size 30: 15,36 GOOD (HHFFFF)
Size 31: 16,37 BAD (HHHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,40 BAD (HHHFHH)
Size 34: 17,41 GOOD (HHFFFF)
Size 35: 18,42 BAD (HHHFHH)
Size 36: 18,43 GOOD (HHFFFF)
Size 37: 19,44 BAD (HHHFHH)
Size 38: 19,46 GOOD (HHFFFF)
Size 39: 20,47 BAD (HHHFHH)
Size 40: 20,48 GOOD (HHFFFF)
Size 41: 21,49 BAD (HHHFHH)
Size 42: 21,50 GOOD (HHFFFF)
Size 43: 22,52 BAD (HHHFHH)
Size 44: 22,53 GOOD (HHFFFF)
Size 45: 23,54 BAD (HHHFHH)
Size 46: 23,55 GOOD (HHFFFF)
Size 47: 24,56 BAD (HHHFHH)
Size 48: 24,58 GOOD (HHFFFF)
Size 49: 25,59 BAD (HHHFHH)
Size 50: 25,60 GOOD (HHFFFF)
Size 51: 26,61 BAD (HHHFHH)
Size 52: 26,62 GOOD (HHFFFF)
Size 53: 27,64 BAD (HHHFHH)
Size 54: 27,65 GOOD (HHFFFF)
Size 55: 28,66 BAD (HHHFHH)
Size 56: 28,67 GOOD (HHFFFF)
Size 57: 29,68 BAD (HHHFHH)
Size 58: 29,70 GOOD (HHFFFF)
Size 59: 30,71 BAD (HHHFHH)
Size 60: 30,72 GOOD (HHFFFF)
Size 61: 31,73 BAD (HHHFHH)
Size 62: 31,74 GOOD (HHFFFF)
Size 63: 32,76 BAD (HHHFHH)
Size 64: 32,77 GOOD (HHFFFF)
Size 65: 33,78 BAD (HHHFHH)
Size 66: 33,79 GOOD (HHFFFF)
Size 67: 34,80 BAD (HHHFHH)
Size 68: 34,82 GOOD (HHFFFF)
Size 69: 35,83 BAD (HHHFHH)
Size 70: 35,84 GOOD (HHFFFF)
Size 71: 36,85 BAD (HHHFHH)
Size 72: 36,86 GOOD (HHFFFF)
Size 73: 37,88 BAD (HHHFHH)
Size 74: 37,89 GOOD (HHFFFF)
Size 75: 38,90 BAD (HHHFHH)
Size 76: 38,91 GOOD (HHFFFF)
Size 77: 39,92 BAD (HHHFHH)
Size 78: 39,94 GOOD (HHFFFF)
Size 79: 40,95 BAD (HHHFHH)
Size 80: 40,96 GOOD (HHFFFF)
Size 81: 41,97 BAD (HHHFHH)
Size 82: 41,98 GOOD (HHFFFF)
Size 83: 42,100 BAD (HHHFHH)
Size 84: 42,101 GOOD (HHFFFF)
Size 85: 43,102 BAD (HHHFHH)
Size 86: 43,103 GOOD (HHFFFF)
Size 87: 44,104 BAD (HHHFHH)
Size 88: 44,106 GOOD (HHFFFF)
Size 89: 45,107 BAD (HHHFHH)
Size 90: 45,108 GOOD (HHFFFF)
Size 91: 46,109 BAD (HHHFHH)
Size 92: 46,110 GOOD (HHFFFF)
Size 93: 47,112 BAD (HHHFHH)
Size 94: 47,113 GOOD (HHFFFF)
Size 95: 48,114 BAD (HHHFHH)
Size 96: 48,115 GOOD (HHFFFF)
Size 97: 49,116 BAD (HHHFHH)
Size 98: 49,118 GOOD (HHFFFF)
Size 99: 50,119 BAD (HHHFHH)
Size 100: 50,120 GOOD (HHFFFF)
Windows 7
---------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,4 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,10 GOOD (HHFFFF)
Size 9: 5,11 BAD (FFHFHH)
Size 10: 5,12 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,16 BAD (FFHFHH)
Size 14: 7,17 GOOD (HHFFFF)
Size 15: 8,18 BAD (FFHFHH)
Size 16: 8,19 GOOD (HHFFFF)
Size 17: 9,20 BAD (FFHFHH)
Size 18: 9,22 GOOD (HHFFFF)
Size 19: 10,23 BAD (FFHFHH)
Size 20: 10,24 GOOD (HHFFFF)
Size 21: 11,25 BAD (FFHFHH)
Size 22: 11,26 GOOD (HHFFFF)
Size 23: 12,28 BAD (FFHFHH)
Size 24: 12,29 GOOD (HHFFFF)
Size 25: 13,30 BAD (FFHFHH)
Size 26: 13,31 GOOD (HHFFFF)
Size 27: 14,32 BAD (FFHFHH)
Size 28: 14,34 GOOD (HHFFFF)
Size 29: 15,35 BAD (FFHFHH)
Size 30: 15,36 GOOD (HHFFFF)
Size 31: 16,37 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,40 BAD (FFHFHH)
Size 34: 17,41 GOOD (HHFFFF)
Size 35: 18,42 BAD (FFHFHH)
Size 36: 18,43 GOOD (HHFFFF)
Size 37: 19,44 BAD (FFHFHH)
Size 38: 19,46 GOOD (HHFFFF)
Size 39: 20,47 BAD (FFHFHH)
Size 40: 20,48 GOOD (HHFFFF)
Size 41: 21,49 BAD (FFHFHH)
Size 42: 21,50 GOOD (HHFFFF)
Size 43: 22,52 BAD (FFHFHH)
Size 44: 22,53 GOOD (HHFFFF)
Size 45: 23,54 BAD (FFHFHH)
Size 46: 23,55 GOOD (HHFFFF)
Size 47: 24,56 BAD (FFHFHH)
Size 48: 24,58 GOOD (HHFFFF)
Size 49: 25,59 BAD (FFHFHH)
Size 50: 25,60 GOOD (HHFFFF)
Size 51: 26,61 BAD (FFHFHH)
Size 52: 26,62 GOOD (HHFFFF)
Size 53: 27,64 BAD (FFHFHH)
Size 54: 27,65 GOOD (HHFFFF)
Size 55: 28,66 BAD (FFHFHH)
Size 56: 28,67 GOOD (HHFFFF)
Size 57: 29,68 BAD (FFHFHH)
Size 58: 29,70 GOOD (HHFFFF)
Size 59: 30,71 BAD (FFHFHH)
Size 60: 30,72 GOOD (HHFFFF)
Size 61: 31,73 BAD (FFHFHH)
Size 62: 31,74 GOOD (HHFFFF)
Size 63: 32,76 BAD (FFHFHH)
Size 64: 32,77 GOOD (HHFFFF)
Size 65: 33,78 BAD (FFHFHH)
Size 66: 33,79 GOOD (HHFFFF)
Size 67: 34,80 BAD (FFHFHH)
Size 68: 34,82 GOOD (HHFFFF)
Size 69: 35,83 BAD (FFHFHH)
Size 70: 35,84 GOOD (HHFFFF)
Size 71: 36,85 BAD (FFHFHH)
Size 72: 36,86 GOOD (HHFFFF)
Size 73: 37,88 BAD (FFHFHH)
Size 74: 37,89 GOOD (HHFFFF)
Size 75: 38,90 BAD (FFHFHH)
Size 76: 38,91 GOOD (HHFFFF)
Size 77: 39,92 BAD (FFHFHH)
Size 78: 39,94 GOOD (HHFFFF)
Size 79: 40,95 BAD (FFHFHH)
Size 80: 40,96 GOOD (HHFFFF)
Size 81: 41,97 BAD (FFHFHH)
Size 82: 41,98 GOOD (HHFFFF)
Size 83: 42,100 BAD (FFHFHH)
Size 84: 42,101 GOOD (HHFFFF)
Size 85: 43,102 BAD (FFHFHH)
Size 86: 43,103 GOOD (HHFFFF)
Size 87: 44,104 BAD (FFHFHH)
Size 88: 44,106 GOOD (HHFFFF)
Size 89: 45,107 BAD (FFHFHH)
Size 90: 45,108 GOOD (HHFFFF)
Size 91: 46,109 BAD (FFHFHH)
Size 92: 46,110 GOOD (HHFFFF)
Size 93: 47,112 BAD (FFHFHH)
Size 94: 47,113 GOOD (HHFFFF)
Size 95: 48,114 BAD (FFHFHH)
Size 96: 48,115 GOOD (HHFFFF)
Size 97: 49,116 BAD (FFHFHH)
Size 98: 49,118 GOOD (HHFFFF)
Size 99: 50,119 BAD (FFHFHH)
Size 100: 50,120 GOOD (HHFFFF)
Windows 8
---------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,4 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,10 GOOD (HHFFFF)
Size 9: 5,11 BAD (FFHFHH)
Size 10: 5,12 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,16 BAD (FFHFHH)
Size 14: 7,17 GOOD (HHFFFF)
Size 15: 8,18 BAD (FFHFHH)
Size 16: 8,19 GOOD (HHFFFF)
Size 17: 9,20 BAD (FFHFHH)
Size 18: 9,22 GOOD (HHFFFF)
Size 19: 10,23 BAD (FFHFHH)
Size 20: 10,24 GOOD (HHFFFF)
Size 21: 11,25 BAD (FFHFHH)
Size 22: 11,26 GOOD (HHFFFF)
Size 23: 12,28 BAD (FFHFHH)
Size 24: 12,29 GOOD (HHFFFF)
Size 25: 13,30 BAD (FFHFHH)
Size 26: 13,31 GOOD (HHFFFF)
Size 27: 14,32 BAD (FFHFHH)
Size 28: 14,34 GOOD (HHFFFF)
Size 29: 15,35 BAD (FFHFHH)
Size 30: 15,36 GOOD (HHFFFF)
Size 31: 16,37 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,40 BAD (FFHFHH)
Size 34: 17,41 GOOD (HHFFFF)
Size 35: 18,42 BAD (FFHFHH)
Size 36: 18,43 GOOD (HHFFFF)
Size 37: 19,44 BAD (FFHFHH)
Size 38: 19,46 GOOD (HHFFFF)
Size 39: 20,47 BAD (FFHFHH)
Size 40: 20,48 GOOD (HHFFFF)
Size 41: 21,49 BAD (FFHFHH)
Size 42: 21,50 GOOD (HHFFFF)
Size 43: 22,52 BAD (FFHFHH)
Size 44: 22,53 GOOD (HHFFFF)
Size 45: 23,54 BAD (FFHFHH)
Size 46: 23,55 GOOD (HHFFFF)
Size 47: 24,56 BAD (FFHFHH)
Size 48: 24,58 GOOD (HHFFFF)
Size 49: 25,59 BAD (FFHFHH)
Size 50: 25,60 GOOD (HHFFFF)
Size 51: 26,61 BAD (FFHFHH)
Size 52: 26,62 GOOD (HHFFFF)
Size 53: 27,64 BAD (FFHFHH)
Size 54: 27,65 GOOD (HHFFFF)
Size 55: 28,66 BAD (FFHFHH)
Size 56: 28,67 GOOD (HHFFFF)
Size 57: 29,68 BAD (FFHFHH)
Size 58: 29,70 GOOD (HHFFFF)
Size 59: 30,71 BAD (FFHFHH)
Size 60: 30,72 GOOD (HHFFFF)
Size 61: 31,73 BAD (FFHFHH)
Size 62: 31,74 GOOD (HHFFFF)
Size 63: 32,76 BAD (FFHFHH)
Size 64: 32,77 GOOD (HHFFFF)
Size 65: 33,78 BAD (FFHFHH)
Size 66: 33,79 GOOD (HHFFFF)
Size 67: 34,80 BAD (FFHFHH)
Size 68: 34,82 GOOD (HHFFFF)
Size 69: 35,83 BAD (FFHFHH)
Size 70: 35,84 GOOD (HHFFFF)
Size 71: 36,85 BAD (FFHFHH)
Size 72: 36,86 GOOD (HHFFFF)
Size 73: 37,88 BAD (FFHFHH)
Size 74: 37,89 GOOD (HHFFFF)
Size 75: 38,90 BAD (FFHFHH)
Size 76: 38,91 GOOD (HHFFFF)
Size 77: 39,92 BAD (FFHFHH)
Size 78: 39,94 GOOD (HHFFFF)
Size 79: 40,95 BAD (FFHFHH)
Size 80: 40,96 GOOD (HHFFFF)
Size 81: 41,97 BAD (FFHFHH)
Size 82: 41,98 GOOD (HHFFFF)
Size 83: 42,100 BAD (FFHFHH)
Size 84: 42,101 GOOD (HHFFFF)
Size 85: 43,102 BAD (FFHFHH)
Size 86: 43,103 GOOD (HHFFFF)
Size 87: 44,104 BAD (FFHFHH)
Size 88: 44,106 GOOD (HHFFFF)
Size 89: 45,107 BAD (FFHFHH)
Size 90: 45,108 GOOD (HHFFFF)
Size 91: 46,109 BAD (FFHFHH)
Size 92: 46,110 GOOD (HHFFFF)
Size 93: 47,112 BAD (FFHFHH)
Size 94: 47,113 GOOD (HHFFFF)
Size 95: 48,114 BAD (FFHFHH)
Size 96: 48,115 GOOD (HHFFFF)
Size 97: 49,116 BAD (FFHFHH)
Size 98: 49,118 GOOD (HHFFFF)
Size 99: 50,119 BAD (FFHFHH)
Size 100: 50,120 GOOD (HHFFFF)
Windows 8.1
-----------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,4 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,10 GOOD (HHFFFF)
Size 9: 5,11 BAD (FFHFHH)
Size 10: 5,12 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,16 BAD (FFHFHH)
Size 14: 7,17 GOOD (HHFFFF)
Size 15: 8,18 BAD (FFHFHH)
Size 16: 8,19 GOOD (HHFFFF)
Size 17: 9,20 BAD (FFHFHH)
Size 18: 9,22 GOOD (HHFFFF)
Size 19: 10,23 BAD (FFHFHH)
Size 20: 10,24 GOOD (HHFFFF)
Size 21: 11,25 BAD (FFHFHH)
Size 22: 11,26 GOOD (HHFFFF)
Size 23: 12,28 BAD (FFHFHH)
Size 24: 12,29 GOOD (HHFFFF)
Size 25: 13,30 BAD (FFHFHH)
Size 26: 13,31 GOOD (HHFFFF)
Size 27: 14,32 BAD (FFHFHH)
Size 28: 14,34 GOOD (HHFFFF)
Size 29: 15,35 BAD (FFHFHH)
Size 30: 15,36 GOOD (HHFFFF)
Size 31: 16,37 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,40 BAD (FFHFHH)
Size 34: 17,41 GOOD (HHFFFF)
Size 35: 18,42 BAD (FFHFHH)
Size 36: 18,43 GOOD (HHFFFF)
Size 37: 19,44 BAD (FFHFHH)
Size 38: 19,46 GOOD (HHFFFF)
Size 39: 20,47 BAD (FFHFHH)
Size 40: 20,48 GOOD (HHFFFF)
Size 41: 21,49 BAD (FFHFHH)
Size 42: 21,50 GOOD (HHFFFF)
Size 43: 22,52 BAD (FFHFHH)
Size 44: 22,53 GOOD (HHFFFF)
Size 45: 23,54 BAD (FFHFHH)
Size 46: 23,55 GOOD (HHFFFF)
Size 47: 24,56 BAD (FFHFHH)
Size 48: 24,58 GOOD (HHFFFF)
Size 49: 25,59 BAD (FFHFHH)
Size 50: 25,60 GOOD (HHFFFF)
Size 51: 26,61 BAD (FFHFHH)
Size 52: 26,62 GOOD (HHFFFF)
Size 53: 27,64 BAD (FFHFHH)
Size 54: 27,65 GOOD (HHFFFF)
Size 55: 28,66 BAD (FFHFHH)
Size 56: 28,67 GOOD (HHFFFF)
Size 57: 29,68 BAD (FFHFHH)
Size 58: 29,70 GOOD (HHFFFF)
Size 59: 30,71 BAD (FFHFHH)
Size 60: 30,72 GOOD (HHFFFF)
Size 61: 31,73 BAD (FFHFHH)
Size 62: 31,74 GOOD (HHFFFF)
Size 63: 32,76 BAD (FFHFHH)
Size 64: 32,77 GOOD (HHFFFF)
Size 65: 33,78 BAD (FFHFHH)
Size 66: 33,79 GOOD (HHFFFF)
Size 67: 34,80 BAD (FFHFHH)
Size 68: 34,82 GOOD (HHFFFF)
Size 69: 35,83 BAD (FFHFHH)
Size 70: 35,84 GOOD (HHFFFF)
Size 71: 36,85 BAD (FFHFHH)
Size 72: 36,86 GOOD (HHFFFF)
Size 73: 37,88 BAD (FFHFHH)
Size 74: 37,89 GOOD (HHFFFF)
Size 75: 38,90 BAD (FFHFHH)
Size 76: 38,91 GOOD (HHFFFF)
Size 77: 39,92 BAD (FFHFHH)
Size 78: 39,94 GOOD (HHFFFF)
Size 79: 40,95 BAD (FFHFHH)
Size 80: 40,96 GOOD (HHFFFF)
Size 81: 41,97 BAD (FFHFHH)
Size 82: 41,98 GOOD (HHFFFF)
Size 83: 42,100 BAD (FFHFHH)
Size 84: 42,101 GOOD (HHFFFF)
Size 85: 43,102 BAD (FFHFHH)
Size 86: 43,103 GOOD (HHFFFF)
Size 87: 44,104 BAD (FFHFHH)
Size 88: 44,106 GOOD (HHFFFF)
Size 89: 45,107 BAD (FFHFHH)
Size 90: 45,108 GOOD (HHFFFF)
Size 91: 46,109 BAD (FFHFHH)
Size 92: 46,110 GOOD (HHFFFF)
Size 93: 47,112 BAD (FFHFHH)
Size 94: 47,113 GOOD (HHFFFF)
Size 95: 48,114 BAD (FFHFHH)
Size 96: 48,115 GOOD (HHFFFF)
Size 97: 49,116 BAD (FFHFHH)
Size 98: 49,118 GOOD (HHFFFF)
Size 99: 50,119 BAD (FFHFHH)
Size 100: 50,120 GOOD (HHFFFF)
Windows 10 14342 Old Console
----------------------------
Size 1: 1,2 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,4 BAD (FFHFHH)
Size 4: 2,5 GOOD (HHFFFF)
Size 5: 3,6 BAD (FFHFHH)
Size 6: 3,7 GOOD (HHFFFF)
Size 7: 4,8 BAD (FFHFHH)
Size 8: 4,10 GOOD (HHFFFF)
Size 9: 5,11 BAD (FFHFHH)
Size 10: 5,12 GOOD (HHFFFF)
Size 11: 6,13 BAD (FFHFHH)
Size 12: 6,14 GOOD (HHFFFF)
Size 13: 7,16 BAD (FFHFHH)
Size 14: 7,17 GOOD (HHFFFF)
Size 15: 8,18 BAD (FFHFHH)
Size 16: 8,19 GOOD (HHFFFF)
Size 17: 9,20 BAD (FFHFHH)
Size 18: 9,22 GOOD (HHFFFF)
Size 19: 10,23 BAD (FFHFHH)
Size 20: 10,24 GOOD (HHFFFF)
Size 21: 11,25 BAD (FFHFHH)
Size 22: 11,26 GOOD (HHFFFF)
Size 23: 12,28 BAD (FFHFHH)
Size 24: 12,29 GOOD (HHFFFF)
Size 25: 13,30 BAD (FFHFHH)
Size 26: 13,31 GOOD (HHFFFF)
Size 27: 14,32 BAD (FFHFHH)
Size 28: 14,34 GOOD (HHFFFF)
Size 29: 15,35 BAD (FFHFHH)
Size 30: 15,36 GOOD (HHFFFF)
Size 31: 16,37 BAD (FFHFHH)
Size 32: 16,38 GOOD (HHFFFF)
Size 33: 17,40 BAD (FFHFHH)
Size 34: 17,41 GOOD (HHFFFF)
Size 35: 18,42 BAD (FFHFHH)
Size 36: 18,43 GOOD (HHFFFF)
Size 37: 19,44 BAD (FFHFHH)
Size 38: 19,46 GOOD (HHFFFF)
Size 39: 20,47 BAD (FFHFHH)
Size 40: 20,48 GOOD (HHFFFF)
Size 41: 21,49 BAD (FFHFHH)
Size 42: 21,50 GOOD (HHFFFF)
Size 43: 22,52 BAD (FFHFHH)
Size 44: 22,53 GOOD (HHFFFF)
Size 45: 23,54 BAD (FFHFHH)
Size 46: 23,55 GOOD (HHFFFF)
Size 47: 24,56 BAD (FFHFHH)
Size 48: 24,58 GOOD (HHFFFF)
Size 49: 25,59 BAD (FFHFHH)
Size 50: 25,60 GOOD (HHFFFF)
Size 51: 26,61 BAD (FFHFHH)
Size 52: 26,62 GOOD (HHFFFF)
Size 53: 27,64 BAD (FFHFHH)
Size 54: 27,65 GOOD (HHFFFF)
Size 55: 28,66 BAD (FFHFHH)
Size 56: 28,67 GOOD (HHFFFF)
Size 57: 29,68 BAD (FFHFHH)
Size 58: 29,70 GOOD (HHFFFF)
Size 59: 30,71 BAD (FFHFHH)
Size 60: 30,72 GOOD (HHFFFF)
Size 61: 31,73 BAD (FFHFHH)
Size 62: 31,74 GOOD (HHFFFF)
Size 63: 32,76 BAD (FFHFHH)
Size 64: 32,77 GOOD (HHFFFF)
Size 65: 33,78 BAD (FFHFHH)
Size 66: 33,79 GOOD (HHFFFF)
Size 67: 34,80 BAD (FFHFHH)
Size 68: 34,82 GOOD (HHFFFF)
Size 69: 35,83 BAD (FFHFHH)
Size 70: 35,84 GOOD (HHFFFF)
Size 71: 36,85 BAD (FFHFHH)
Size 72: 36,86 GOOD (HHFFFF)
Size 73: 37,88 BAD (FFHFHH)
Size 74: 37,89 GOOD (HHFFFF)
Size 75: 38,90 BAD (FFHFHH)
Size 76: 38,91 GOOD (HHFFFF)
Size 77: 39,92 BAD (FFHFHH)
Size 78: 39,94 GOOD (HHFFFF)
Size 79: 40,95 BAD (FFHFHH)
Size 80: 40,96 GOOD (HHFFFF)
Size 81: 41,97 BAD (FFHFHH)
Size 82: 41,98 GOOD (HHFFFF)
Size 83: 42,100 BAD (FFHFHH)
Size 84: 42,101 GOOD (HHFFFF)
Size 85: 43,102 BAD (FFHFHH)
Size 86: 43,103 GOOD (HHFFFF)
Size 87: 44,104 BAD (FFHFHH)
Size 88: 44,106 GOOD (HHFFFF)
Size 89: 45,107 BAD (FFHFHH)
Size 90: 45,108 GOOD (HHFFFF)
Size 91: 46,109 BAD (FFHFHH)
Size 92: 46,110 GOOD (HHFFFF)
Size 93: 47,112 BAD (FFHFHH)
Size 94: 47,113 GOOD (HHFFFF)
Size 95: 48,114 BAD (FFHFHH)
Size 96: 48,115 GOOD (HHFFFF)
Size 97: 49,116 BAD (FFHFHH)
Size 98: 49,118 GOOD (HHFFFF)
Size 99: 50,119 BAD (FFHFHH)
Size 100: 50,120 GOOD (HHFFFF)
Windows 10 14342 New Console
----------------------------
Size 1: 1,1 GOOD (HHFFFF)
Size 2: 1,2 GOOD (HHFFFF)
Size 3: 2,3 GOOD (HHFFFF)
Size 4: 2,4 GOOD (HHFFFF)
Size 5: 3,5 GOOD (HHFFFF)
Size 6: 3,6 GOOD (HHFFFF)
Size 7: 4,7 GOOD (HHFFFF)
Size 8: 4,8 GOOD (HHFFFF)
Size 9: 5,9 GOOD (HHFFFF)
Size 10: 5,10 GOOD (HHFFFF)
Size 11: 6,11 GOOD (HHFFFF)
Size 12: 6,12 GOOD (HHFFFF)
Size 13: 7,13 GOOD (HHFFFF)
Size 14: 7,14 GOOD (HHFFFF)
Size 15: 8,15 GOOD (HHFFFF)
Size 16: 8,16 GOOD (HHFFFF)
Size 17: 9,17 GOOD (HHFFFF)
Size 18: 9,18 GOOD (HHFFFF)
Size 19: 10,19 GOOD (HHFFFF)
Size 20: 10,20 GOOD (HHFFFF)
Size 21: 11,21 GOOD (HHFFFF)
Size 22: 11,22 GOOD (HHFFFF)
Size 23: 12,23 GOOD (HHFFFF)
Size 24: 12,24 GOOD (HHFFFF)
Size 25: 13,25 GOOD (HHFFFF)
Size 26: 13,26 GOOD (HHFFFF)
Size 27: 14,27 GOOD (HHFFFF)
Size 28: 14,28 GOOD (HHFFFF)
Size 29: 15,29 GOOD (HHFFFF)
Size 30: 15,30 GOOD (HHFFFF)
Size 31: 16,31 GOOD (HHFFFF)
Size 32: 16,32 GOOD (HHFFFF)
Size 33: 17,33 GOOD (HHFFFF)
Size 34: 17,34 GOOD (HHFFFF)
Size 35: 18,35 GOOD (HHFFFF)
Size 36: 18,36 GOOD (HHFFFF)
Size 37: 19,37 GOOD (HHFFFF)
Size 38: 19,38 GOOD (HHFFFF)
Size 39: 20,39 GOOD (HHFFFF)
Size 40: 20,40 GOOD (HHFFFF)
Size 41: 21,41 GOOD (HHFFFF)
Size 42: 21,42 GOOD (HHFFFF)
Size 43: 22,43 GOOD (HHFFFF)
Size 44: 22,44 GOOD (HHFFFF)
Size 45: 23,45 GOOD (HHFFFF)
Size 46: 23,46 GOOD (HHFFFF)
Size 47: 24,47 GOOD (HHFFFF)
Size 48: 24,48 GOOD (HHFFFF)
Size 49: 25,49 GOOD (HHFFFF)
Size 50: 25,50 GOOD (HHFFFF)
Size 51: 26,51 GOOD (HHFFFF)
Size 52: 26,52 GOOD (HHFFFF)
Size 53: 27,53 GOOD (HHFFFF)
Size 54: 27,54 GOOD (HHFFFF)
Size 55: 28,55 GOOD (HHFFFF)
Size 56: 28,56 GOOD (HHFFFF)
Size 57: 29,57 GOOD (HHFFFF)
Size 58: 29,58 GOOD (HHFFFF)
Size 59: 30,59 GOOD (HHFFFF)
Size 60: 30,60 GOOD (HHFFFF)
Size 61: 31,61 GOOD (HHFFFF)
Size 62: 31,62 GOOD (HHFFFF)
Size 63: 32,63 GOOD (HHFFFF)
Size 64: 32,64 GOOD (HHFFFF)
Size 65: 33,65 GOOD (HHFFFF)
Size 66: 33,66 GOOD (HHFFFF)
Size 67: 34,67 GOOD (HHFFFF)
Size 68: 34,68 GOOD (HHFFFF)
Size 69: 35,69 GOOD (HHFFFF)
Size 70: 35,70 GOOD (HHFFFF)
Size 71: 36,71 GOOD (HHFFFF)
Size 72: 36,72 GOOD (HHFFFF)
Size 73: 37,73 GOOD (HHFFFF)
Size 74: 37,74 GOOD (HHFFFF)
Size 75: 38,75 GOOD (HHFFFF)
Size 76: 38,76 GOOD (HHFFFF)
Size 77: 39,77 GOOD (HHFFFF)
Size 78: 39,78 GOOD (HHFFFF)
Size 79: 40,79 GOOD (HHFFFF)
Size 80: 40,80 GOOD (HHFFFF)
Size 81: 41,81 GOOD (HHFFFF)
Size 82: 41,82 GOOD (HHFFFF)
Size 83: 42,83 GOOD (HHFFFF)
Size 84: 42,84 GOOD (HHFFFF)
Size 85: 43,85 GOOD (HHFFFF)
Size 86: 43,86 GOOD (HHFFFF)
Size 87: 44,87 GOOD (HHFFFF)
Size 88: 44,88 GOOD (HHFFFF)
Size 89: 45,89 GOOD (HHFFFF)
Size 90: 45,90 GOOD (HHFFFF)
Size 91: 46,91 GOOD (HHFFFF)
Size 92: 46,92 GOOD (HHFFFF)
Size 93: 47,93 GOOD (HHFFFF)
Size 94: 47,94 GOOD (HHFFFF)
Size 95: 48,95 GOOD (HHFFFF)
Size 96: 48,96 GOOD (HHFFFF)
Size 97: 49,97 GOOD (HHFFFF)
Size 98: 49,98 GOOD (HHFFFF)
Size 99: 50,99 GOOD (HHFFFF)
Size 100: 50,100 GOOD (HHFFFF)

View File

@ -0,0 +1,16 @@
The narrowest allowed console window, in pixels, on a conventional (~96dpi)
monitor:
(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 1 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 16 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
sz1:px sz1:col sz16:px sz16:col
Vista: 124 104 137 10
Windows 7: 132 112 147 11
Windows 8: 140 120 147 11
Windows 8.1: 140 120 147 11
Windows 10 OLD: 136 116 147 11
Windows 10 NEW: 136 103 136 10
I used build 14342 to test Windows 10.

View File

@ -0,0 +1,4 @@
As before, avoid odd sizes in favor of even sizes.
It's curious that the Japanese font is handled so poorly, especially with
Windows 8 and later.

View File

@ -0,0 +1,144 @@
Issues:
- Starting with the 14342 build, changing the font using
SetCurrentConsoleFontEx does not affect the window size. e.g. The content
itself will resize/redraw, but the window neither shrinks nor expands.
Presumably this is an oversight? It's almost a convenience; if a program
is going to resize the window anyway, then it's nice that the window size
contraints don't get in the way. Ordinarily, changing the font doesn't just
change the window size in pixels--it can also change the size as measured in
rows and columns.
- (Aside: in the 14342 build, there is also a bug with wmic.exe. Open a console
with more than 300 lines of screen buffer, then fill those lines with, e.g.,
dir /s. Then run wmic.exe. You won't be able to see the wmic.exe prompt.
If you query the screen buffer info somehow, you'll notice that the srWindow
is not contained within the dwSize. This breaks winpty's scraping, because
it's invalid.)
- In build 14316, with the Japanese locale, with the 437 code page, attempting
to set the Consolas font instead sets the Terminal (raster) font. It seems
to pick an appropriate vertical size.
- It seems necessary to specify "-family 0x36" for maximum reliability.
Setting the family to 0 almost always works, and specifying just -tt rarely
works.
Win7
English locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 932 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt unreliable
SetFont.exe -face Consolas -h 16 -family 0x36 works
Win10 Build 10586
New console
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Win10 Build 14316
Old console
English locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 932 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selected very small Consolas font
SetFont.exe -face Consolas -h 16 -family 0x36 works
New console
English locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt works
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 932 code page:
SetFont.exe -face Consolas -h 16 selects gothic instead
SetFont.exe -face Consolas -h 16 -tt selects gothic instead
SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 selects Terminal font instead
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36(*) selects Terminal font instead
Win10 Build 14342
Old Console
English locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 932 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
SetFont.exe -face Consolas -h 16 -family 0x36 works
New console
English locale / 437 code page:
SetFont.exe -face Consolas -h 16 works
SetFont.exe -face Consolas -h 16 -tt works
SetFont.exe -face Consolas -h 16 -family 0x36 works
Japanese locale / 932 code page:
SetFont.exe -face Consolas -h 16 selects gothic instead
SetFont.exe -face Consolas -h 16 -tt selects gothic instead
SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
Japanese locale / 437 code page:
SetFont.exe -face Consolas -h 16 selects Terminal font instead
SetFont.exe -face Consolas -h 16 -tt works
SetFont.exe -face Consolas -h 16 -family 0x36 works
(*) I was trying to figure out whether the inconsistency was at when I stumbled
onto this completely unexpected bug. Here's more detail:
F:\>SetFont.exe -face Consolas -h 16 -family 0x36 -weight normal -w 8
Setting to: nFont=0 dwFontSize=(8,16) FontFamily=0x36 FontWeight=400 FaceName="Consolas"
SetCurrentConsoleFontEx returned 1
F:\>GetFont.exe
largestConsoleWindowSize=(96,50)
maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
00-00: 12x16
GetNumberOfConsoleFonts returned 0
CP=437 OutputCP=437
F:\>SetFont.exe -face "Lucida Console" -h 16 -family 0x36 -weight normal
Setting to: nFont=0 dwFontSize=(0,16) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
SetCurrentConsoleFontEx returned 1
F:\>GetFont.exe
largestConsoleWindowSize=(96,50)
maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
00-00: 12x16
GetNumberOfConsoleFonts returned 0
CP=437 OutputCP=437
F:\>SetFont.exe -face "Lucida Console" -h 12 -family 0x36 -weight normal
Setting to: nFont=0 dwFontSize=(0,12) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
SetCurrentConsoleFontEx returned 1
F:\>GetFont.exe
largestConsoleWindowSize=(230,66)
maxWnd=0: nFont=0 dwFontSize=(5,12) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
maxWnd=1: nFont=0 dwFontSize=(116,36) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
00-00: 5x12
GetNumberOfConsoleFonts returned 0
CP=437 OutputCP=437
Even attempting to set to a Lucida Console / Consolas font from the Console
properties dialog fails.

100
misc/FontSurvey.cc Executable file
View File

@ -0,0 +1,100 @@
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "TestUtil.cc"
#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
std::vector<bool> condense(const std::vector<CHAR_INFO> &buf) {
std::vector<bool> ret;
size_t i = 0;
while (i < buf.size()) {
if (buf[i].Char.UnicodeChar == L' ' &&
((buf[i].Attributes & 0x300) == 0)) {
// end of line
break;
} else if (i + 1 < buf.size() &&
((buf[i].Attributes & 0x300) == 0x100) &&
((buf[i + 1].Attributes & 0x300) == 0x200) &&
buf[i].Char.UnicodeChar != L' ' &&
buf[i].Char.UnicodeChar == buf[i + 1].Char.UnicodeChar) {
// double-width
ret.push_back(true);
i += 2;
} else if ((buf[i].Attributes & 0x300) == 0) {
// single-width
ret.push_back(false);
i++;
} else {
ASSERT(false && "unexpected output");
}
}
return ret;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s \"arguments for SetFont.exe\"\n", argv[0]);
return 1;
}
const char *setFontArgs = argv[1];
const wchar_t testLine[] = { 0xA2, 0xA3, 0x2014, 0x3044, 0x30FC, 0x4000, 0 };
const HANDLE conout = openConout();
char setFontCmd[1024];
for (int h = 1; h <= 100; ++h) {
sprintf(setFontCmd, ".\\SetFont.exe %s -h %d && cls", setFontArgs, h);
system(setFontCmd);
CONSOLE_FONT_INFOEX infoex = {};
infoex.cbSize = sizeof(infoex);
BOOL success = GetCurrentConsoleFontEx(conout, FALSE, &infoex);
ASSERT(success && "GetCurrentConsoleFontEx failed");
DWORD actual = 0;
success = WriteConsoleW(conout, testLine, wcslen(testLine), &actual, nullptr);
ASSERT(success && actual == wcslen(testLine));
std::vector<CHAR_INFO> readBuf(14);
const SMALL_RECT readRegion = {0, 0, static_cast<short>(readBuf.size() - 1), 0};
SMALL_RECT readRegion2 = readRegion;
success = ReadConsoleOutputW(
conout, readBuf.data(),
{static_cast<short>(readBuf.size()), 1},
{0, 0},
&readRegion2);
ASSERT(success && !memcmp(&readRegion, &readRegion2, sizeof(readRegion)));
const auto widths = condense(readBuf);
std::string widthsStr;
for (bool width : widths) {
widthsStr.append(width ? "F" : "H");
}
char size[16];
sprintf(size, "%d,%d", infoex.dwFontSize.X, infoex.dwFontSize.Y);
const char *status = "";
if (widthsStr == "HHFFFF") {
status = "GOOD";
} else if (widthsStr == "HHHFFF") {
status = "OK";
} else {
status = "BAD";
}
trace("Size %3d: %-7s %-4s (%s)", h, size, status, widthsStr.c_str());
}
sprintf(setFontCmd, ".\\SetFont.exe %s -h 14", setFontArgs);
system(setFontCmd);
}

62
misc/FreezePerfTest.cc Normal file
View File

@ -0,0 +1,62 @@
#include <windows.h>
#include "TestUtil.cc"
const int SC_CONSOLE_MARK = 0xFFF2;
const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
int main(int argc, char *argv[0]) {
if (argc != 2) {
printf("Usage: %s (mark|selectall|read)\n", argv[0]);
return 1;
}
enum class Test { Mark, SelectAll, Read } test;
if (!strcmp(argv[1], "mark")) {
test = Test::Mark;
} else if (!strcmp(argv[1], "selectall")) {
test = Test::SelectAll;
} else if (!strcmp(argv[1], "read")) {
test = Test::Read;
} else {
printf("Invalid test: %s\n", argv[1]);
return 1;
}
HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
TimeMeasurement tm;
HWND hwnd = GetConsoleWindow();
setWindowPos(0, 0, 1, 1);
setBufferSize(100, 3000);
system("cls");
setWindowPos(0, 2975, 100, 25);
setCursorPos(0, 2999);
ShowWindow(hwnd, SW_HIDE);
for (int i = 0; i < 1000; ++i) {
// CONSOLE_SCREEN_BUFFER_INFO info = {};
// GetConsoleScreenBufferInfo(conout, &info);
if (test == Test::Mark) {
SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
} else if (test == Test::SelectAll) {
SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
} else if (test == Test::Read) {
static CHAR_INFO buffer[100 * 3000];
const SMALL_RECT readRegion = {0, 0, 99, 2999};
SMALL_RECT tmp = readRegion;
BOOL ret = ReadConsoleOutput(conout, buffer, {100, 3000}, {0, 0}, &tmp);
ASSERT(ret && !memcmp(&tmp, &readRegion, sizeof(tmp)));
}
}
ShowWindow(hwnd, SW_SHOW);
printf("elapsed: %f\n", tm.elapsed());
return 0;
}

20
misc/GetCh.cc Executable file
View File

@ -0,0 +1,20 @@
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
int main() {
printf("\nPress any keys -- Ctrl-D exits\n\n");
while (true) {
const int ch = getch();
printf("0x%x", ch);
if (isgraph(ch)) {
printf(" '%c'", ch);
}
printf("\n");
if (ch == 0x4) { // Ctrl-D
break;
}
}
return 0;
}

41
misc/GetConsolePos.cc Normal file
View File

@ -0,0 +1,41 @@
#include <windows.h>
#include <stdio.h>
#include "TestUtil.cc"
int main() {
const HANDLE conout = openConout();
CONSOLE_SCREEN_BUFFER_INFO info = {};
BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
ASSERT(ret && "GetConsoleScreenBufferInfo failed");
trace("cursor=%d,%d", info.dwCursorPosition.X, info.dwCursorPosition.Y);
printf("cursor=%d,%d\n", info.dwCursorPosition.X, info.dwCursorPosition.Y);
trace("srWindow={L=%d,T=%d,R=%d,B=%d}", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
printf("srWindow={L=%d,T=%d,R=%d,B=%d}\n", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
trace("dwSize=%d,%d", info.dwSize.X, info.dwSize.Y);
printf("dwSize=%d,%d\n", info.dwSize.X, info.dwSize.Y);
const HWND hwnd = GetConsoleWindow();
if (hwnd != NULL) {
RECT r = {};
if (GetWindowRect(hwnd, &r)) {
const int w = r.right - r.left;
const int h = r.bottom - r.top;
trace("hwnd: pos=(%d,%d) size=(%d,%d)", r.left, r.top, w, h);
printf("hwnd: pos=(%d,%d) size=(%d,%d)\n", r.left, r.top, w, h);
} else {
trace("GetWindowRect failed");
printf("GetWindowRect failed\n");
}
} else {
trace("GetConsoleWindow returned NULL");
printf("GetConsoleWindow returned NULL\n");
}
return 0;
}

261
misc/GetFont.cc Normal file
View File

@ -0,0 +1,261 @@
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>
#include "../src/shared/OsModule.h"
#include "../src/shared/StringUtil.h"
#include "TestUtil.cc"
#include "../src/shared/StringUtil.cc"
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
// Some of these types and functions are missing from the MinGW headers.
// Others are undocumented.
struct AGENT_CONSOLE_FONT_INFO {
DWORD nFont;
COORD dwFontSize;
};
struct AGENT_CONSOLE_FONT_INFOEX {
ULONG cbSize;
DWORD nFont;
COORD dwFontSize;
UINT FontFamily;
UINT FontWeight;
WCHAR FaceName[LF_FACESIZE];
};
// undocumented XP API
typedef BOOL WINAPI SetConsoleFont_t(
HANDLE hOutput,
DWORD dwFontIndex);
// undocumented XP API
typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
// XP and up
typedef BOOL WINAPI GetCurrentConsoleFont_t(
HANDLE hOutput,
BOOL bMaximumWindow,
AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
// XP and up
typedef COORD WINAPI GetConsoleFontSize_t(
HANDLE hConsoleOutput,
DWORD nFont);
// Vista and up
typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
HANDLE hConsoleOutput,
BOOL bMaximumWindow,
AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
// Vista and up
typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
HANDLE hConsoleOutput,
BOOL bMaximumWindow,
AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
#define GET_MODULE_PROC(mod, funcName) \
m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
#define DEFINE_ACCESSOR(funcName) \
funcName##_t &funcName() const { \
ASSERT(valid()); \
return *m_##funcName; \
}
class XPFontAPI {
public:
XPFontAPI() : m_kernel32(L"kernel32.dll") {
GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
}
bool valid() const {
return m_GetCurrentConsoleFont != NULL &&
m_GetConsoleFontSize != NULL;
}
DEFINE_ACCESSOR(GetCurrentConsoleFont)
DEFINE_ACCESSOR(GetConsoleFontSize)
private:
OsModule m_kernel32;
GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
GetConsoleFontSize_t *m_GetConsoleFontSize;
};
class UndocumentedXPFontAPI : public XPFontAPI {
public:
UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
GET_MODULE_PROC(m_kernel32, SetConsoleFont);
GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
}
bool valid() const {
return this->XPFontAPI::valid() &&
m_SetConsoleFont != NULL &&
m_GetNumberOfConsoleFonts != NULL;
}
DEFINE_ACCESSOR(SetConsoleFont)
DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
private:
OsModule m_kernel32;
SetConsoleFont_t *m_SetConsoleFont;
GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
};
class VistaFontAPI : public XPFontAPI {
public:
VistaFontAPI() : m_kernel32(L"kernel32.dll") {
GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
}
bool valid() const {
return this->XPFontAPI::valid() &&
m_GetCurrentConsoleFontEx != NULL &&
m_SetCurrentConsoleFontEx != NULL;
}
DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
private:
OsModule m_kernel32;
GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
};
static std::vector<std::pair<DWORD, COORD> > readFontTable(
XPFontAPI &api, HANDLE conout, DWORD maxCount) {
std::vector<std::pair<DWORD, COORD> > ret;
for (DWORD i = 0; i < maxCount; ++i) {
COORD size = api.GetConsoleFontSize()(conout, i);
if (size.X == 0 && size.Y == 0) {
break;
}
ret.push_back(std::make_pair(i, size));
}
return ret;
}
static void dumpFontTable(HANDLE conout) {
const int kMaxCount = 1000;
XPFontAPI api;
if (!api.valid()) {
printf("dumpFontTable: cannot dump font table -- missing APIs\n");
return;
}
std::vector<std::pair<DWORD, COORD> > table =
readFontTable(api, conout, kMaxCount);
std::string line;
char tmp[128];
size_t first = 0;
while (first < table.size()) {
size_t last = std::min(table.size() - 1, first + 10 - 1);
winpty_snprintf(tmp, "%02u-%02u:",
static_cast<unsigned>(first), static_cast<unsigned>(last));
line = tmp;
for (size_t i = first; i <= last; ++i) {
if (i % 10 == 5) {
line += " - ";
}
winpty_snprintf(tmp, " %2dx%-2d",
table[i].second.X, table[i].second.Y);
line += tmp;
}
printf("%s\n", line.c_str());
first = last + 1;
}
if (table.size() == kMaxCount) {
printf("... stopped reading at %d fonts ...\n", kMaxCount);
}
}
static std::string stringToCodePoints(const std::wstring &str) {
std::string ret = "(";
for (size_t i = 0; i < str.size(); ++i) {
char tmp[32];
winpty_snprintf(tmp, "%X", str[i]);
if (ret.size() > 1) {
ret.push_back(' ');
}
ret += tmp;
}
ret.push_back(')');
return ret;
}
static void dumpFontInfoEx(
const AGENT_CONSOLE_FONT_INFOEX &infoex) {
std::wstring faceName(infoex.FaceName,
winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
cprintf(L"nFont=%u dwFontSize=(%d,%d) "
"FontFamily=0x%x FontWeight=%u FaceName=%ls %hs\n",
static_cast<unsigned>(infoex.nFont),
infoex.dwFontSize.X, infoex.dwFontSize.Y,
infoex.FontFamily, infoex.FontWeight, faceName.c_str(),
stringToCodePoints(faceName).c_str());
}
static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, BOOL maxWindow) {
AGENT_CONSOLE_FONT_INFOEX infoex = {0};
infoex.cbSize = sizeof(infoex);
if (!api.GetCurrentConsoleFontEx()(conout, maxWindow, &infoex)) {
printf("GetCurrentConsoleFontEx call failed\n");
return;
}
dumpFontInfoEx(infoex);
}
static void dumpXPFont(XPFontAPI &api, HANDLE conout, BOOL maxWindow) {
AGENT_CONSOLE_FONT_INFO info = {0};
if (!api.GetCurrentConsoleFont()(conout, maxWindow, &info)) {
printf("GetCurrentConsoleFont call failed\n");
return;
}
printf("nFont=%u dwFontSize=(%d,%d)\n",
static_cast<unsigned>(info.nFont),
info.dwFontSize.X, info.dwFontSize.Y);
}
static void dumpFontAndTable(HANDLE conout) {
VistaFontAPI vista;
if (vista.valid()) {
printf("maxWnd=0: "); dumpVistaFont(vista, conout, FALSE);
printf("maxWnd=1: "); dumpVistaFont(vista, conout, TRUE);
dumpFontTable(conout);
return;
}
UndocumentedXPFontAPI xp;
if (xp.valid()) {
printf("maxWnd=0: "); dumpXPFont(xp, conout, FALSE);
printf("maxWnd=1: "); dumpXPFont(xp, conout, TRUE);
dumpFontTable(conout);
return;
}
printf("setSmallFont: neither Vista nor XP APIs detected -- giving up\n");
dumpFontTable(conout);
}
int main() {
const HANDLE conout = openConout();
const COORD largest = GetLargestConsoleWindowSize(conout);
printf("largestConsoleWindowSize=(%d,%d)\n", largest.X, largest.Y);
dumpFontAndTable(conout);
UndocumentedXPFontAPI xp;
if (xp.valid()) {
printf("GetNumberOfConsoleFonts returned %u\n", xp.GetNumberOfConsoleFonts()());
} else {
printf("The GetNumberOfConsoleFonts API was missing\n");
}
printf("CP=%u OutputCP=%u\n", GetConsoleCP(), GetConsoleOutputCP());
return 0;
}

51
misc/IdentifyConsoleWindow.ps1 Executable file
View File

@ -0,0 +1,51 @@
#
# Usage: powershell <path>\IdentifyConsoleWindow.ps1
#
# This script determines whether the process has a console attached, whether
# that console has a non-NULL window (e.g. HWND), and whether the window is on
# the current window station.
#
$signature = @'
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool SetConsoleTitle(String title);
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int GetWindowText(IntPtr hWnd,
System.Text.StringBuilder lpString,
int nMaxCount);
'@
$WinAPI = Add-Type -MemberDefinition $signature `
-Name WinAPI -Namespace IdentifyConsoleWindow -PassThru
if (!$WinAPI::SetConsoleTitle("ConsoleWindowScript")) {
echo "error: could not change console title -- is a console attached?"
exit 1
} else {
echo "note: successfully set console title to ""ConsoleWindowScript""."
}
$hwnd = $WinAPI::GetConsoleWindow()
if ($hwnd -eq 0) {
echo "note: GetConsoleWindow returned NULL."
} else {
echo "note: GetConsoleWindow returned 0x$($hwnd.ToString("X"))."
$sb = New-Object System.Text.StringBuilder -ArgumentList 4096
if ($WinAPI::GetWindowText($hwnd, $sb, $sb.Capacity)) {
$title = $sb.ToString()
echo "note: GetWindowText returned ""${title}""."
if ($title -eq "ConsoleWindowScript") {
echo "success!"
} else {
echo "error: expected to see ""ConsoleWindowScript""."
echo " (Perhaps the console window is on a different window station?)"
}
} else {
echo "error: GetWindowText could not read the window title."
echo " (Perhaps the console window is on a different window station?)"
}
}

87
misc/IsNewConsole.cc Normal file
View File

@ -0,0 +1,87 @@
// Determines whether this is a new console by testing whether MARK moves the
// cursor.
//
// WARNING: This test program may behave erratically if run under winpty.
//
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "TestUtil.cc"
const int SC_CONSOLE_MARK = 0xFFF2;
const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
static COORD getWindowPos(HANDLE conout) {
CONSOLE_SCREEN_BUFFER_INFO info = {};
BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
ASSERT(ret && "GetConsoleScreenBufferInfo failed");
return { info.srWindow.Left, info.srWindow.Top };
}
static COORD getWindowSize(HANDLE conout) {
CONSOLE_SCREEN_BUFFER_INFO info = {};
BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
ASSERT(ret && "GetConsoleScreenBufferInfo failed");
return {
static_cast<short>(info.srWindow.Right - info.srWindow.Left + 1),
static_cast<short>(info.srWindow.Bottom - info.srWindow.Top + 1)
};
}
static COORD getCursorPos(HANDLE conout) {
CONSOLE_SCREEN_BUFFER_INFO info = {};
BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
ASSERT(ret && "GetConsoleScreenBufferInfo failed");
return info.dwCursorPosition;
}
static void setCursorPos(HANDLE conout, COORD pos) {
BOOL ret = SetConsoleCursorPosition(conout, pos);
ASSERT(ret && "SetConsoleCursorPosition failed");
}
int main() {
const HANDLE conout = openConout();
const HWND hwnd = GetConsoleWindow();
ASSERT(hwnd != NULL && "GetConsoleWindow() returned NULL");
// With the legacy console, the Mark command moves the the cursor to the
// top-left cell of the visible console window. Determine whether this
// is the new console by seeing if the cursor moves.
const auto windowSize = getWindowSize(conout);
if (windowSize.X <= 1) {
printf("Error: console window must be at least 2 columns wide\n");
trace("Error: console window must be at least 2 columns wide");
return 1;
}
bool cursorMoved = false;
const auto initialPos = getCursorPos(conout);
const auto windowPos = getWindowPos(conout);
setCursorPos(conout, { static_cast<short>(windowPos.X + 1), windowPos.Y });
{
const auto posA = getCursorPos(conout);
SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
const auto posB = getCursorPos(conout);
cursorMoved = memcmp(&posA, &posB, sizeof(posA)) != 0;
SendMessage(hwnd, WM_CHAR, 27, 0x00010001); // Send ESCAPE
}
setCursorPos(conout, initialPos);
if (cursorMoved) {
printf("Legacy console (i.e. MARK moved cursor)\n");
trace("Legacy console (i.e. MARK moved cursor)");
} else {
printf("Windows 10 new console (i.e MARK did not move cursor)\n");
trace("Windows 10 new console (i.e MARK did not move cursor)");
}
return 0;
}

0
misc/MouseInputNotes.txt Executable file → Normal file
View File

34
misc/MoveConsoleWindow.cc Normal file
View File

@ -0,0 +1,34 @@
#include <windows.h>
#include "TestUtil.cc"
int main(int argc, char *argv[]) {
if (argc != 3 && argc != 5) {
printf("Usage: %s x y\n", argv[0]);
printf("Usage: %s x y width height\n", argv[0]);
return 1;
}
HWND hwnd = GetConsoleWindow();
const int x = atoi(argv[1]);
const int y = atoi(argv[2]);
int w = 0, h = 0;
if (argc == 3) {
RECT r = {};
BOOL ret = GetWindowRect(hwnd, &r);
ASSERT(ret && "GetWindowRect failed on console window");
w = r.right - r.left;
h = r.bottom - r.top;
} else {
w = atoi(argv[3]);
h = atoi(argv[4]);
}
BOOL ret = MoveWindow(hwnd, x, y, w, h, TRUE);
trace("MoveWindow: ret=%d", ret);
printf("MoveWindow: ret=%d\n", ret);
return 0;
}

View File

@ -1,75 +0,0 @@
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
static void queryCurrentConsoleFont(HANDLE conout, BOOL max) {
CONSOLE_FONT_INFO info = {0};
if (!GetCurrentConsoleFont(conout, max, &info)) {
cprintf(L"GetCurrentConsoleFont call failed\n");
} else {
cprintf(L"info(max=%d): nFont=%u dwFontSize=(%d,%d)\n",
max, static_cast<unsigned>(info.nFont),
info.dwFontSize.X, info.dwFontSize.Y);
}
}
static void queryCurrentConsoleFontEx(HANDLE conout, BOOL max) {
CONSOLE_FONT_INFOEX infoex = {0};
infoex.cbSize = sizeof(infoex);
if (!GetCurrentConsoleFontEx(conout, max, &infoex)) {
cprintf(L"GetCurrentConsoleFontEx call failed\n");
} else {
wchar_t faceName[LF_FACESIZE + 1];
memcpy(faceName, infoex.FaceName, sizeof(faceName));
faceName[LF_FACESIZE] = L'\0';
cprintf(L"infoex(max=%d): nFont=%u dwFontSize=(%d,%d) "
L"FontFamily=0x%x FontWeight=%u "
L"FaceName=\"%ls\"",
max, static_cast<unsigned>(infoex.nFont),
infoex.dwFontSize.X, infoex.dwFontSize.Y,
infoex.FontFamily, infoex.FontWeight,
faceName);
cprintf(L" (");
for (int i = 0; i < LF_FACESIZE; ++i) {
if (i > 0) {
cprintf(L" ");
}
cprintf(L"%X", infoex.FaceName[i]);
if (infoex.FaceName[i] == L'\0') {
break;
}
}
cprintf(L")\n");
}
}
int main() {
const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
queryCurrentConsoleFont(conout, FALSE);
queryCurrentConsoleFont(conout, TRUE);
queryCurrentConsoleFontEx(conout, FALSE);
queryCurrentConsoleFontEx(conout, TRUE);
const COORD largest = GetLargestConsoleWindowSize(conout);
cprintf(L"largestConsoleWindowSize=(%d,%d)\n", largest.X, largest.Y);
for (int i = 0;; ++i) {
const COORD size = GetConsoleFontSize(conout, i);
if (size.X == 0 && size.Y == 0) {
break;
}
cprintf(L"font %d: %dx%d\n", i, size.X, size.Y);
}
HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
FARPROC proc = GetProcAddress(kernel32, "GetNumberOfConsoleFonts");
if (proc == NULL) {
cprintf(L"Could not get address of GetNumberOfConsoleFonts\n");
} else {
cprintf(L"GetNumberOfConsoleFonts returned %d\n",
reinterpret_cast<int WINAPI(*)(HANDLE)>(proc)(conout));
}
cprintf(L"InputCP=%u OutputCP=%u", GetConsoleCP(), GetConsoleOutputCP());
return 0;
}

View File

@ -0,0 +1,101 @@
//
// Verify that console selection blocks writes to an inactive console screen
// buffer. Writes TEST PASSED or TEST FAILED to the popup console window.
//
#include <windows.h>
#include <stdio.h>
#include <string>
#include "TestUtil.cc"
const int SC_CONSOLE_MARK = 0xFFF2;
const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
bool g_useMark = false;
CALLBACK DWORD pausingThread(LPVOID dummy)
{
HWND hwnd = GetConsoleWindow();
trace("Sending selection to freeze");
SendMessage(hwnd, WM_SYSCOMMAND,
g_useMark ? SC_CONSOLE_MARK :
SC_CONSOLE_SELECT_ALL,
0);
Sleep(1000);
trace("Sending escape WM_CHAR to unfreeze");
SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
Sleep(1000);
}
static HANDLE createBuffer() {
HANDLE buf = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL);
ASSERT(buf != INVALID_HANDLE_VALUE);
return buf;
}
static void runTest(bool useMark, bool createEarly) {
trace("=======================================");
trace("useMark=%d createEarly=%d", useMark, createEarly);
g_useMark = useMark;
HANDLE buf = INVALID_HANDLE_VALUE;
if (createEarly) {
buf = createBuffer();
}
CreateThread(NULL, 0,
pausingThread, NULL,
0, NULL);
Sleep(500);
if (!createEarly) {
trace("Creating buffer");
TimeMeasurement tm1;
buf = createBuffer();
const double elapsed1 = tm1.elapsed();
if (elapsed1 >= 0.250) {
printf("!!! TEST FAILED !!!\n");
Sleep(2000);
return;
}
}
trace("Writing to aux buffer");
TimeMeasurement tm2;
DWORD actual = 0;
BOOL ret = WriteConsoleW(buf, L"HI", 2, &actual, NULL);
const double elapsed2 = tm2.elapsed();
trace("Writing to aux buffer: finished: ret=%d actual=%d (elapsed=%1.3f)", ret, actual, elapsed2);
if (elapsed2 < 0.250) {
printf("!!! TEST FAILED !!!\n");
} else {
printf("TEST PASSED\n");
}
Sleep(2000);
}
int main(int argc, char **argv) {
if (argc == 1) {
startChildProcess(L"child");
return 0;
}
std::string arg = argv[1];
if (arg == "child") {
for (int useMark = 0; useMark <= 1; useMark++) {
for (int createEarly = 0; createEarly <= 1; createEarly++) {
runTest(useMark, createEarly);
}
}
printf("done...\n");
Sleep(1000);
}
return 0;
}

1
misc/ScreenBufferTest.cc Executable file → Normal file
View File

@ -52,7 +52,6 @@
#include <string.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
const char *g_prefix = "";

View File

@ -1,7 +1,6 @@
#include <windows.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
const char *g_prefix = "";

90
misc/SetBufInfo.cc Executable file
View File

@ -0,0 +1,90 @@
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "TestUtil.cc"
static void usage() {
printf("usage: SetBufInfo [-set] [-buf W H] [-win W H] [-pos X Y]\n");
}
int main(int argc, char *argv[]) {
const HANDLE conout = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
ASSERT(conout != INVALID_HANDLE_VALUE);
bool change = false;
BOOL success;
CONSOLE_SCREEN_BUFFER_INFOEX info = {};
info.cbSize = sizeof(info);
success = GetConsoleScreenBufferInfoEx(conout, &info);
ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
for (int i = 1; i < argc; ) {
std::string arg = argv[i];
if (arg == "-buf" && (i + 2) < argc) {
info.dwSize.X = atoi(argv[i + 1]);
info.dwSize.Y = atoi(argv[i + 2]);
i += 3;
change = true;
} else if (arg == "-pos" && (i + 2) < argc) {
int dx = info.srWindow.Right - info.srWindow.Left;
int dy = info.srWindow.Bottom - info.srWindow.Top;
info.srWindow.Left = atoi(argv[i + 1]);
info.srWindow.Top = atoi(argv[i + 2]);
i += 3;
info.srWindow.Right = info.srWindow.Left + dx;
info.srWindow.Bottom = info.srWindow.Top + dy;
change = true;
} else if (arg == "-win" && (i + 2) < argc) {
info.srWindow.Right = info.srWindow.Left + atoi(argv[i + 1]) - 1;
info.srWindow.Bottom = info.srWindow.Top + atoi(argv[i + 2]) - 1;
i += 3;
change = true;
} else if (arg == "-set") {
change = true;
++i;
} else if (arg == "--help" || arg == "-help") {
usage();
exit(0);
} else {
fprintf(stderr, "error: unrecognized argument: %s\n", arg.c_str());
usage();
exit(1);
}
}
if (change) {
success = SetConsoleScreenBufferInfoEx(conout, &info);
if (success) {
printf("success\n");
} else {
printf("SetConsoleScreenBufferInfoEx call failed\n");
}
success = GetConsoleScreenBufferInfoEx(conout, &info);
ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
}
auto dump = [](const char *fmt, ...) {
char msg[256];
va_list ap;
va_start(ap, fmt);
vsprintf(msg, fmt, ap);
va_end(ap);
trace("%s", msg);
printf("%s\n", msg);
};
dump("buffer-size: %d x %d", info.dwSize.X, info.dwSize.Y);
dump("window-size: %d x %d",
info.srWindow.Right - info.srWindow.Left + 1,
info.srWindow.Bottom - info.srWindow.Top + 1);
dump("window-pos: %d, %d", info.srWindow.Left, info.srWindow.Top);
return 0;
}

32
misc/SetBufferSize.cc Normal file
View File

@ -0,0 +1,32 @@
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "TestUtil.cc"
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Usage: %s x y width height\n", argv[0]);
return 1;
}
const HANDLE conout = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
ASSERT(conout != INVALID_HANDLE_VALUE);
COORD size = {
(short)atoi(argv[1]),
(short)atoi(argv[2]),
};
BOOL ret = SetConsoleScreenBufferSize(conout, size);
const unsigned lastError = GetLastError();
const char *const retStr = ret ? "OK" : "failed";
trace("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)", retStr, lastError);
printf("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)\n", retStr, lastError);
return 0;
}

1
misc/SetCursorPos.cc Executable file → Normal file
View File

@ -1,7 +1,6 @@
#include <windows.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
int main(int argc, char *argv[]) {
int col = atoi(argv[1]);

View File

@ -5,32 +5,21 @@
#include <string>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
// Attempt to set the console font to the given facename and pixel size.
// These APIs should exist on Vista and up.
static void setConsoleFont(const wchar_t *faceName, int pixelSize)
{
CONSOLE_FONT_INFOEX fontex = {0};
fontex.cbSize = sizeof(fontex);
fontex.FontWeight = 400;
fontex.dwFontSize.Y = pixelSize;
wcsncpy(fontex.FaceName, faceName, COUNT_OF(fontex.FaceName));
fontex.nFont = 34;
BOOL ret = SetCurrentConsoleFontEx(
GetStdHandle(STD_OUTPUT_HANDLE),
FALSE,
&fontex);
cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret);
}
// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
int main() {
setlocale(LC_ALL, "");
wchar_t *cmdline = GetCommandLineW();
int argc = 0;
wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
const HANDLE conout = openConout();
if (argc == 1) {
cprintf(L"Usage:\n");
@ -41,8 +30,10 @@ int main() {
cprintf(L" -idx INDEX\n");
cprintf(L" -w WIDTH\n");
cprintf(L" -h HEIGHT\n");
cprintf(L" -family (0xNN|NN)\n");
cprintf(L" -weight (normal|bold|NNN)\n");
cprintf(L" -face FACENAME\n");
cprintf(L" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
cprintf(L" -tt\n");
cprintf(L" -vec\n");
cprintf(L" -vp\n");
@ -62,7 +53,6 @@ int main() {
if (proc == NULL) {
cprintf(L"Couldn't get address of SetConsoleFont\n");
} else {
const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
BOOL ret = reinterpret_cast<BOOL WINAPI(*)(HANDLE, DWORD)>(proc)(
conout, index);
cprintf(L"SetFont returned %d\n", ret);
@ -98,6 +88,9 @@ int main() {
} else if (arg == L"-face") {
wcsncpy(fontex.FaceName, next.c_str(), COUNT_OF(fontex.FaceName));
++i; continue;
} else if (arg == L"-family") {
fontex.FontFamily = strtol(narrowString(next).c_str(), nullptr, 0);
++i; continue;
}
}
if (arg == L"-tt") {
@ -121,11 +114,13 @@ int main() {
} else if (arg == L"-decorative") {
fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_DECORATIVE;
} else if (arg == L"-face-gothic") {
// ゴシック
const wchar_t gothicFace[] = {
0xFF2D, 0xFF33, 0x20, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0x0
};
wcsncpy(fontex.FaceName, gothicFace, COUNT_OF(fontex.FaceName));
wcsncpy(fontex.FaceName, kMSGothic, COUNT_OF(fontex.FaceName));
} else if (arg == L"-face-simsun") {
wcsncpy(fontex.FaceName, kNSimSun, COUNT_OF(fontex.FaceName));
} else if (arg == L"-face-minglight") {
wcsncpy(fontex.FaceName, kMingLight, COUNT_OF(fontex.FaceName));
} else if (arg == L"-face-gulimche") {
wcsncpy(fontex.FaceName, kGulimChe, COUNT_OF(fontex.FaceName));
} else {
cprintf(L"Unrecognized argument: %ls\n", arg.c_str());
exit(1);
@ -141,7 +136,7 @@ int main() {
fontex.FaceName);
BOOL ret = SetCurrentConsoleFontEx(
GetStdHandle(STD_OUTPUT_HANDLE),
conout,
FALSE,
&fontex);
cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret);

36
misc/SetWindowRect.cc Normal file
View File

@ -0,0 +1,36 @@
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "TestUtil.cc"
int main(int argc, char *argv[]) {
if (argc != 5) {
printf("Usage: %s x y width height\n", argv[0]);
return 1;
}
const HANDLE conout = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
ASSERT(conout != INVALID_HANDLE_VALUE);
SMALL_RECT sr = {
(short)atoi(argv[1]),
(short)atoi(argv[2]),
(short)(atoi(argv[1]) + atoi(argv[3]) - 1),
(short)(atoi(argv[2]) + atoi(argv[4]) - 1),
};
trace("Calling SetConsoleWindowInfo with {L=%d,T=%d,R=%d,B=%d}",
sr.Left, sr.Top, sr.Right, sr.Bottom);
BOOL ret = SetConsoleWindowInfo(conout, TRUE, &sr);
const unsigned lastError = GetLastError();
const char *const retStr = ret ? "OK" : "failed";
trace("SetConsoleWindowInfo ret: %s (LastError=0x%x)", retStr, lastError);
printf("SetConsoleWindowInfo ret: %s (LastError=0x%x)\n", retStr, lastError);
return 0;
}

0
misc/Spew.py Normal file → Executable file
View File

14
misc/TestUtil.cc Executable file → Normal file
View File

@ -9,6 +9,11 @@
#include <string>
#include "../src/shared/DebugClient.h"
#include "../src/shared/TimeMeasurement.h"
#include "../src/shared/DebugClient.cc"
#include "../src/shared/WinptyAssert.cc"
#include "../src/shared/WinptyException.cc"
// Launch this test program again, in a new console that we will destroy.
static void startChildProcess(const wchar_t *args) {
@ -156,3 +161,12 @@ static std::string narrowString(const std::wstring &input)
assert(mblen2 == mblen);
return std::string(tmp.data(), tmp.size());
}
HANDLE openConout() {
const HANDLE conout = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
ASSERT(conout != INVALID_HANDLE_VALUE);
return conout;
}

View File

@ -16,7 +16,6 @@
#include <string.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))

1
misc/UnicodeWideTest1.cc Executable file → Normal file
View File

@ -4,7 +4,6 @@
#include <vector>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))

1
misc/UnicodeWideTest2.cc Executable file → Normal file
View File

@ -8,7 +8,6 @@
#include <stdio.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
static void writeChars(const wchar_t *text) {
wcslen(text);

View File

@ -28,9 +28,9 @@ int main(int argc, char *argv[]) {
// Read it back.
std::vector<CHAR_INFO> readBuffer(dataToWrite.size() * 2);
COORD bufSize = {readBuffer.size(), 1};
COORD bufSize = {static_cast<short>(readBuffer.size()), 1};
COORD bufCoord = {0, 0};
SMALL_RECT topLeft = {0, 0, readBuffer.size() - 1, 0};
SMALL_RECT topLeft = {0, 0, static_cast<short>(readBuffer.size() - 1), 0};
ret = ReadConsoleOutputW(
GetStdHandle(STD_OUTPUT_HANDLE), readBuffer.data(),
bufSize, bufCoord, &topLeft);

1
misc/VeryLargeRead.cc Executable file → Normal file
View File

@ -63,7 +63,6 @@
#include <vector>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
int main(int argc, char *argv[]) {
long long width = 9000;

2
misc/Win10ResizeWhileFrozen.cc Executable file → Normal file
View File

@ -14,7 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include "../src/shared/DebugClient.cc"
#include "TestUtil.cc"
const int SC_CONSOLE_MARK = 0xFFF2;

1
misc/Win10WrapTest1.cc Executable file → Normal file
View File

@ -6,7 +6,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "../src/shared/DebugClient.cc"
#include "TestUtil.cc"
int main(int argc, char *argv[]) {

1
misc/Win10WrapTest2.cc Executable file → Normal file
View File

@ -1,7 +1,6 @@
#include <windows.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
int main(int argc, char *argv[]) {
if (argc == 1) {

View File

@ -0,0 +1,27 @@
// I noticed this on the ConEmu web site:
//
// https://social.msdn.microsoft.com/Forums/en-US/40c8e395-cca9-45c8-b9b8-2fbe6782ac2b/readconsoleoutput-cause-access-violation-writing-location-exception
// https://conemu.github.io/en/MicrosoftBugs.html
//
// In Windows 7, 8, and 8.1, a ReadConsoleOutputW with an out-of-bounds read
// region crashes the application. I have reproduced the problem on Windows 8
// and 8.1, but not on Windows 7.
//
#include <windows.h>
#include "TestUtil.cc"
int main() {
setWindowPos(0, 0, 1, 1);
setBufferSize(80, 25);
setWindowPos(0, 0, 80, 25);
const HANDLE conout = openConout();
static CHAR_INFO lineBuf[80];
SMALL_RECT readRegion = { 0, 999, 79, 999 };
const BOOL ret = ReadConsoleOutputW(conout, lineBuf, {80, 1}, {0, 0}, &readRegion);
ASSERT(!ret && "ReadConsoleOutputW should have failed");
return 0;
}

106
misc/WriteConsole.cc Executable file
View File

@ -0,0 +1,106 @@
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include <vector>
static std::wstring mbsToWcs(const std::string &s) {
const size_t len = mbstowcs(nullptr, s.c_str(), 0);
if (len == static_cast<size_t>(-1)) {
assert(false && "mbsToWcs: invalid string");
}
std::wstring ret;
ret.resize(len);
const size_t len2 = mbstowcs(&ret[0], s.c_str(), len);
assert(len == len2);
return ret;
}
uint32_t parseHex(wchar_t ch, bool &invalid) {
if (ch >= L'0' && ch <= L'9') {
return ch - L'0';
} else if (ch >= L'a' && ch <= L'f') {
return ch - L'a' + 10;
} else if (ch >= L'A' && ch <= L'F') {
return ch - L'A' + 10;
} else {
invalid = true;
return 0;
}
}
int main(int argc, char *argv[]) {
std::vector<std::wstring> args;
for (int i = 1; i < argc; ++i) {
args.push_back(mbsToWcs(argv[i]));
}
std::wstring out;
for (const auto &arg : args) {
if (!out.empty()) {
out.push_back(L' ');
}
for (size_t i = 0; i < arg.size(); ++i) {
wchar_t ch = arg[i];
wchar_t nch = i + 1 < arg.size() ? arg[i + 1] : L'\0';
if (ch == L'\\') {
switch (nch) {
case L'a': ch = L'\a'; ++i; break;
case L'b': ch = L'\b'; ++i; break;
case L'e': ch = L'\x1b'; ++i; break;
case L'f': ch = L'\f'; ++i; break;
case L'n': ch = L'\n'; ++i; break;
case L'r': ch = L'\r'; ++i; break;
case L't': ch = L'\t'; ++i; break;
case L'v': ch = L'\v'; ++i; break;
case L'\\': ch = L'\\'; ++i; break;
case L'\'': ch = L'\''; ++i; break;
case L'\"': ch = L'\"'; ++i; break;
case L'\?': ch = L'\?'; ++i; break;
case L'x':
if (i + 3 < arg.size()) {
bool invalid = false;
uint32_t d1 = parseHex(arg[i + 2], invalid);
uint32_t d2 = parseHex(arg[i + 3], invalid);
if (!invalid) {
i += 3;
ch = (d1 << 4) | d2;
}
}
break;
case L'u':
if (i + 5 < arg.size()) {
bool invalid = false;
uint32_t d1 = parseHex(arg[i + 2], invalid);
uint32_t d2 = parseHex(arg[i + 3], invalid);
uint32_t d3 = parseHex(arg[i + 4], invalid);
uint32_t d4 = parseHex(arg[i + 5], invalid);
if (!invalid) {
i += 5;
ch = (d1 << 24) | (d2 << 16) | (d3 << 8) | d4;
}
}
break;
default: break;
}
}
out.push_back(ch);
}
}
DWORD actual = 0;
if (!WriteConsoleW(
GetStdHandle(STD_OUTPUT_HANDLE),
out.c_str(),
out.size(),
&actual,
nullptr)) {
fprintf(stderr, "WriteConsole failed (is stdout a console?)\n");
exit(1);
}
return 0;
}

View File

@ -1 +0,0 @@
/build

View File

@ -1,101 +0,0 @@
#include <TestCommon.h>
REGISTER(Test_CreateProcess_ModeCombos, always);
static void Test_CreateProcess_ModeCombos() {
// It is often unclear how (or whether) various combinations of
// CreateProcess parameters work when combined. Try to test the ambiguous
// combinations.
SpawnFailure failure;
{
// CREATE_NEW_CONSOLE | DETACHED_PROCESS ==> call fails
Worker p;
auto c = p.tryChild({ false, CREATE_NEW_CONSOLE | DETACHED_PROCESS }, &failure);
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// CREATE_NO_WINDOW | CREATE_NEW_CONSOLE ==> CREATE_NEW_CONSOLE dominates
Worker p;
auto c = p.tryChild({ false, CREATE_NO_WINDOW | CREATE_NEW_CONSOLE }, &failure);
CHECK(c.valid());
CHECK(c.consoleWindow() != nullptr);
CHECK(IsWindowVisible(c.consoleWindow()));
}
{
// CREATE_NO_WINDOW | DETACHED_PROCESS ==> DETACHED_PROCESS dominates
Worker p;
auto c = p.tryChild({ false, CREATE_NO_WINDOW | DETACHED_PROCESS }, &failure);
CHECK(c.valid());
CHECK_EQ(c.newBuffer().value(), INVALID_HANDLE_VALUE);
}
}
REGISTER(Test_CreateProcess_STARTUPINFOEX, isAtLeastVista);
static void Test_CreateProcess_STARTUPINFOEX() {
// STARTUPINFOEX tests.
Worker p;
SpawnFailure failure;
auto pipe1 = newPipe(p, true);
auto ph1 = std::get<0>(pipe1);
auto ph2 = std::get<1>(pipe1);
auto testSetup = [&](SpawnParams sp, size_t cb, HANDLE inherit) {
sp.sui.cb = cb;
sp.inheritCount = 1;
sp.inheritList = { inherit };
return p.tryChild(sp, &failure);
};
{
// The STARTUPINFOEX parameter is ignored if
// EXTENDED_STARTUPINFO_PRESENT isn't present.
auto c = testSetup({true}, sizeof(STARTUPINFOEXW), ph1.value());
CHECK(c.valid());
auto ch2 = Handle::invent(ph2.value(), c);
// i.e. ph2 was inherited, because ch2 identifies the same thing.
CHECK(compareObjectHandles(ph2, ch2));
}
{
// If EXTENDED_STARTUPINFO_PRESENT is specified, but the cb value
// is wrong, the API call fails.
auto c = testSetup({true, EXTENDED_STARTUPINFO_PRESENT},
sizeof(STARTUPINFOW), ph1.value());
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
}
REGISTER(Test_CreateNoWindow_HiddenVsNothing, always);
static void Test_CreateNoWindow_HiddenVsNothing() {
Worker p;
auto c = p.child({ false, CREATE_NO_WINDOW });
if (isAtLeastWin7()) {
// As of Windows 7, GetConsoleWindow returns NULL.
CHECK(c.consoleWindow() == nullptr);
} else {
// On earlier operating systems, GetConsoleWindow returns a handle
// to an invisible window.
CHECK(c.consoleWindow() != nullptr);
CHECK(!IsWindowVisible(c.consoleWindow()));
}
}
// MSDN's CreateProcess page currently has this note in it:
//
// Important The caller is responsible for ensuring that the standard
// handle fields in STARTUPINFO contain valid handle values. These fields
// are copied unchanged to the child process without validation, even when
// the dwFlags member specifies STARTF_USESTDHANDLES. Incorrect values can
// cause the child process to misbehave or crash. Use the Application
// Verifier runtime verification tool to detect invalid handles.
//
// XXX: The word "even" here sticks out. Verify that the standard handle
// fields in STARTUPINFO are ignored when STARTF_USESTDHANDLES is not
// specified.

View File

@ -1,66 +0,0 @@
#include <TestCommon.h>
// Test CreateProcess called with dwCreationFlags containing DETACHED_PROCESS.
// This macro will output nicer line information than a function if it fails.
#define CHECK_NULL(proc) \
do { \
CHECK(handleInts(stdHandles(proc)) == \
(std::vector<uint64_t> {0,0,0})); \
} while(0)
REGISTER(Test_CreateProcess_Detached, always);
static void Test_CreateProcess_Detached() {
{
Worker p;
auto c1 = p.child({ true, DETACHED_PROCESS });
CHECK_NULL(c1);
auto c2 = p.child({ false, DETACHED_PROCESS });
CHECK_NULL(c2);
}
{
Worker p;
auto c = p.child({ true, DETACHED_PROCESS, {
p.getStdin(),
p.getStdout(),
p.getStderr(),
}});
CHECK(handleValues(stdHandles(c)) == handleValues(stdHandles(p)));
}
{
Worker p;
auto c = p.child({ false, DETACHED_PROCESS, {
p.getStdin(),
p.getStdout(),
p.getStderr(),
}});
if (isTraditionalConio()) {
CHECK(handleValues(stdHandles(c)) == handleValues(stdHandles(p)));
} else{
CHECK_NULL(c);
}
}
{
Worker p({ false, DETACHED_PROCESS });
auto pipe = newPipe(p, true);
std::get<0>(pipe).setStdin();
std::get<1>(pipe).setStdout().setStderr();
{
auto c1 = p.child({ true, DETACHED_PROCESS });
CHECK_NULL(c1);
auto c2 = p.child({ false, DETACHED_PROCESS });
CHECK_NULL(c2);
}
{
// The worker p2 was started with STARTF_USESTDHANDLES and with
// standard handles referring to a pipe. Nevertheless, its
// children's standard handles are NULL.
auto p2 = p.child({ true, DETACHED_PROCESS, stdHandles(p) });
auto c1 = p2.child({ true, DETACHED_PROCESS });
CHECK_NULL(c1);
auto c2 = p2.child({ false, DETACHED_PROCESS });
CHECK_NULL(c2);
}
}
}

View File

@ -1,212 +0,0 @@
#include <TestCommon.h>
// If CreateProcess is called with these parameters:
// - bInheritHandles=FALSE
// - STARTF_USESTDHANDLES is not specified
// - the "CreationConsoleMode" is Inherit (see console-handles.md)
// then Windows duplicates each of STDIN/STDOUT/STDERR to the child.
//
// There are variations between OS releases, especially with regards to
// how console handles work.
// This handle duplication seems to be broken in WOW64 mode. It affects
// at least:
// - Windows 7 SP1
// For some reason, the problem apparently only affects the client operating
// system, not the server OS.
//
// Export this function to the other duplicate tests.
bool brokenDuplicationInWow64() {
return isWin7() && isWorkstation() && isWow64();
}
namespace {
static bool handlesAreNull(Worker &p) {
return handleInts(stdHandles(p)) == std::vector<uint64_t> {0, 0, 0};
}
static std::string testMessage(bool isNull) {
return isNull ? "BUG(dup->NULL)" : "OK(dup)";
}
template <typename T>
void Test_CreateProcess_Duplicate_Impl(T makeChild) {
printTestName(__FUNCTION__);
{
// An inheritable pipe is still inherited.
Worker p;
auto pipe = newPipe(p, true);
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
CHECK(wh.inheritable());
auto c = makeChild(p, { false });
const auto expect = testMessage(brokenDuplicationInWow64());
const auto actual = testMessage(handlesAreNull(c));
std::cout << __FUNCTION__ << ": expect: " << expect << std::endl;
std::cout << __FUNCTION__ << ": actual: " << actual << std::endl;
CHECK_EQ(actual, expect);
if (c.getStdout().value() != nullptr) {
{
ObjectSnap snap;
CHECK(snap.eq({ c.getStdin(), c.getStdout(), c.getStderr(), wh }));
}
for (auto h : stdHandles(c)) {
CHECK(h.tryFlags());
if (!h.tryFlags()) {
continue;
}
auto inheritMessage = [](bool inheritable) {
return inheritable
? "OK(inherit)"
: "BAD(dup->non-inheritable)";
};
const std::string expect = inheritMessage(isAtLeastVista());
const std::string actual = inheritMessage(h.inheritable());
if (expect == actual && isAtLeastVista()) {
continue; // We'll just stay silent in this case.
}
std::cout << __FUNCTION__ << ": expect: " << expect << std::endl;
std::cout << __FUNCTION__ << ": actual: " << actual << std::endl;
CHECK_EQ(actual, expect);
}
}
}
{
// A non-inheritable pipe is still inherited.
Worker p;
auto pipe = newPipe(p, false);
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
auto c = makeChild(p, { false });
const auto expect = testMessage(brokenDuplicationInWow64());
const auto actual = testMessage(handlesAreNull(c));
std::cout << __FUNCTION__ << ": expect: " << expect << std::endl;
std::cout << __FUNCTION__ << ": actual: " << actual << std::endl;
CHECK_EQ(actual, expect);
if (c.getStdout().value() != nullptr) {
{
ObjectSnap snap;
CHECK(snap.eq({ c.getStdin(), c.getStdout(), c.getStderr(), wh }));
}
// CreateProcess makes separate handles for stdin/stdout/stderr,
// even though the parent has the same handle for each of them.
CHECK(c.getStdin().value() != c.getStdout().value());
CHECK(c.getStdout().value() != c.getStderr().value());
CHECK(c.getStdin().value() != c.getStderr().value());
for (auto h : stdHandles(c)) {
CHECK(h.tryFlags() && !h.inheritable());
}
// Calling FreeConsole in the child does not free the duplicated
// handles.
c.detach();
{
ObjectSnap snap;
CHECK(snap.eq({ c.getStdin(), c.getStdout(), c.getStderr(), wh }));
}
}
}
{
// Bogus values are transformed into zero.
Worker p;
Handle::invent(0x10000ull, p).setStdin().setStdout();
Handle::invent(0x0ull, p).setStderr();
auto c = makeChild(p, { false });
CHECK(handleInts(stdHandles(c)) == (std::vector<uint64_t> {0,0,0}));
}
if (isAtLeastWin8()) {
// On Windows 8 and up, if a standard handle we duplicate just happens
// to be a console handle, that isn't sufficient reason for FreeConsole
// to close it.
Worker p;
auto c = makeChild(p, { false });
auto ph = stdHandles(p);
auto ch = stdHandles(c);
auto check = [&]() {
ObjectSnap snap;
for (int i = 0; i < 3; ++i) {
CHECK(snap.eq(ph[i], ch[i]));
CHECK(ph[i].tryFlags() && ch[i].tryFlags());
CHECK_EQ(ph[i].tryFlags() && ph[i].inheritable(),
ch[i].tryFlags() && ch[i].inheritable());
}
};
check();
c.detach();
check();
}
{
// Traditional console-like values are passed through as-is,
// up to 0x0FFFFFFFull.
Worker p;
Handle::invent(0x0FFFFFFFull, p).setStdin();
Handle::invent(0x10000003ull, p).setStdout();
Handle::invent(0x00000003ull, p).setStderr();
auto c = makeChild(p, { false });
if (isAtLeastWin8()) {
// These values are invalid on Windows 8 and turned into NULL.
CHECK(handleInts(stdHandles(c)) ==
(std::vector<uint64_t> { 0, 0, 0 }));
} else {
CHECK(handleInts(stdHandles(c)) ==
(std::vector<uint64_t> { 0x0FFFFFFFull, 0, 3 }));
}
}
{
// Test setting STDIN/STDOUT/STDERR to non-inheritable console handles.
//
// Handle duplication does not apply to traditional console handles,
// and a console handle is inherited if and only if it is inheritable.
//
// On new releases, this will Just Work.
//
Worker p;
p.getStdout().setFirstChar('A');
p.openConin(false).setStdin();
p.newBuffer(false, 'B').setStdout().setStderr();
auto c = makeChild(p, { false });
if (!isAtLeastWin8()) {
CHECK(handleValues(stdHandles(p)) == handleValues(stdHandles(c)));
CHECK(!c.getStdin().tryFlags());
CHECK(!c.getStdout().tryFlags());
CHECK(!c.getStderr().tryFlags());
} else {
// In Win8, a console handle works like all other handles.
CHECK_EQ(c.getStdout().firstChar(), 'B');
ObjectSnap snap;
CHECK(snap.eq({ p.getStdout(), p.getStderr(),
c.getStdout(), c.getStderr() }));
CHECK(!c.getStdout().inheritable());
CHECK(!c.getStderr().inheritable());
}
}
}
} // anonymous namespace
REGISTER(Test_CreateProcess_Duplicate, always);
static void Test_CreateProcess_Duplicate() {
Test_CreateProcess_Duplicate_Impl([](Worker &p, SpawnParams sp) {
return p.child(sp);
});
if (isModernConio()) {
// With modern console I/O, calling CreateProcess with these
// parameters also duplicates standard handles:
// - bInheritHandles=TRUE
// - STARTF_USESTDHANDLES not specified
// - an inherit list (PROC_THREAD_ATTRIBUTE_HANDLE_LIST) is specified
Test_CreateProcess_Duplicate_Impl([](Worker &p, SpawnParams sp) {
return childWithDummyInheritList(p, sp, false);
});
Test_CreateProcess_Duplicate_Impl([](Worker &p, SpawnParams sp) {
return childWithDummyInheritList(p, sp, true);
});
}
}

View File

@ -1,81 +0,0 @@
#include <TestCommon.h>
// With CreateProcess's default handle duplication behavior, the
// GetCurrentProcess() psuedo-handle (i.e. INVALID_HANDLE_VALUE) is translated
// to a real handle value for the child process. It is a handle to the parent
// process. Naturally, this was unintended behavior, and as of Windows 8.1,
// the handle is instead translated to NULL. On some older operating systems,
// the WOW64 mode also translates it to NULL.
const std::string bugParentProc = "BUG(parent-proc)";
const std::string okInvalid = "OK(INVALID)";
const std::string okNull = "OK(NULL)";
static std::string determineChildStdout(Worker &c, Worker &p) {
if (c.getStdout().value() == nullptr) {
return okNull;
} else if (c.getStdout().value() == INVALID_HANDLE_VALUE) {
return okInvalid;
} else {
auto handleToPInP = Handle::dup(p.processHandle(), p);
CHECK(compareObjectHandles(c.getStdout(), handleToPInP));
return bugParentProc;
}
}
REGISTER(Test_CreateProcess_Duplicate_PseudoHandleBug, always);
static void Test_CreateProcess_Duplicate_PseudoHandleBug() {
Worker p;
Handle::invent(GetCurrentProcess(), p).setStdout();
auto c = p.child({ false });
const std::string expect =
(isAtLeastWin8_1() || (isAtLeastVista() && isWow64()))
? okNull
: bugParentProc;
const std::string actual = determineChildStdout(c, p);
trace("%s: actual: %s", __FUNCTION__, actual.c_str());
std::cout << __FUNCTION__ << ": expect: " << expect << std::endl;
std::cout << __FUNCTION__ << ": actual: " << actual << std::endl;
CHECK_EQ(actual, expect);
}
REGISTER(Test_CreateProcess_Duplicate_PseudoHandleBug_IL, isAtLeastVista);
static void Test_CreateProcess_Duplicate_PseudoHandleBug_IL() {
// As above, but use an inherit list. With an inherit list, standard
// handles are duplicated, but only with Windows 8 and up.
for (int useDummyPipe = 0; useDummyPipe <= 1; ++useDummyPipe) {
Worker p;
Handle::invent(INVALID_HANDLE_VALUE, p).setStdout();
auto c = childWithDummyInheritList(p, {}, useDummyPipe != 0);
// Figure out what we expect to see.
std::string expect;
if (isAtLeastWin8_1()) {
// Windows 8.1 turns INVALID_HANDLE_VALUE into NULL.
expect = okNull;
} else if (isAtLeastWin8()) {
// Windows 8 tries to duplicate the handle. WOW64 seems to be
// OK, though.
if (isWow64()) {
expect = okNull;
} else {
expect = bugParentProc;
}
} else {
// Prior to Windows 8, duplication doesn't occur in this case, so
// the bug isn't relevant. We run the test anyway, but it's less
// interesting.
expect = okInvalid;
}
const std::string actual = determineChildStdout(c, p);
trace("%s: actual: %s", __FUNCTION__, actual.c_str());
std::cout << __FUNCTION__ << ": expect: " << expect << std::endl;
std::cout << __FUNCTION__ << ": actual: " << actual << std::endl;
CHECK_EQ(actual, expect);
}
}

View File

@ -1,47 +0,0 @@
#include <TestCommon.h>
// Windows XP bug: default inheritance doesn't work with the read end
// of a pipe, even if it's inheritable. It works with the write end.
bool brokenDuplicationInWow64();
REGISTER(Test_CreateProcess_Duplicate_XPPipeBug, always);
static void Test_CreateProcess_Duplicate_XPPipeBug() {
auto check = [](Worker &proc, Handle correct, bool expectNull) {
CHECK_EQ((proc.getStdin().value() == nullptr), expectNull);
CHECK_EQ((proc.getStdout().value() == nullptr), expectNull);
CHECK_EQ((proc.getStderr().value() == nullptr), expectNull);
if (proc.getStdout().value() != nullptr) {
ObjectSnap snap;
CHECK(snap.eq({
proc.getStdin(), proc.getStdout(), proc.getStderr(), correct
}));
}
};
Worker p;
auto pipe = newPipe(p, false);
auto rh = std::get<0>(pipe).setStdin().setStdout().setStderr();
auto c1 = p.child({ false });
check(c1, rh, !isAtLeastVista() || brokenDuplicationInWow64());
// Marking the handle itself inheritable makes no difference.
rh.setInheritable(true);
auto c2 = p.child({ false });
check(c2, rh, !isAtLeastVista() || brokenDuplicationInWow64());
// If we enter bInheritHandles=TRUE mode, it works.
auto c3 = p.child({ true });
check(c3, rh, false);
// Using STARTF_USESTDHANDLES works too.
Handle::invent(nullptr, p).setStdin().setStdout().setStderr();
auto c4 = p.child({ true, 0, { rh, rh, rh }});
check(c4, rh, false);
// Also test the write end of the pipe.
auto wh = std::get<1>(pipe).setStdin().setStdout().setStderr();
auto c5 = p.child({ false });
check(c5, wh, brokenDuplicationInWow64());
}

View File

@ -1,53 +0,0 @@
#include <TestCommon.h>
//
// Test CreateProcess when called with these parameters:
// - STARTF_USESTDHANDLES is not specified
// - bInheritHandles=TRUE
// - CreationConsoleMode=Inherit
//
REGISTER(Test_CreateProcess_InheritAllHandles, always);
static void Test_CreateProcess_InheritAllHandles() {
auto &hv = handleValues;
{
// Simple case: the standard handles are left as-is.
Worker p;
auto pipe = newPipe(p, true);
std::get<0>(pipe).setStdin();
std::get<1>(pipe).setStdout().setStderr();
auto c = p.child({ true });
CHECK(hv(stdHandles(c)) == hv(stdHandles(p)));
}
{
// We can pass arbitrary values through.
Worker p;
Handle::invent(0x0ull, p).setStdin();
Handle::invent(0x10000ull, p).setStdout();
Handle::invent(INVALID_HANDLE_VALUE, p).setStderr();
auto c = p.child({ true });
CHECK(hv(stdHandles(c)) == hv(stdHandles(p)));
}
{
// Passing through a non-inheritable handle produces an invalid child
// handle.
Worker p;
p.openConin(false).setStdin();
p.openConout(false).setStdout().setStderr();
auto c = p.child({ true });
CHECK(hv(stdHandles(c)) == hv(stdHandles(p)));
if (isTraditionalConio()) {
CHECK(!c.getStdin().tryFlags());
CHECK(!c.getStdout().tryFlags());
CHECK(!c.getStderr().tryFlags());
} else {
ObjectSnap snap;
CHECK(!snap.eq(p.getStdin(), c.getStdin()));
CHECK(!snap.eq(p.getStdout(), c.getStdout()));
CHECK(!snap.eq(p.getStderr(), c.getStderr()));
}
}
}

View File

@ -1,388 +0,0 @@
#include <TestCommon.h>
//
// Test CreateProcess, using PROC_THREAD_ATTRIBUTE_HANDLE_LIST to restrict the
// inherited handles.
//
// Ordinarily, standard handles are copied as-is.
//
// On Windows 8 and later, if a PROC_THREAD_ATTRIBUTE_HANDLE_LIST list is used,
// then the standard handles are duplicated instead.
//
REGISTER(Test_CreateProcess_InheritList, isAtLeastVista);
static void Test_CreateProcess_InheritList() {
// Specifically test inherit lists.
SpawnFailure failure;
auto testSetup = [&](Worker &proc,
SpawnParams sp,
std::initializer_list<HANDLE> inheritList) {
sp.dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = inheritList.size();
std::copy(inheritList.begin(), inheritList.end(),
sp.inheritList.begin());
return proc.tryChild(sp, &failure);
};
Worker p;
auto pipe1 = newPipe(p, true);
auto ph1 = std::get<0>(pipe1);
auto ph2 = std::get<1>(pipe1);
auto pipe2 = newPipe(p, true);
auto ph3 = std::get<0>(pipe2);
auto ph4 = std::get<1>(pipe2);
auto phNI = ph1.dup(false);
// Add an extra console handle so we can verify that a child's console
// handles didn't revert to the original default, but were inherited.
p.openConout(true);
auto testSetupStdHandles = [&](SpawnParams sp) {
const auto in = sp.sui.hStdInput;
const auto out = sp.sui.hStdOutput;
const auto err = sp.sui.hStdError;
sp.dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
sp.sui.cb = sizeof(STARTUPINFOEXW);
// This test case isn't interested in what
// PROC_THREAD_ATTRIBUTE_HANDLE_LIST does when there are duplicate
// handles in its list.
ASSERT(in != out && out != err && in != err);
sp.inheritCount = 3;
sp.inheritList = { in, out, err };
return p.tryChild(sp, &failure);
};
auto ch1 = [&](RemoteWorker &c) { return Handle::invent(ph1.value(), c); };
auto ch2 = [&](RemoteWorker &c) { return Handle::invent(ph2.value(), c); };
auto ch3 = [&](RemoteWorker &c) { return Handle::invent(ph3.value(), c); };
auto ch4 = [&](RemoteWorker &c) { return Handle::invent(ph4.value(), c); };
{
// Use PROC_THREAD_ATTRIBUTE_HANDLE_LIST correctly.
auto c = testSetup(p, {true}, {ph1.value()});
CHECK(c.valid());
// i.e. ph1 was inherited, because ch1 identifies the same thing.
// ph2 was not inherited, because it wasn't listed.
ObjectSnap snap;
CHECK(snap.eq(ph1, ch1(c)));
CHECK(!snap.eq(ph2, ch2(c)));
if (!isAtLeastWin8()) {
// The traditional console handles were all inherited, but they're
// also the standard handles, so maybe that's an exception. We'll
// test more aggressively below.
CHECK(handleValues(c.scanForConsoleHandles()) ==
handleValues(p.scanForConsoleHandles()));
}
}
{
// UpdateProcThreadAttribute fails if the buffer size is zero.
auto c = testSetup(p, {true}, {});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::UpdateProcThreadAttribute);
CHECK_EQ(failure.errCode, (DWORD)ERROR_BAD_LENGTH);
}
{
// Attempting to inherit the GetCurrentProcess pseudo-handle also
// fails. (The MSDN docs point out that using GetCurrentProcess here
// will fail.)
auto c = testSetup(p, {true}, {GetCurrentProcess()});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// CreateProcess fails if the inherit list has a non-inheritable handle
// in it. (STARTF_USESTDHANDLES not set.)
auto c1 = testSetup(p, {true}, {phNI.value()});
CHECK(!c1.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// CreateProcess fails if the inherit list has a non-inheritable handle
// in it. (STARTF_USESTDHANDLES set.)
auto c = testSetup(p, {true, 0, {phNI, phNI, phNI}}, {phNI.value()});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// If bInheritHandles=FALSE and PROC_THREAD_ATTRIBUTE_HANDLE_LIST are
// combined, the API call fails. (STARTF_USESTDHANDLES not set.)
auto c = testSetup(p, {false}, {ph1.value()});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// If bInheritHandles=FALSE and PROC_THREAD_ATTRIBUTE_HANDLE_LIST are
// combined, the API call fails. (STARTF_USESTDHANDLES set.)
auto c = testSetupStdHandles({false, 0, {ph1, ph2, ph4}});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
if (!isAtLeastWin8()) {
// Attempt to restrict inheritance to just one of the three open
// traditional console handles.
auto c = testSetupStdHandles({true, 0, {ph1, ph2, p.getStderr()}});
if (isWin7()) {
// On Windows 7, the CreateProcess call fails with a strange
// error.
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_NO_SYSTEM_RESOURCES);
} else {
// On Vista, the CreateProcess call succeeds, but handle
// inheritance is broken. All of the console handles are
// inherited, not just the error screen buffer that was listed.
// None of the pipe handles were inherited, even though two were
// listed.
c.dumpConsoleHandles();
CHECK(handleValues(c.scanForConsoleHandles()) ==
handleValues(p.scanForConsoleHandles()));
{
ObjectSnap snap;
CHECK(!snap.eq(ph1, ch1(c)));
CHECK(!snap.eq(ph2, ch2(c)));
CHECK(!snap.eq(ph3, ch3(c)));
CHECK(!snap.eq(ph4, ch4(c)));
}
}
}
if (!isAtLeastWin8()) {
// Make a final valiant effort to find a
// PROC_THREAD_ATTRIBUTE_HANDLE_LIST and console handle interaction.
// We'll set all the standard handles to pipes. Nevertheless, all
// console handles are inherited.
auto c = testSetupStdHandles({true, 0, {ph1, ph2, ph4}});
CHECK(c.valid());
CHECK(handleValues(c.scanForConsoleHandles()) ==
handleValues(p.scanForConsoleHandles()));
}
//
// What does it mean if the inherit list has a NULL handle in it?
//
{
// CreateProcess apparently succeeds if the inherit list has a single
// NULL in it. Inheritable handles unrelated to standard handles are
// not inherited.
auto c = testSetup(p, {true}, {NULL});
CHECK(c.valid());
// None of the inheritable handles were inherited.
ObjectSnap snap;
CHECK(!snap.eq(ph1, ch1(c)));
CHECK(!snap.eq(ph2, ch2(c)));
}
{
// {NULL, a handle} ==> nothing is inherited.
auto c = testSetup(p, {true}, {NULL, ph2.value()});
CHECK(c.valid());
ObjectSnap snap;
CHECK(!snap.eq(ph1, ch1(c)));
CHECK(!snap.eq(ph2, ch2(c)));
}
{
// {a handle, NULL} ==> nothing is inherited. (Apparently a NULL
// anywhere in the list means "inherit nothing"? The attribute is not
// ignored.)
auto c = testSetup(p, {true}, {ph1.value(), NULL});
CHECK(c.valid());
ObjectSnap snap;
CHECK(!snap.eq(ph1, ch1(c)));
CHECK(!snap.eq(ph2, ch2(c)));
}
{
// bInheritHandles=FALSE still fails.
auto c = testSetup(p, {false}, {NULL});
CHECK(!c.valid());
CHECK_EQ(failure.kind, SpawnFailure::CreateProcess);
CHECK_EQ(failure.errCode, (DWORD)ERROR_INVALID_PARAMETER);
}
{
// Test whether inheritList={NULL} has an unexpected effect on the
// standard handles. Everything seems consistent.
auto q = testSetup(p, {true}, {ph1.value(), ph2.value()});
ch1(q).setStdin();
ch2(q).setStdout().setStderr();
auto c = testSetup(q, {true}, {NULL});
ObjectSnap snap;
if (isAtLeastWin8()) {
// In Windows 8, standard handles are duplicated if an inherit
// list is specified.
CHECK(snap.eq({c.getStdin(), q.getStdin(), ch1(q)}));
CHECK(snap.eq({c.getStdout(), q.getStdout(), ch2(q)}));
CHECK(snap.eq({c.getStderr(), q.getStderr(), ch2(q)}));
CHECK(c.getStdout().value() != c.getStderr().value());
CHECK(c.getStdin().tryFlags() && c.getStdin().inheritable());
CHECK(c.getStdout().tryFlags() && c.getStdout().inheritable());
CHECK(c.getStderr().tryFlags() && c.getStderr().inheritable());
} else {
// The standard handles were not successfully inherited.
CHECK(handleValues(stdHandles(c)) == handleValues(stdHandles(q)));
CHECK(!snap.eq(ch1(c), ch1(q)));
CHECK(!snap.eq(ch2(c), ch2(q)));
}
}
}
REGISTER(Test_CreateProcess_InheritList_StdHandles, isAtLeastVista);
static void Test_CreateProcess_InheritList_StdHandles() {
// List one of the standard handles in the inherit list, and see what
// happens to the standard list.
auto check = [](Worker &p, RemoteHandle rh, RemoteHandle wh) {
ASSERT(!rh.isTraditionalConsole());
ASSERT(!wh.isTraditionalConsole());
{
// Test bInheritHandles=TRUE, STARTF_USESTDHANDLES, and the
// PROC_THREAD_ATTRIBUTE_HANDLE_LIST attribute. Verify that the
// standard handles are set to handles whose inheritability was
// suppressed.
SpawnParams sp { true, EXTENDED_STARTUPINFO_PRESENT, {rh, wh, wh} };
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 1;
sp.inheritList = { wh.value() };
auto c = p.child(sp);
ObjectSnap snap;
CHECK(handleValues(stdHandles(c)) ==
handleValues(std::vector<RemoteHandle> {rh, wh, wh}));
CHECK(!snap.eq(rh, c.getStdin()));
CHECK(snap.eq(wh, c.getStdout()));
CHECK(snap.eq(wh, c.getStderr()));
}
{
// Same as above, but use a single NULL in the inherit list. Now
// none of the handles are inherited, but the standard values are
// unchanged.
SpawnParams sp { true, EXTENDED_STARTUPINFO_PRESENT, {rh, wh, wh} };
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 1;
sp.inheritList = { NULL };
auto c = p.child(sp);
ObjectSnap snap;
CHECK(handleValues(stdHandles(c)) ==
handleValues(std::vector<RemoteHandle> {rh, wh, wh}));
CHECK(!snap.eq(rh, c.getStdin()));
CHECK(!snap.eq(wh, c.getStdout()));
CHECK(!snap.eq(wh, c.getStderr()));
}
if (!isAtLeastWin8()) {
// Same as above, but avoid STARTF_USESTDHANDLES this time. The
// behavior changed with Windows 8, which now appears to duplicate
// handles in this case.
rh.setStdin();
wh.setStdout().setStderr();
SpawnParams sp { true, EXTENDED_STARTUPINFO_PRESENT };
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 1;
sp.inheritList = { wh.value() };
auto c = p.child(sp);
ObjectSnap snap;
CHECK(handleValues(stdHandles(p)) == handleValues(stdHandles(c)));
CHECK(!snap.eq(p.getStdin(), c.getStdin()));
CHECK(snap.eq(p.getStdout(), c.getStdout()));
}
};
{
Worker p;
auto pipe = newPipe(p, true);
check(p, std::get<0>(pipe), std::get<1>(pipe));
}
if (isModernConio()) {
Worker p;
check(p, p.openConin(true), p.openConout(true));
}
}
REGISTER(Test_CreateProcess_InheritList_ModernDuplication, isAtLeastVista);
static void Test_CreateProcess_InheritList_ModernDuplication() {
auto &hv = handleValues;
for (int useDummyPipe = 0; useDummyPipe <= 1; ++useDummyPipe) {
// Once we've specified an inherit list, non-inheritable standard
// handles are duplicated.
Worker p;
auto pipe = newPipe(p);
auto rh = std::get<0>(pipe).setStdin();
auto wh = std::get<1>(pipe).setStdout().setStderr();
auto c = childWithDummyInheritList(p, {}, useDummyPipe != 0);
if (isModernConio()) {
ObjectSnap snap;
CHECK(snap.eq(rh, c.getStdin()));
CHECK(snap.eq(wh, c.getStdout()));
CHECK(snap.eq(wh, c.getStderr()));
CHECK(c.getStdout().value() != c.getStderr().value());
for (auto h : stdHandles(c)) {
CHECK(!h.inheritable());
}
} else {
CHECK(hv(stdHandles(c)) == hv(stdHandles(p)));
CHECK(!c.getStdin().tryFlags());
CHECK(!c.getStdout().tryFlags());
CHECK(!c.getStderr().tryFlags());
}
}
for (int useDummyPipe = 0; useDummyPipe <= 1; ++useDummyPipe) {
// Invalid handles are translated to 0x0. (For full details, see the
// "duplicate" CreateProcess tests.)
Worker p;
Handle::invent(0x0ull, p).setStdin();
Handle::invent(0xdeadbeefull, p).setStdout();
auto c = childWithDummyInheritList(p, {}, useDummyPipe != 0);
if (isModernConio()) {
CHECK(c.getStdin().uvalue() == 0ull);
CHECK(c.getStdout().uvalue() == 0ull);
} else {
CHECK(c.getStdin().uvalue() == 0ull);
CHECK(c.getStdout().value() ==
Handle::invent(0xdeadbeefull, c).value());
}
}
}
REGISTER(Test_CreateProcess_Duplicate_StdHandles, isModernConio);
static void Test_CreateProcess_Duplicate_StdHandles() {
// The default Unbound console handles should be inheritable, so with
// bInheritHandles=TRUE and standard handles listed in the inherit list,
// the child process should have six console handles, all usable.
Worker p;
SpawnParams sp { true, EXTENDED_STARTUPINFO_PRESENT };
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 3;
sp.inheritList = {
p.getStdin().value(),
p.getStdout().value(),
p.getStderr().value(),
};
auto c = p.child(sp);
std::vector<uint64_t> expected;
extendVector(expected, handleInts(stdHandles(p)));
extendVector(expected, handleInts(stdHandles(c)));
std::sort(expected.begin(), expected.end());
auto correct = handleInts(c.scanForConsoleHandles());
std::sort(correct.begin(), correct.end());
p.dumpConsoleHandles();
c.dumpConsoleHandles();
CHECK(expected == correct);
}

View File

@ -1,53 +0,0 @@
#include <TestCommon.h>
//
// Test CreateProcess when called with these parameters:
// - STARTF_USESTDHANDLES is not specified
// - bInheritHandles=FALSE or bInheritHandles=TRUE
// - CreationConsoleMode=NewConsole
//
REGISTER(Test_CreateProcess_NewConsole, always);
static void Test_CreateProcess_NewConsole() {
auto check = [](Worker &p, bool inheritHandles) {
auto c = p.child({ inheritHandles, Worker::defaultCreationFlags() });
if (isTraditionalConio()) {
checkInitConsoleHandleSet(c);
CHECK(handleInts(stdHandles(c)) ==
(std::vector<uint64_t> {0x3, 0x7, 0xb}));
} else {
checkModernConsoleHandleInit(c, true, true, true);
}
return c;
};
{
Worker p;
check(p, true);
check(p, false);
}
{
Worker p;
p.openConin(false).setStdin();
p.newBuffer(false).setStdout().dup(true).setStderr();
check(p, true);
check(p, false);
}
if (isModernConio()) {
// The default Unbound console handles should be inheritable, so with
// bInheritHandles=TRUE, the child process should have six console
// handles, all usable.
Worker p;
auto c = check(p, true);
std::vector<uint64_t> expected;
extendVector(expected, handleInts(stdHandles(p)));
extendVector(expected, handleInts(stdHandles(c)));
std::sort(expected.begin(), expected.end());
auto correct = handleInts(c.scanForConsoleHandles());
std::sort(correct.begin(), correct.end());
CHECK(expected == correct);
}
}

View File

@ -1,190 +0,0 @@
#include <TestCommon.h>
//
// Test CreateProcess when called with these parameters:
// - STARTF_USESTDHANDLES is specified
// - bInheritHandles=FALSE or bInheritHandles=TRUE
// - CreationConsoleMode=NewConsole or CreationConsoleMode=Inherit
//
// Verify:
// - The resulting traditional ConsoleHandleSet is correct.
// - Windows 8 creates Unbound handles when appropriate.
// - Standard handles are set correctly.
//
// Before Windows 8, the child process has the standard handles specified
// in STARTUPINFO, without exception. Starting with Windows 8, the STARTUPINFO
// handles are ignored with bInheritHandles=FALSE, and even with
// bInheritHandles=TRUE, a NULL hStd{Input,Output,Error} field is translated to
// a new open handle if a new console is being created.
template <typename T>
void checkVariousInputs(T check) {
{
// Specify the original std values. With CreationConsoleMode==Inherit
// and bInheritHandles=FALSE, this code used to work (i.e. produce
// valid standard handles in the child). As of Windows 8, the standard
// handles are now NULL instead.
Worker p;
check(p, stdHandles(p));
}
{
Worker p;
check(p, {
p.getStdin().dup(),
p.getStdout().dup(),
p.getStderr().dup(),
});
}
{
Worker p;
check(p, {
p.getStdin().dup(true),
p.getStdout().dup(true),
p.getStderr().dup(true),
});
}
{
Worker p;
check(p, {
p.openConin(),
p.openConout(),
p.openConout(),
});
}
{
Worker p;
check(p, {
p.openConin(true),
p.openConout(true),
p.openConout(true),
});
}
{
// Invalid handles.
Worker p;
check(p, {
Handle::invent(nullptr, p),
Handle::invent(0x10000ull, p),
Handle::invent(0xdeadbeecull, p),
});
check(p, {
Handle::invent(INVALID_HANDLE_VALUE, p),
Handle::invent(nullptr, p),
Handle::invent(nullptr, p),
});
check(p, {
Handle::invent(nullptr, p),
Handle::invent(nullptr, p),
Handle::invent(nullptr, p),
});
}
{
// Try a non-inheritable pipe.
Worker p;
auto pipe = newPipe(p, false);
check(p, {
std::get<0>(pipe),
std::get<1>(pipe),
std::get<1>(pipe),
});
}
{
// Try an inheritable pipe.
Worker p;
auto pipe = newPipe(p, true);
check(p, {
std::get<0>(pipe),
std::get<1>(pipe),
std::get<1>(pipe),
});
}
}
REGISTER(Test_CreateProcess_UseStdHandles, always);
static void Test_CreateProcess_UseStdHandles() {
checkVariousInputs([](Worker &p, std::vector<Handle> newHandles) {
ASSERT(newHandles.size() == 3);
auto check = [&](Worker &c, bool inheritHandles, bool newConsole) {
trace("Test_CreateProcess_UseStdHandles: "
"inheritHandles=%d newConsole=%d",
inheritHandles, newConsole);
auto childHandles = stdHandles(c);
if (isTraditionalConio()) {
CHECK(handleValues(stdHandles(c)) == handleValues(newHandles));
if (newConsole) {
checkInitConsoleHandleSet(c);
} else {
checkInitConsoleHandleSet(c, p);
}
// The child handles have the same values as the parent.
// Verify that the child standard handles point to the right
// kernel objects.
ObjectSnap snap;
for (int i = 0; i < 3; ++i) {
if (newHandles[i].value() == nullptr ||
newHandles[i].value() == INVALID_HANDLE_VALUE) {
// Nothing to check.
} else if (newHandles[i].isTraditionalConsole()) {
// Everything interesting was already checked in
// checkInitConsoleHandleSet.
} else if (newHandles[i].tryFlags()) {
// A handle is not inherited simply because it is
// listed in STARTUPINFO. The new child standard
// handle is valid iff:
// - the parent handle was valid, AND
// - the parent handle was inheritable, AND
// - bInheritHandles is TRUE
//
// The logic below is not obviously true for all
// possible handle values, but it will work for all
// values we test for. (i.e. There could be some
// handle H to object O that isn't inherited, but by
// sheer conincidence, the child gets a handle H that
// also refers to O. (e.g. Windows internal objects.)
// This test case works because we know that Windows
// won't create a reference to our test objects.)
CHECK(snap.eq(newHandles[i], childHandles[i]) ==
(inheritHandles && newHandles[i].inheritable()));
}
}
} else {
ObjectSnap snap;
bool consoleOpened[3] = {false, false, false};
for (int i = 0; i < 3; ++i) {
if (inheritHandles && newHandles[i].value() != nullptr) {
// The parent's standard handle is used, without
// validation or duplication. It is not inherited
// simply because it is listed in STARTUPINFO.
CHECK(childHandles[i].value() ==
newHandles[i].value());
if (newHandles[i].value() == INVALID_HANDLE_VALUE) {
// The test below does not work on the current
// process pseudo-handle (aka
// INVALID_HANDLE_VALUE).
} else if (newHandles[i].tryFlags()) {
CHECK(snap.eq(newHandles[i], childHandles[i]) ==
newHandles[i].inheritable());
}
} else if (newConsole) {
consoleOpened[i] = true;
} else {
CHECK(childHandles[i].value() == nullptr);
}
}
checkModernConsoleHandleInit(c,
consoleOpened[0],
consoleOpened[1],
consoleOpened[2]);
}
};
for (int inheritInt = 0; inheritInt <= 1; ++inheritInt) {
const bool inherit = inheritInt != 0;
auto c1 = p.child({inherit, 0, newHandles});
check(c1, inherit, false);
auto c2 = p.child(
{inherit, Worker::defaultCreationFlags(), newHandles});
check(c2, inherit, true);
}
});
}

View File

@ -1,223 +0,0 @@
#include <TestCommon.h>
REGISTER(Test_CompareObjectHandles, always);
static void Test_CompareObjectHandles() {
// Verify that compareObjectHandles and ObjectSnap are working.
Worker p;
Worker other;
auto pipe1 = newPipe(p, true);
auto ph1 = std::get<0>(pipe1);
auto ph2 = std::get<1>(pipe1);
auto ph1dup = ph1.dup();
auto ph1other = ph1.dup(other);
ObjectSnap snap;
CHECK(!compareObjectHandles(ph1, ph2));
CHECK(compareObjectHandles(ph1, ph1dup));
CHECK(compareObjectHandles(ph1, ph1other));
CHECK(!snap.eq(ph1, ph2));
CHECK(snap.eq(ph1, ph1dup));
CHECK(snap.eq(ph1, ph1other));
CHECK(snap.eq({ ph1, ph1other, ph1dup }));
CHECK(!snap.eq({ ph2, ph1, ph1other, ph1dup }));
CHECK(!snap.eq({ ph1, ph2, ph1other, ph1dup }));
CHECK(!snap.eq({ ph1, ph1other, ph2, ph1dup }));
CHECK(!snap.eq({ ph1, ph1other, ph1dup, ph2 }));
}
REGISTER(Test_IntrinsicInheritFlags, always);
static void Test_IntrinsicInheritFlags() {
// Console handles have an inherit flag, just as kernel handles do.
//
// In Windows 7, there is a bug where DuplicateHandle(h, FALSE) makes the
// new handle inheritable if the old handle was inheritable.
Worker p;
auto n = p.newBuffer(FALSE);
auto y = p.newBuffer(TRUE);
auto nn = n.dup(FALSE);
auto yn = y.dup(FALSE);
auto ny = n.dup(TRUE);
auto yy = y.dup(TRUE);
p.dumpConsoleHandles();
CHECK(n.inheritable() == false);
CHECK(nn.inheritable() == false);
CHECK(yn.inheritable() == isWin7());
CHECK(y.inheritable() == true);
CHECK(ny.inheritable() == true);
CHECK(yy.inheritable() == true);
for (auto &h : (Handle[]){ n, y, nn, ny, yn, yy }) {
const bool v = h.inheritable();
if (isWin7()) {
// In Windows 7, the console handle inherit flags could not be
// changed.
CHECK(h.trySetInheritable(v) == false);
CHECK(h.trySetInheritable(!v) == false);
CHECK(h.inheritable() == v);
} else {
// With older and newer operating systems, the inheritability can
// be changed. (In newer operating systems, i.e. Windows 8 and up,
// the console handles are just normal kernel handles.)
CHECK(h.trySetInheritable(!v) == true);
CHECK(h.inheritable() == !v);
}
}
p.dumpConsoleHandles();
// For sanity's sake, check that DuplicateHandle(h, FALSE) does the right
// thing with an inheritable pipe handle, even on Windows 7.
auto pipeY = std::get<0>(newPipe(p, TRUE));
auto pipeN = pipeY.dup(FALSE);
CHECK(pipeY.inheritable() == true);
CHECK(pipeN.inheritable() == false);
}
REGISTER(Test_Input_Vs_Output, always);
static void Test_Input_Vs_Output() {
// Ensure that APIs meant for the other kind of handle fail.
Worker p;
CHECK(!p.getStdin().tryScreenBufferInfo());
CHECK(!p.getStdout().tryNumberOfConsoleInputEvents());
}
REGISTER(Test_Detach_Does_Not_Change_Standard_Handles, always);
static void Test_Detach_Does_Not_Change_Standard_Handles() {
// Detaching the current console does not affect the standard handles.
auto check = [](Worker &p) {
auto handles1 = handleValues(stdHandles(p));
p.detach();
auto handles2 = handleValues(stdHandles(p));
CHECK(handles1 == handles2);
};
// Simplest form of the test.
{
Worker p1;
check(p1);
}
// Also do a test with duplicated handles, just in case detaching resets
// the handles to their defaults.
{
Worker p2;
p2.getStdin().dup(TRUE).setStdin();
p2.getStdout().dup(TRUE).setStdout();
p2.getStderr().dup(TRUE).setStderr();
check(p2);
}
// Do another test with STARTF_USESTDHANDLES, just in case detaching resets
// to the hStd{Input,Output,Error} values.
{
Worker p3;
auto pipe = newPipe(p3, true);
auto rh = std::get<0>(pipe);
auto wh = std::get<1>(pipe);
auto p3c = p3.child({true, 0, {rh, wh, wh.dup(true)}});
check(p3c);
}
}
REGISTER(Test_Activate_Does_Not_Change_Standard_Handles, always);
static void Test_Activate_Does_Not_Change_Standard_Handles() {
// SetConsoleActiveScreenBuffer does not change the standard handles.
// MSDN documents this fact on "Console Handles"[1]
//
// "Note that changing the active screen buffer does not affect the
// handle returned by GetStdHandle. Similarly, using SetStdHandle to
// change the STDOUT handle does not affect the active screen buffer."
//
// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
Worker p;
auto handles1 = handleValues(stdHandles(p));
p.newBuffer(TRUE).activate();
auto handles2 = handleValues(stdHandles(p));
CHECK(handles1 == handles2);
}
REGISTER(Test_Active_ScreenBuffer_Order, always);
static void Test_Active_ScreenBuffer_Order() {
// SetActiveConsoleScreenBuffer does not increase a refcount on the
// screen buffer. Instead, when the active screen buffer's refcount hits
// zero, Windows activates the most-recently-activated buffer.
auto firstChar = [](Worker &p) {
auto h = p.openConout();
auto ret = h.firstChar();
h.close();
return ret;
};
{
// Simplest test
Worker p;
p.getStdout().setFirstChar('a');
auto h = p.newBuffer(false, 'b').activate();
h.close();
CHECK_EQ(firstChar(p), 'a');
}
{
// a -> b -> c -> b -> a
Worker p;
p.getStdout().setFirstChar('a');
auto b = p.newBuffer(false, 'b').activate();
auto c = p.newBuffer(false, 'c').activate();
c.close();
CHECK_EQ(firstChar(p), 'b');
b.close();
CHECK_EQ(firstChar(p), 'a');
}
{
// a -> b -> c -> b -> c -> a
Worker p;
p.getStdout().setFirstChar('a');
auto b = p.newBuffer(false, 'b').activate();
auto c = p.newBuffer(false, 'c').activate();
b.activate();
b.close();
CHECK_EQ(firstChar(p), 'c');
c.close();
CHECK_EQ(firstChar(p), 'a');
}
}
REGISTER(Test_GetStdHandle_SetStdHandle, always);
static void Test_GetStdHandle_SetStdHandle() {
// A commenter on the Old New Thing blog suggested that
// GetStdHandle/SetStdHandle could have internally used CloseHandle and/or
// DuplicateHandle, which would have changed the resource management
// obligations of the callers to those APIs. In fact, the APIs are just
// simple wrappers around global variables. Try to write tests for this
// fact.
//
// http://blogs.msdn.com/b/oldnewthing/archive/2013/03/07/10399690.aspx#10400489
auto &hv = handleValues;
{
// Set values and read them back. We get the same handles.
Worker p;
auto pipe = newPipe(p);
auto rh = std::get<0>(pipe);
auto wh1 = std::get<1>(pipe);
auto wh2 = std::get<1>(pipe).dup();
setStdHandles({ rh, wh1, wh2 });
CHECK(hv(stdHandles(p)) == hv({ rh, wh1, wh2}));
// Call again, and we still get the same handles.
CHECK(hv(stdHandles(p)) == hv({ rh, wh1, wh2}));
}
{
Worker p;
p.getStdout().setFirstChar('a');
p.newBuffer(false, 'b').activate().setStdout().dup().setStderr();
std::get<1>(newPipe(p)).setStdout().dup().setStderr();
// SetStdHandle doesn't close its previous handle when it's given a new
// handle. Therefore, the two handles given to SetStdHandle for STDOUT
// and STDERR are still open, and the new screen buffer is still
// active.
CHECK_EQ(p.openConout().firstChar(), 'b');
}
}

View File

@ -1,240 +0,0 @@
#include <TestCommon.h>
REGISTER(Test_AttachConsole_AllocConsole_StdHandles, isModernConio);
static void Test_AttachConsole_AllocConsole_StdHandles() {
// Verify that AttachConsole does the right thing w.r.t. console handle
// sets and standard handles.
auto check = [](bool newConsole, bool useStdHandles, int nullIndex) {
trace("checking: newConsole=%d useStdHandles=%d nullIndex=%d",
newConsole, useStdHandles, nullIndex);
Worker p;
SpawnParams sp = useStdHandles
? SpawnParams { true, 0, stdHandles(p) }
: SpawnParams { false, 0 };
auto c = p.child(sp);
auto pipe = newPipe(c, true);
std::get<0>(pipe).setStdin();
std::get<1>(pipe).setStdout().setStdout();
if (nullIndex == 0) {
Handle::invent(nullptr, c).setStdin();
} else if (nullIndex == 1) {
Handle::invent(nullptr, c).setStdout();
} else if (nullIndex == 2) {
Handle::invent(nullptr, c).setStderr();
}
auto origStdHandles = stdHandles(c);
c.detach();
CHECK(handleValues(stdHandles(c)) == handleValues(origStdHandles));
if (newConsole) {
c.alloc();
} else {
Worker other;
c.attach(other);
}
if (useStdHandles) {
auto curHandles = stdHandles(c);
for (int i = 0; i < 3; ++i) {
if (i != nullIndex) {
CHECK(curHandles[i].value() == origStdHandles[i].value());
}
}
checkModernConsoleHandleInit(c,
nullIndex == 0,
nullIndex == 1,
nullIndex == 2);
} else {
checkModernConsoleHandleInit(c, true, true, true);
}
};
for (int i = -1; i < 3; ++i) {
check(false, false, i);
check(false, true, i);
check(true, false, i);
check(true, true, i);
}
}
REGISTER(Test_Unbound_vs_Bound, isModernConio);
static void Test_Unbound_vs_Bound() {
{
// An Unbound output handle refers to the initial buffer.
Worker p;
auto ob = p.getStdout().setFirstChar('O');
p.newBuffer(true, 'N').activate().setStdout().setStderr();
CHECK_EQ(ob.firstChar(), 'O');
// The handle can come from another process.
Worker p2;
CHECK_EQ(p2.getStdout().dup(p).firstChar(), 'O');
// CONOUT$ will use the new buffer, though.
CHECK_EQ(p.openConout().firstChar(), 'N');
}
{
// A Bound handle from another process does not work.
Worker wa;
Worker wb;
wa.getStdout().setFirstChar('a');
wb.getStdout().setFirstChar('b');
auto a_b = wb.openConout().dup(wa);
auto a_c = wb.newBuffer(false, 'c').dup(wa);
CHECK(a_b.tryFlags());
CHECK(a_c.tryFlags());
CHECK(!a_b.tryScreenBufferInfo());
CHECK(!a_c.tryScreenBufferInfo());
// We can *make* them work, though, if we reattach p to p2's console.
wa.detach();
CHECK(a_b.tryFlags() && a_c.tryFlags());
wa.attach(wb);
CHECK(a_b.tryScreenBufferInfo() && a_b.firstChar() == 'b');
CHECK(a_c.tryScreenBufferInfo() && a_c.firstChar() == 'c');
}
}
REGISTER(Test_Console_Without_Processes, isModernConio);
static void Test_Console_Without_Processes() {
auto waitForTitle = [](HWND hwnd, const std::string &title) {
for (int i = 0; i < 100 && (windowText(hwnd) != title); ++i) {
Sleep(20);
}
};
auto waitForNotTitle = [](HWND hwnd, const std::string &title) {
for (int i = 0; i < 100 && (windowText(hwnd) == title); ++i) {
Sleep(20);
}
};
{
// It is possible to have a console with no attached process. Verify
// that the console window has the expected title even after its only
// process detaches. The window dies once the duplicated Bound handle
// is closed.
Worker p({ false, CREATE_NEW_CONSOLE });
auto bound = p.openConout();
auto hwnd = p.consoleWindow();
auto title = makeTempName(__FUNCTION__);
p.setTitle(title);
waitForTitle(hwnd, title);
p.detach();
Sleep(200);
CHECK_EQ(windowText(hwnd), title);
bound.close();
waitForNotTitle(hwnd, title);
CHECK(windowText(hwnd) != title);
}
}
REGISTER(Test_Implicit_Buffer_Reference, isModernConio);
static void Test_Implicit_Buffer_Reference() {
// Test that a process attached to a console holds an implicit reference
// to the screen buffer that was active at attachment.
auto activeFirstChar = [](Worker &proc) {
auto b = proc.openConout();
auto ret = b.firstChar();
b.close();
return ret;
};
{
Worker p;
Worker p2({ false, DETACHED_PROCESS });
p.getStdout().setFirstChar('A');
auto b = p.newBuffer(false, 'B').activate();
auto pipe = newPipe(p, true);
// Spawn a child process that has no console handles open.
SpawnParams sp({ true, EXTENDED_STARTUPINFO_PRESENT, {
std::get<0>(pipe),
std::get<1>(pipe),
std::get<1>(pipe),
}});
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 2;
sp.inheritList = {
std::get<0>(pipe).value(),
std::get<1>(pipe).value(),
};
auto c = p.child(sp);
CHECK_EQ(c.scanForConsoleHandles().size(), 0u);
// Now close the only open handle to the B buffer. The active
// buffer remains A, because the child implicitly references B.
b.close();
CHECK_EQ(activeFirstChar(p), 'B');
c.detach();
// Once the child detaches, B is freed, and A activates.
CHECK_EQ(activeFirstChar(p), 'A');
}
}
REGISTER(Test_FreeConsole_Closes_Handles, isModernConio);
static void Test_FreeConsole_Closes_Handles() {
auto check = [](Worker &proc, bool ineq, bool outeq, bool erreq) {
auto dupin = proc.getStdin().dup();
auto dupout = proc.getStdout().dup();
auto duperr = proc.getStderr().dup();
proc.detach();
ObjectSnap snap;
CHECK_EQ(snap.eq(proc.getStdin(), dupin), ineq);
CHECK_EQ(snap.eq(proc.getStdout(), dupout), outeq);
CHECK_EQ(snap.eq(proc.getStderr(), duperr), erreq);
dupin.close();
dupout.close();
duperr.close();
};
{
// The child opened three console handles, so FreeConsole closes all of
// them.
Worker p;
check(p, false, false, false);
}
{
// The child inherited the handles, so FreeConsole closes none of them.
Worker p;
auto c = p.child({ true });
check(c, true, true, true);
}
{
// Duplicated console handles: still none of them are closed.
Worker p;
auto c = p.child({ false });
check(c, true, true, true);
}
{
// FreeConsole doesn't close the current stdhandles; it closes the
// handles it opened at attach-time.
Worker p;
p.openConout().setStderr();
check(p, false, false, true);
}
{
// With UseStdHandles, handles aren't closed.
Worker p;
auto c = p.child({ true, 0, stdHandles(p) });
check(c, true, true, true);
}
{
// Using StdHandles, AllocConsole sometimes only opens a few handles.
// Only the handles it opens are closed.
Worker p({ false, DETACHED_PROCESS });
auto pipe = newPipe(p, true);
auto c = p.child({ true, DETACHED_PROCESS, {
std::get<0>(pipe),
std::get<1>(pipe),
std::get<1>(pipe),
}});
Handle::invent(0ull, c).setStderr();
c.alloc();
CHECK(c.getStdin().value() == std::get<0>(pipe).value());
CHECK(c.getStdout().value() == std::get<1>(pipe).value());
CHECK(c.getStderr().tryScreenBufferInfo());
check(c, true, true, false);
}
}

View File

@ -1,307 +0,0 @@
#include <TestCommon.h>
REGISTER(Test_HandleDuplication, isTraditionalConio);
static void Test_HandleDuplication() {
// A traditional console handle cannot be duplicated to another process,
// and it must be duplicated using the GetConsoleProcess() pseudo-value.
// (This tests targetProcess != psuedo-value, but it doesn't test
// sourceProcess != pseudo-value. Not worth the trouble.)
Worker p, other;
p.getStdout().setFirstChar('x');
CHECK_EQ(p.getStdout().dup().firstChar(), 'x');
CHECK_EQ(p.getStdout().dup(p).value(), INVALID_HANDLE_VALUE);
CHECK_EQ(p.getStdout().dup(other).value(), INVALID_HANDLE_VALUE);
}
REGISTER(Test_NewConsole_Resets_ConsoleHandleSet, isTraditionalConio);
static void Test_NewConsole_Resets_ConsoleHandleSet() {
// Test that creating a new console properly resets everything.
Worker p;
// Open some handles to demonstrate the "clean slate" outcome.
auto orig = stdHandles(p);
p.getStdin().dup(true).setStdin();
p.newBuffer(true).setStderr().dup(true).setStdout().activate();
for (auto &h : orig) {
h.close();
}
auto checkClean = [](Worker &proc) {
proc.dumpConsoleHandles();
CHECK_EQ(proc.getStdin().uvalue(), 0x3u);
CHECK_EQ(proc.getStdout().uvalue(), 0x7u);
CHECK_EQ(proc.getStderr().uvalue(), 0xbu);
auto handles = proc.scanForConsoleHandles();
CHECK(handleValues(handles) == (std::vector<HANDLE> {
proc.getStdin().value(),
proc.getStdout().value(),
proc.getStderr().value(),
}));
CHECK(allInheritable(handles));
};
// A child with a new console is reset to a blank slate.
for (int inherit = 0; inherit <= 1; ++inherit) {
auto c1 = p.child({ inherit != 0, CREATE_NEW_CONSOLE });
checkClean(c1);
auto c2 = p.child({ inherit != 0, CREATE_NO_WINDOW });
checkClean(c2);
// Starting a child from a DETACHED_PROCESS also produces a clean
// configuration.
Worker detachedParent({ false, DETACHED_PROCESS });
auto pipe = newPipe(detachedParent, true);
std::get<0>(pipe).setStdin();
std::get<1>(pipe).setStdout().dup(true).setStdout();
Worker c3 = detachedParent.child({ inherit != 0, 0 });
checkClean(c3);
}
// Similarly, detaching and allocating a new console resets the
// ConsoleHandleSet.
p.detach();
p.alloc();
checkClean(p);
}
REGISTER(Test_CreateProcess_DetachedProcess, isTraditionalConio);
static void Test_CreateProcess_DetachedProcess() {
// A child with DETACHED_PROCESS has no console, and its standard handles
// are set to 0 by default.
Worker p;
p.getStdin().dup(TRUE).setStdin();
p.getStdout().dup(TRUE).setStdout();
p.getStderr().dup(TRUE).setStderr();
auto c = p.child({ true, DETACHED_PROCESS });
CHECK(c.getStdin().uvalue() == 0);
CHECK(c.getStdout().uvalue() == 0);
CHECK(c.getStderr().uvalue() == 0);
CHECK(c.scanForConsoleHandles().empty());
CHECK(c.consoleWindow() == NULL);
// XXX: What do GetConsoleCP and GetConsoleOutputCP do when no console is attached?
// Verify that we have a blank slate even with an implicit console
// creation.
auto c2 = c.child({ true });
auto c2h = c2.scanForConsoleHandles();
CHECK(handleValues(c2h) == (std::vector<HANDLE> {
c2.getStdin().value(),
c2.getStdout().value(),
c2.getStderr().value(),
}));
}
REGISTER(Test_Creation_bInheritHandles_Flag, isTraditionalConio);
static void Test_Creation_bInheritHandles_Flag() {
// The bInheritHandles flags to CreateProcess has no effect on console
// handles.
Worker p;
for (auto &h : (Handle[]){
p.getStdin(),
p.getStdout(),
p.getStderr(),
p.newBuffer(false),
p.newBuffer(true),
}) {
h.dup(false);
h.dup(true);
}
auto cY = p.child({ true });
auto cN = p.child({ false });
auto &hv = handleValues;
CHECK(hv(cY.scanForConsoleHandles()) == hv(inheritableHandles(p.scanForConsoleHandles())));
CHECK(hv(cN.scanForConsoleHandles()) == hv(inheritableHandles(p.scanForConsoleHandles())));
}
REGISTER(Test_HandleAllocationOrder, isTraditionalConio);
static void Test_HandleAllocationOrder() {
// When a new handle is created, it always assumes the lowest unused value.
Worker p;
auto h3 = p.getStdin();
auto h7 = p.getStdout();
auto hb = p.getStderr();
auto hf = h7.dup(true);
auto h13 = h3.dup(true);
auto h17 = hb.dup(true);
CHECK(h3.uvalue() == 0x3);
CHECK(h7.uvalue() == 0x7);
CHECK(hb.uvalue() == 0xb);
CHECK(hf.uvalue() == 0xf);
CHECK(h13.uvalue() == 0x13);
CHECK(h17.uvalue() == 0x17);
hf.close();
h13.close();
h7.close();
h7 = h3.dup(true);
hf = h3.dup(true);
h13 = h3.dup(true);
auto h1b = h3.dup(true);
CHECK(h7.uvalue() == 0x7);
CHECK(hf.uvalue() == 0xf);
CHECK(h13.uvalue() == 0x13);
CHECK(h1b.uvalue() == 0x1b);
}
REGISTER(Test_InheritNothing, isTraditionalConio);
static void Test_InheritNothing() {
// It's possible for the standard handles to be non-inheritable.
//
// Avoid calling DuplicateHandle(h, FALSE), because it produces inheritable
// console handles on Windows 7.
Worker p;
auto conin = p.openConin();
auto conout = p.openConout();
p.getStdin().close();
p.getStdout().close();
p.getStderr().close();
conin.setStdin();
conout.setStdout().dup().setStderr();
p.dumpConsoleHandles();
auto c = p.child({ true });
// The child has no open console handles.
CHECK(c.scanForConsoleHandles().empty());
c.dumpConsoleHandles();
// The standard handle values are inherited, even though they're invalid.
CHECK(c.getStdin().value() == p.getStdin().value());
CHECK(c.getStdout().value() == p.getStdout().value());
CHECK(c.getStderr().value() == p.getStderr().value());
// Verify a console is attached.
CHECK(c.openConin().value() != INVALID_HANDLE_VALUE);
CHECK(c.openConout().value() != INVALID_HANDLE_VALUE);
CHECK(c.newBuffer().value() != INVALID_HANDLE_VALUE);
}
REGISTER(Test_AttachConsole_And_CreateProcess_Inheritance, isTraditionalConio);
static void Test_AttachConsole_And_CreateProcess_Inheritance() {
Worker p;
Worker unrelated({ false, DETACHED_PROCESS });
auto conin = p.getStdin().dup(true);
auto conout1 = p.getStdout().dup(true);
auto conout2 = p.getStderr().dup(true);
p.openConout(false); // an extra handle for checkInitConsoleHandleSet testing
p.openConout(true); // an extra handle for checkInitConsoleHandleSet testing
p.getStdin().close();
p.getStdout().close();
p.getStderr().close();
conin.setStdin();
conout1.setStdout();
conout2.setStderr();
auto c = p.child({ true });
auto c2 = c.child({ true });
c2.detach();
c2.attach(c);
unrelated.attach(p);
// The first child will have the same standard handles as the parent.
CHECK(c.getStdin().value() == p.getStdin().value());
CHECK(c.getStdout().value() == p.getStdout().value());
CHECK(c.getStderr().value() == p.getStderr().value());
// AttachConsole sets the handles to (0x3, 0x7, 0xb) regardless of handle
// validity. In this case, c2 initially had non-default handles, and it
// attached to a process that has and also initially had non-default
// handles. Nevertheless, the new standard handles are the defaults.
for (auto proc : {&c2, &unrelated}) {
CHECK(proc->getStdin().uvalue() == 0x3);
CHECK(proc->getStdout().uvalue() == 0x7);
CHECK(proc->getStderr().uvalue() == 0xb);
}
// The set of inheritable console handles in these processes exactly match
// that of the parent.
checkInitConsoleHandleSet(c, p);
checkInitConsoleHandleSet(c2, p);
checkInitConsoleHandleSet(unrelated, p);
}
REGISTER(Test_Detach_Implicitly_Closes_Handles, isTraditionalConio);
static void Test_Detach_Implicitly_Closes_Handles() {
// After detaching, calling GetHandleInformation fails on previous console
// handles.
Worker p;
Handle orig[] = {
p.getStdin(),
p.getStdout(),
p.getStderr(),
p.getStdin().dup(TRUE),
p.getStdout().dup(TRUE),
p.getStderr().dup(TRUE),
p.openConin(TRUE),
p.openConout(TRUE),
};
p.detach();
for (auto h : orig) {
CHECK(!h.tryFlags());
}
}
REGISTER(Test_AttachConsole_AllocConsole_StdHandles, isTraditionalConio);
static void Test_AttachConsole_AllocConsole_StdHandles() {
// Verify that AttachConsole does the right thing w.r.t. console handle
// sets and standard handles.
auto check = [](bool newConsole, bool useStdHandles) {
trace("checking: newConsole=%d useStdHandles=%d",
newConsole, useStdHandles);
Worker p;
SpawnParams sp = useStdHandles
? SpawnParams { true, 0, stdHandles(p) }
: SpawnParams { false, 0 };
p.openConout(false); // 0x0f
p.openConout(true); // 0x13
auto c = p.child(sp);
auto pipe = newPipe(c, true);
std::get<0>(pipe).setStdin();
std::get<1>(pipe).setStdout().setStdout();
auto origStdHandles = stdHandles(c);
c.detach();
CHECK(handleValues(stdHandles(c)) == handleValues(origStdHandles));
if (newConsole) {
c.alloc();
checkInitConsoleHandleSet(c);
} else {
Worker other;
auto out = other.newBuffer(true, 'N'); // 0x0f
other.openConin(false); // 0x13
auto in = other.openConin(true); // 0x17
out.activate(); // activate new buffer
other.getStdin().close(); // close 0x03
other.getStdout().close(); // close 0x07
other.getStderr().close(); // close 0x0b
in.setStdin(); // 0x17
out.setStdout().dup(true).setStderr(); // 0x0f and 0x1b
c.attach(other);
checkInitConsoleHandleSet(c, other);
}
if (useStdHandles) {
CHECK(handleValues(stdHandles(c)) == handleValues(origStdHandles));
} else {
CHECK(handleInts(stdHandles(c)) ==
(std::vector<uint64_t> { 0x3, 0x7, 0xb }));
}
};
check(false, false);
check(false, true);
check(true, false);
check(true, true);
}

View File

@ -1,117 +0,0 @@
#include <TestCommon.h>
// Test for the Windows 7 win7_conout_crash bug.
//
// See console-handle.md, #win7_conout_crash, for theory. Basically, if a
// process does not have a handle for a screen buffer, and it opens and closes
// CONOUT$, then the buffer is destroyed, even though another process is still
// using it. Closing the *other* handles crashes conhost.exe.
//
// The bug affects Windows 7 SP1, but does not affect
// Windows Server 2008 R2 SP1, the server version of the OS.
//
REGISTER(Win7_RefCount_Bug, always);
static void Win7_RefCount_Bug() {
{
// Simplest demonstration:
//
// We will have two screen buffers in this test, O and N. The parent opens
// CONOUT$ to access N, but when it closes its handle, N is freed,
// restoring O as the active buffer.
//
Worker p;
p.getStdout().setFirstChar('O');
auto c = p.child();
c.newBuffer(false, 'N').activate();
auto conout = p.openConout();
CHECK_EQ(conout.firstChar(), 'N');
conout.close();
// At this point, Win7 is broken. Test for it and hope we don't crash.
conout = p.openConout();
if (isWin7() && isWorkstation()) {
CHECK_EQ(conout.firstChar(), 'O');
} else {
CHECK_EQ(conout.firstChar(), 'N');
}
}
{
// We can still "close" the handle by first importing it to another
// process, then detaching that process from its console.
Worker p;
Worker assistant({ false, DETACHED_PROCESS });
p.getStdout().setFirstChar('O');
auto c = p.child();
c.newBuffer(false, 'N').activate();
// Do the read a few times for good measure.
for (int i = 0; i < 5; ++i) {
auto conout = p.openConout(true); // Must be inheritable!
CHECK_EQ(conout.firstChar(), 'N');
assistant.attach(p); // The attach imports the CONOUT$ handle
conout.close();
assistant.detach(); // Exiting would also work.
}
}
{
// If the child detaches, the screen buffer is still allocated. This
// demonstrates that the CONOUT$ handle *did* increment a refcount on
// the buffer.
Worker p;
p.getStdout().setFirstChar('O');
Worker c = p.child();
c.newBuffer(false, 'N').activate();
auto conout = p.openConout();
c.detach(); // The child must exit/detach *without* closing the handle.
CHECK_EQ(conout.firstChar(), 'N');
auto conout2 = p.openConout();
CHECK_EQ(conout2.firstChar(), 'N');
// It is now safe to close the handles. There is no other "console
// object" referencing the screen buffer.
conout.close();
conout2.close();
}
{
// If there are multiple console objects, closing any of them frees
// the screen buffer.
Worker p;
auto c1 = p.child();
auto c2 = p.child();
p.getStdout().setFirstChar('O');
p.newBuffer(false, 'N').activate();
auto ch1 = c1.openConout();
auto ch2 = c2.openConout();
CHECK_EQ(ch1.firstChar(), 'N');
CHECK_EQ(ch2.firstChar(), 'N');
ch1.close();
// At this point, Win7 is broken. Test for it and hope we don't crash.
auto testHandle = c1.openConout();
if (isWin7() && isWorkstation()) {
CHECK_EQ(testHandle.firstChar(), 'O');
} else {
CHECK_EQ(testHandle.firstChar(), 'N');
}
}
if (isTraditionalConio()) {
// Two processes can share a console object; in that case, CloseHandle
// does not immediately fail.
for (int i = 0; i < 2; ++i) {
Worker p1;
Worker p2 = p1.child();
Worker p3({false, DETACHED_PROCESS});
p1.getStdout().setFirstChar('O');
Worker observer = p1.child();
p1.newBuffer(false, 'N').activate();
auto objref1 = p2.openConout(true);
p3.attach(p2);
auto objref2 = Handle::invent(objref1.value(), p3);
if (i == 0) {
objref1.close();
} else {
objref2.close();
}
CHECK_EQ(observer.openConout().firstChar(), 'N');
}
}
}

View File

@ -1,33 +0,0 @@
#include <TestCommon.h>
int main() {
for (DWORD flags : {CREATE_NEW_CONSOLE, CREATE_NO_WINDOW}) {
if (flags == CREATE_NEW_CONSOLE) {
printTestName("Using CREATE_NEW_CONSOLE as default creation mode");
} else {
printTestName("Using CREATE_NO_WINDOW as default creation mode");
}
Worker::setDefaultCreationFlags(flags);
for (auto &test : registeredTests()) {
std::string name;
bool (*cond)();
void (*func)();
std::tie(name, cond, func) = test;
if (cond()) {
printTestName(name);
func();
}
}
}
std::cout << std::endl;
const auto failures = failedTests();
if (failures.empty()) {
std::cout << "All tests passed!" << std::endl;
} else {
std::cout << "Failed tests:" << std::endl;
for (auto name : failures) {
std::cout << " " << name << std::endl;
}
}
return 0;
}

View File

@ -1,112 +0,0 @@
include ../../config.mk
CXX = $(MINGW_CXX)
# Pass -Wno-format to disable format checking because gcc complains about
# %I64x. I can't use %lld because it's broken on MinGW-on-WinXP, though it
# works on later OSs. %I64x works everywhere with MinGW, at runtime. I
# need to find a better way to do this int-to-string conversion.
CXXFLAGS += \
-MMD \
-Wall \
-Wno-format \
-Iharness -I../../src/shared \
-std=c++11 \
-DUNICODE \
-D_UNICODE \
-D_WIN32_WINNT=0x0600
LDFLAGS += -static -static-libgcc -static-libstdc++
# To disable PCH, just comment out these two lines.
PCHFLAGS = -include build/obj/pch.h
PCHDEPS = build/obj/pch.h.gch
# Use gmake -n to see the command-lines gmake would run.
COMMON_OBJECTS = \
build/obj/harness/Event.o \
build/obj/harness/NtHandleQuery.o \
build/obj/harness/ShmemParcel.o \
build/obj/harness/Spawn.o \
build/obj/harness/UnicodeConversions.o \
build/obj/harness/Util.o \
build/obj/shared/DebugClient.o \
build/obj/shared/WinptyAssert.o \
build/obj/shared/winpty_wcsnlen.o
WORKER_OBJECTS = \
build/obj/harness/WorkerProgram.o
TEST_OBJECTS = \
build/obj/harness/RemoteHandle.o \
build/obj/harness/RemoteWorker.o \
build/obj/harness/TestUtil.o \
HANDLETESTS_OBJECTS = \
build/obj/HandleTests/CreateProcess.o \
build/obj/HandleTests/CreateProcess_Detached.o \
build/obj/HandleTests/CreateProcess_Duplicate.o \
build/obj/HandleTests/CreateProcess_Duplicate_PseudoHandleBug.o \
build/obj/HandleTests/CreateProcess_Duplicate_XPPipeBug.o \
build/obj/HandleTests/CreateProcess_InheritAllHandles.o \
build/obj/HandleTests/CreateProcess_InheritList.o \
build/obj/HandleTests/CreateProcess_NewConsole.o \
build/obj/HandleTests/CreateProcess_UseStdHandles.o \
build/obj/HandleTests/MiscTests.o \
build/obj/HandleTests/Modern.o \
build/obj/HandleTests/Traditional.o \
build/obj/HandleTests/Win7_Conout_Crash.o \
build/obj/HandleTests/main.o
include tests.mk
.PHONY : all
all : \
$(TESTS) \
build/Worker.exe \
build/HandleTests.exe \
build/HandleTests.exe.manifest
.PHONY : clean
clean:
rm -fr build
build/obj/pch.h.gch : harness/pch.h
@echo Compiling PCH $<
@mkdir -p $$(dirname $@)
@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
build/obj/%.o : %.cc $(PCHDEPS)
@echo Compiling $<
@mkdir -p $$(dirname $@)
@$(CXX) $(PCHFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
build/obj/shared/%.o : ../../src/shared/%.cc $(PCHDEPS)
@echo Compiling $<
@mkdir -p $$(dirname $@)
@$(CXX) $(PCHFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
.PRECIOUS : build/obj/%.o
.PRECIOUS : build/obj/shared/%.o
build/Worker.exe : $(WORKER_OBJECTS) $(COMMON_OBJECTS)
@echo Linking $@
@$(CXX) -o $@ $^ $(LDFLAGS)
build/HandleTests.exe : $(HANDLETESTS_OBJECTS) $(TEST_OBJECTS) $(COMMON_OBJECTS)
@echo Linking $@
@$(CXX) -o $@ $^ $(LDFLAGS)
build/HandleTests.exe.manifest : manifest.xml
@Echo Copying $< to $@
@mkdir -p $$(dirname $@)
@cp $< $@
build/%.exe : build/obj/%.o $(TEST_OBJECTS) $(COMMON_OBJECTS)
@echo Linking $@
@$(CXX) -o $@ $^ $(LDFLAGS)
-include $(COMMON_OBJECTS:.o=.d)
-include $(WORKER_OBJECTS:.o=.d)
-include $(TEST_OBJECTS:.o=.d)
-include $(HANDLETESTS_OBJECTS:.o=.d)
-include build/obj/*.d

View File

@ -1,119 +0,0 @@
// Test GetConsoleTitleW.
//
// Each of these OS sets implements different semantics for the system call:
// * Windows XP
// * Vista and Windows 7
// * Windows 8 and up (at least to Windows 10)
//
#include <TestCommon.h>
static void checkBuf(const std::array<wchar_t, 1024> &actual,
const std::array<wchar_t, 1024> &expected,
const char *filename,
int line) {
if (actual != expected) {
for (size_t i = 0; i < actual.size(); ++i) {
if (actual[i] != expected[i]) {
std::cout << filename << ":" << line << ": "
<< "char mismatch: [" << i << "]: "
<< actual[i] << " != " << expected[i]
<< " ('" << static_cast<char>(actual[i]) << "' != '"
<< static_cast<char>(expected[i]) << "')"
<< std::endl;
}
}
}
}
#define CHECK_BUF(actual, ...) (checkBuf((actual), __VA_ARGS__, __FILE__, __LINE__))
int main() {
Worker w;
std::array<wchar_t, 1024> readBuf;
const std::wstring kNul = std::wstring(L"", 1);
const std::array<wchar_t, 1024> kJunk = {
'1', '2', '3', '4', '5', '6', '7', '8',
'9', '0', 'A', 'B', 'C', 'D', 'E', 'F',
};
for (auto inputStr : {
std::wstring(L""),
std::wstring(L"a"),
std::wstring(L"ab"),
std::wstring(L"abc"),
std::wstring(L"abcd"),
std::wstring(L"abcde"),
}) {
for (size_t readLen = 0; readLen < 12; ++readLen) {
std::cout << "Testing \"" << narrowString(inputStr) << "\", "
<< "reading " << readLen << " chars" << std::endl;
// Set the title and read it back.
w.setTitle(narrowString(inputStr));
readBuf = kJunk;
const DWORD retVal = w.titleInternal(readBuf, readLen);
if (readLen == 0) {
// When passing a buffer size 0, the API returns 0 and leaves
// the buffer untouched. Every OS version does the same thing.
CHECK_EQ(retVal, 0u);
CHECK_BUF(readBuf, kJunk);
continue;
}
std::wstring expectedWrite;
if (isAtLeastWin8()) {
expectedWrite = inputStr.substr(0, readLen - 1) + kNul;
// The call returns the untruncated length.
CHECK_EQ(retVal, inputStr.size());
}
else if (isAtLeastVista()) {
// Vista and Windows 7 have a bug where the title is instead
// truncated to half the correct number of characters. (i.e.
// The `readlen` is seemingly interpreted as a byte count
// rather than a character count.) The bug isn't present on XP
// or Windows 8.
if (readLen == 1) {
// There is not even room for a NUL terminator, so it's
// just left off. The call still succeeds, though.
expectedWrite = std::wstring();
} else {
expectedWrite =
inputStr.substr(0, (readLen / 2) - 1) + kNul;
}
// The call returns the untruncated length.
CHECK_EQ(retVal, inputStr.size());
}
else {
// Unlike later OSs, XP returns a truncated title length.
// Moreover, whenever it would return 0, either because:
// * the title is blank, and/or
// * the read length is 1
// then XP does not NUL-terminate the buffer.
const size_t truncatedLen = std::min(inputStr.size(), readLen - 1);
if (truncatedLen == 0) {
expectedWrite = std::wstring();
} else {
expectedWrite = inputStr.substr(0, truncatedLen) + kNul;
}
CHECK_EQ(retVal, truncatedLen);
}
// I will assume that remaining characters have undefined values,
// but I suspect they're actually unchanged. On the other hand,
// the API must never modify the bytes beyond `readLen`.
auto expected = kJunk;
std::copy(&readBuf[0], &readBuf[readLen], expected.begin());
std::copy(expectedWrite.begin(), expectedWrite.end(), expected.begin());
CHECK_BUF(readBuf, expected);
}
}
return 0;
}

View File

@ -1,32 +0,0 @@
#include <TestCommon.h>
int main() {
trace("----------------------------------");
Worker p;
p.getStdout().write("<-- origBuffer -->");
auto c = p.child();
auto cb = c.newBuffer(FALSE);
cb.activate();
cb.write("<-- cb -->");
c.dumpConsoleHandles(TRUE);
// Proposed fix: the agent somehow decides it should attach to this
// particular child process. Does that fix the problem?
//
// No, because the child's new buffer was not marked inheritable. If it
// were inheritable, then the parent would "inherit" the handle during
// attach, and both processes would use the same refcount for
// `CloseHandle`.
p.detach();
p.attach(c);
p.dumpConsoleHandles(TRUE);
auto pb = p.openConout();
cb.close();
// Demonstrate that pb is an invalid handle.
pb.close();
Sleep(300000);
}

View File

@ -1,56 +0,0 @@
#include <TestCommon.h>
const int SC_CONSOLE_MARK = 0xFFF2;
const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
int main() {
SpawnParams sp;
sp.bInheritHandles = TRUE;
trace("----------------------------------");
Worker p;
p.getStdout().write("<-- origBuffer -->");
auto c = p.child();
auto cb = c.newBuffer();
cb.activate();
cb.write("<-- cb -->");
// This is what the winpty-agent would want to do:
// - It tries to "freeze" the console with "Select All", which blocks
// WriteConsole but little else. Closing a screen buffer is not
// blocked.
// - Then, winpty wants to get the buffer info, then read screen content.
// - If the child process closes its special screen buffer during the
// scraping, then on Windows 7, conhost can start reading freed memory
// and crash. In this test case, `info2` is frequently garbage.
// Somehow winpty-agent needs to avoid this situation, but options seem
// scarce:
// - The Windows 7 bug only happens with `CloseHandle` AFAICT. If a
// buffer handle goes away implicitly from `FreeConsole` or process
// exit, then the buffer is reference counted properly. If app
// developers avoid closing their buffer handle, winpty can work.
// - Be really careful about when to scrape. Pay close attention to
// the kinds of WinEvents a full-screen app generates just before it
// exits, and try to fast-path everything such that no scraping is
// necessary.
// - Start interfering with the user processes attached to the console.
// - e.g. inject a DLL inside the processes and open CONOUT$, or
// override APIs, etc.
// - Attach to the right console process before opening CONOUT$. If
// that console's buffer handle is inheritable, then opening CONOUT$
// will then produce a safe handle.
// - Accept a certain amount of unreliability.
SendMessage(p.consoleWindow(), WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
auto scrape = p.openConout();
auto info1 = scrape.screenBufferInfo();
cb.close();
Sleep(200); // Helps the test fail more often.
auto info2 = scrape.screenBufferInfo();
SendMessage(p.consoleWindow(), WM_CHAR, 27, 0x00010001);
trace("%d %d %d %d", info1.srWindow.Left, info1.srWindow.Top, info1.srWindow.Right, info1.srWindow.Bottom);
trace("%d %d %d %d", info2.srWindow.Left, info2.srWindow.Top, info2.srWindow.Right, info2.srWindow.Bottom);
Sleep(300000);
}

View File

@ -1,94 +0,0 @@
#pragma once
#include "FixedSizeString.h"
#include "Spawn.h"
#include <array>
#include <cstdint>
struct Command {
enum Kind {
AllocConsole,
AttachConsole,
Close,
CloseQuietly,
DumpConsoleHandles,
DumpStandardHandles,
Duplicate,
Exit,
FreeConsole,
GetConsoleProcessList,
GetConsoleScreenBufferInfo,
GetConsoleSelectionInfo,
GetConsoleTitle,
GetConsoleWindow,
GetHandleInformation,
GetNumberOfConsoleInputEvents,
GetStdin,
GetStderr,
GetStdout,
Hello,
LookupKernelObject,
NewBuffer,
OpenConin,
OpenConout,
ReadConsoleOutput,
ScanForConsoleHandles,
SetConsoleTitle,
SetHandleInformation,
SetStdin,
SetStderr,
SetStdout,
SetActiveBuffer,
SpawnChild,
System,
WriteConsoleOutput,
WriteText,
};
// These fields must appear first so that the LookupKernelObject RPC will
// work. This RPC occurs from 32-bit test programs to a 64-bit worker.
// In that case, most of this struct's fields do not have the same
// addresses or sizes.
Kind kind;
struct {
uint32_t pid;
uint32_t handle[2];
uint32_t kernelObject[2];
} lookupKernelObject;
HANDLE handle;
HANDLE targetProcess;
DWORD dword;
BOOL success;
BOOL bInheritHandle;
BOOL writeToEach;
HWND hwnd;
union {
CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo;
CONSOLE_SELECTION_INFO consoleSelectionInfo;
struct {
FixedSizeString<128> spawnName;
SpawnParams spawnParams;
SpawnFailure spawnFailure;
} spawn;
FixedSizeString<1024> writeText;
FixedSizeString<1024> systemText;
std::array<wchar_t, 1024> consoleTitle;
std::array<DWORD, 1024> processList;
struct {
DWORD mask;
DWORD flags;
} setFlags;
struct {
int count;
std::array<HANDLE, 1024> table;
} scanForConsoleHandles;
struct {
std::array<CHAR_INFO, 1024> buffer;
COORD bufferSize;
COORD bufferCoord;
SMALL_RECT ioRegion;
} consoleIo;
} u;
};

View File

@ -1,31 +0,0 @@
#include "Event.h"
#include "UnicodeConversions.h"
#include <WinptyAssert.h>
Event::Event(const std::string &name) {
// Create manual-reset, not signaled initially.
m_handle = CreateEventW(NULL, TRUE, FALSE, widenString(name).c_str());
ASSERT(m_handle != NULL);
}
Event::~Event() {
if (m_handle != NULL) {
CloseHandle(m_handle);
}
}
void Event::set() {
BOOL ret = SetEvent(m_handle);
ASSERT(ret && "SetEvent failed");
}
void Event::reset() {
BOOL ret = ResetEvent(m_handle);
ASSERT(ret && "ResetEvent failed");
}
void Event::wait() {
DWORD ret = WaitForSingleObject(m_handle, INFINITE);
ASSERT(ret == WAIT_OBJECT_0 && "WaitForSingleObject failed");
}

View File

@ -1,30 +0,0 @@
#pragma once
#include <windows.h>
#include <string>
#include <utility>
class Event {
public:
explicit Event(const std::string &name);
~Event();
void set();
void reset();
void wait();
// no copying
Event(const Event &other) = delete;
Event &operator=(const Event &other) = delete;
// moving is okay
Event(Event &&other) { *this = std::move(other); }
Event &operator=(Event &&other) {
m_handle = other.m_handle;
other.m_handle = NULL;
return *this;
}
private:
HANDLE m_handle;
};

View File

@ -1,37 +0,0 @@
#pragma once
#include <cstdlib>
#include <cstring>
#include <string>
#include <WinptyAssert.h>
template <size_t N>
struct FixedSizeString {
public:
std::string str() const {
ASSERT(strnlen(data, N) < N);
return std::string(data);
}
const char *c_str() const {
ASSERT(strnlen(data, N) < N);
return data;
}
FixedSizeString &operator=(const char *from) {
ASSERT(strlen(from) < N);
strcpy(data, from);
return *this;
}
FixedSizeString &operator=(const std::string &from) {
ASSERT(from.size() < N);
ASSERT(from.size() == strlen(from.c_str()));
strcpy(data, from.c_str());
return *this;
}
private:
char data[N];
};

View File

@ -1,74 +0,0 @@
#include "NtHandleQuery.h"
#include <DebugClient.h>
#include <OsModule.h>
// internal definitions copied from mingw-w64's winternl.h and ntstatus.h.
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004)
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemProcessorInformation = 1,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemPagefileInformation = 18,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;
typedef NTSTATUS NTAPI NtQuerySystemInformation_Type(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
std::vector<SYSTEM_HANDLE_ENTRY> queryNtHandles() {
OsModule ntdll(L"ntdll.dll");
auto funcPtr = ntdll.proc("NtQuerySystemInformation");
ASSERT(funcPtr != NULL && "NtQuerySystemInformation API is missing");
auto func = reinterpret_cast<NtQuerySystemInformation_Type*>(funcPtr);
static std::vector<char> buf(1024);
while (true) {
ULONG returnLength = 0;
auto ret = func(
SystemHandleInformation,
buf.data(),
buf.size(),
&returnLength);
if (ret == STATUS_INFO_LENGTH_MISMATCH) {
buf.resize(buf.size() * 2);
continue;
} else if (ret == STATUS_SUCCESS) {
break;
} else {
trace("Could not query NT handles, status was 0x%x",
static_cast<unsigned>(ret));
return {};
}
}
auto &info = *reinterpret_cast<SYSTEM_HANDLE_INFORMATION*>(buf.data());
std::vector<SYSTEM_HANDLE_ENTRY> ret(info.Count);
std::copy(info.Handle, info.Handle + info.Count, ret.begin());
return ret;
}
// Get the ObjectPointer (underlying NT object) for the NT handle.
void *ntHandlePointer(const std::vector<SYSTEM_HANDLE_ENTRY> &table,
DWORD pid, HANDLE h) {
HANDLE ret = nullptr;
for (auto &entry : table) {
if (entry.OwnerPid == pid &&
entry.HandleValue == reinterpret_cast<uint64_t>(h)) {
ASSERT(ret == nullptr);
ret = entry.ObjectPointer;
}
}
return ret;
}

View File

@ -1,23 +0,0 @@
#pragma once
#include <windows.h>
#include <vector>
typedef struct _SYSTEM_HANDLE_ENTRY {
ULONG OwnerPid;
BYTE ObjectType;
BYTE HandleFlags;
USHORT HandleValue;
PVOID ObjectPointer;
ULONG AccessMask;
} SYSTEM_HANDLE_ENTRY, *PSYSTEM_HANDLE_ENTRY;
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG Count;
SYSTEM_HANDLE_ENTRY Handle[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
std::vector<SYSTEM_HANDLE_ENTRY> queryNtHandles();
void *ntHandlePointer(const std::vector<SYSTEM_HANDLE_ENTRY> &table,
DWORD pid, HANDLE h);

View File

@ -1,47 +0,0 @@
#pragma once
#include <windows.h>
#include <tuple>
#include <WinptyAssert.h>
inline std::tuple<int, int> osversion() {
OSVERSIONINFOW info = { sizeof(info) };
ASSERT(GetVersionExW(&info));
return std::make_tuple(info.dwMajorVersion, info.dwMinorVersion);
}
inline bool isWorkstation() {
OSVERSIONINFOEXW info = { sizeof(info) };
ASSERT(GetVersionExW(reinterpret_cast<OSVERSIONINFO*>(&info)));
return info.wProductType == VER_NT_WORKSTATION;
}
inline bool isWin7() {
return osversion() == std::make_tuple(6, 1);
}
inline bool isAtLeastVista() {
return osversion() >= std::make_tuple(6, 0);
}
inline bool isAtLeastWin7() {
return osversion() >= std::make_tuple(6, 1);
}
inline bool isAtLeastWin8() {
return osversion() >= std::make_tuple(6, 2);
}
inline bool isAtLeastWin8_1() {
return osversion() >= std::make_tuple(6, 3);
}
inline bool isTraditionalConio() {
return !isAtLeastWin8();
}
inline bool isModernConio() {
return isAtLeastWin8();
}

View File

@ -1,226 +0,0 @@
#include "RemoteHandle.h"
#include <string>
#include <vector>
#include "RemoteWorker.h"
#include <DebugClient.h>
#include <WinptyAssert.h>
RemoteHandle RemoteHandle::dup(HANDLE h, RemoteWorker &target,
BOOL bInheritHandle) {
HANDLE targetHandle;
BOOL success = DuplicateHandle(
GetCurrentProcess(),
h,
target.m_process,
&targetHandle,
0, bInheritHandle, DUPLICATE_SAME_ACCESS);
ASSERT(success && "DuplicateHandle failed");
return RemoteHandle(targetHandle, target);
}
RemoteHandle &RemoteHandle::activate() {
worker().cmd().handle = m_value;
worker().rpc(Command::SetActiveBuffer);
return *this;
}
void RemoteHandle::write(const std::string &msg) {
worker().cmd().handle = m_value;
worker().cmd().u.writeText = msg;
worker().rpc(Command::WriteText);
}
void RemoteHandle::close() {
worker().cmd().handle = m_value;
worker().rpc(Command::Close);
}
RemoteHandle &RemoteHandle::setStdin() {
worker().cmd().handle = m_value;
worker().rpc(Command::SetStdin);
return *this;
}
RemoteHandle &RemoteHandle::setStdout() {
worker().cmd().handle = m_value;
worker().rpc(Command::SetStdout);
return *this;
}
RemoteHandle &RemoteHandle::setStderr() {
worker().cmd().handle = m_value;
worker().rpc(Command::SetStderr);
return *this;
}
RemoteHandle RemoteHandle::dupImpl(RemoteWorker *target, BOOL bInheritHandle) {
HANDLE targetProcessFromSource;
if (target == nullptr) {
targetProcessFromSource = GetCurrentProcess();
} else {
// Allow the source worker to see the target worker.
targetProcessFromSource = INVALID_HANDLE_VALUE;
BOOL success = DuplicateHandle(
GetCurrentProcess(),
target->m_process,
worker().m_process,
&targetProcessFromSource,
0, FALSE, DUPLICATE_SAME_ACCESS);
ASSERT(success && "Process handle duplication failed");
}
// Do the user-level duplication in the source process.
worker().cmd().handle = m_value;
worker().cmd().targetProcess = targetProcessFromSource;
worker().cmd().bInheritHandle = bInheritHandle;
worker().rpc(Command::Duplicate);
HANDLE retHandle = worker().cmd().handle;
if (target != nullptr) {
// Cleanup targetProcessFromSource.
worker().cmd().handle = targetProcessFromSource;
worker().rpc(Command::CloseQuietly);
ASSERT(worker().cmd().success &&
"Error closing remote process handle");
}
return RemoteHandle(retHandle, target != nullptr ? *target : worker());
}
CONSOLE_SCREEN_BUFFER_INFO RemoteHandle::screenBufferInfo() {
CONSOLE_SCREEN_BUFFER_INFO ret;
bool success = tryScreenBufferInfo(&ret);
ASSERT(success && "GetConsoleScreenBufferInfo failed");
return ret;
}
bool RemoteHandle::tryScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO *info) {
worker().cmd().handle = m_value;
worker().rpc(Command::GetConsoleScreenBufferInfo);
if (worker().cmd().success && info != nullptr) {
*info = worker().cmd().u.consoleScreenBufferInfo;
}
return worker().cmd().success;
}
DWORD RemoteHandle::flags() {
DWORD ret;
bool success = tryFlags(&ret);
ASSERT(success && "GetHandleInformation failed");
return ret;
}
bool RemoteHandle::tryFlags(DWORD *flags) {
worker().cmd().handle = m_value;
worker().rpc(Command::GetHandleInformation);
if (worker().cmd().success && flags != nullptr) {
*flags = worker().cmd().dword;
}
return worker().cmd().success;
}
void RemoteHandle::setFlags(DWORD mask, DWORD flags) {
bool success = trySetFlags(mask, flags);
ASSERT(success && "SetHandleInformation failed");
}
bool RemoteHandle::trySetFlags(DWORD mask, DWORD flags) {
worker().cmd().handle = m_value;
worker().cmd().u.setFlags.mask = mask;
worker().cmd().u.setFlags.flags = flags;
worker().rpc(Command::SetHandleInformation);
return worker().cmd().success;
}
wchar_t RemoteHandle::firstChar() {
// The "first char" is useful for identifying which output buffer a handle
// refers to.
worker().cmd().handle = m_value;
const SMALL_RECT region = {};
auto &io = worker().cmd().u.consoleIo;
io.bufferSize = { 1, 1 };
io.bufferCoord = {};
io.ioRegion = region;
worker().rpc(Command::ReadConsoleOutput);
ASSERT(worker().cmd().success);
ASSERT(!memcmp(&io.ioRegion, &region, sizeof(region)));
return io.buffer[0].Char.UnicodeChar;
}
RemoteHandle &RemoteHandle::setFirstChar(wchar_t ch) {
// The "first char" is useful for identifying which output buffer a handle
// refers to.
worker().cmd().handle = m_value;
const SMALL_RECT region = {};
auto &io = worker().cmd().u.consoleIo;
io.buffer[0].Char.UnicodeChar = ch;
io.buffer[0].Attributes = 7;
io.bufferSize = { 1, 1 };
io.bufferCoord = {};
io.ioRegion = region;
worker().rpc(Command::WriteConsoleOutput);
ASSERT(worker().cmd().success);
ASSERT(!memcmp(&io.ioRegion, &region, sizeof(region)));
return *this;
}
bool RemoteHandle::tryNumberOfConsoleInputEvents(DWORD *ret) {
worker().cmd().handle = m_value;
worker().rpc(Command::GetNumberOfConsoleInputEvents);
if (worker().cmd().success && ret != nullptr) {
*ret = worker().cmd().dword;
}
return worker().cmd().success;
}
std::vector<RemoteHandle> inheritableHandles(
const std::vector<RemoteHandle> &vec) {
std::vector<RemoteHandle> ret;
for (auto h : vec) {
if (h.inheritable()) {
ret.push_back(h);
}
}
return ret;
}
std::vector<uint64_t> handleInts(const std::vector<RemoteHandle> &vec) {
std::vector<uint64_t> ret;
for (auto h : vec) {
ret.push_back(reinterpret_cast<uint64_t>(h.value()));
}
return ret;
}
std::vector<HANDLE> handleValues(const std::vector<RemoteHandle> &vec) {
std::vector<HANDLE> ret;
for (auto h : vec) {
ret.push_back(h.value());
}
return ret;
}
// It would make more sense to use a std::tuple here, but it's inconvenient.
std::vector<RemoteHandle> stdHandles(RemoteWorker &worker) {
return {
worker.getStdin(),
worker.getStdout(),
worker.getStderr(),
};
}
// It would make more sense to use a std::tuple here, but it's inconvenient.
void setStdHandles(std::vector<RemoteHandle> handles) {
ASSERT(handles.size() == 3);
handles[0].setStdin();
handles[1].setStdout();
handles[2].setStderr();
}
bool allInheritable(const std::vector<RemoteHandle> &vec) {
return handleValues(vec) == handleValues(inheritableHandles(vec));
}

View File

@ -1,82 +0,0 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include <string>
#include <vector>
#include <WinptyAssert.h>
class RemoteWorker;
class RemoteHandle {
friend class RemoteWorker;
private:
RemoteHandle(HANDLE value, RemoteWorker &worker) :
m_value(value), m_worker(&worker)
{
}
public:
static RemoteHandle invent(HANDLE h, RemoteWorker &worker) {
return RemoteHandle(h, worker);
}
static RemoteHandle invent(uint64_t h, RemoteWorker &worker) {
return RemoteHandle(reinterpret_cast<HANDLE>(h), worker);
}
RemoteHandle &activate();
void write(const std::string &msg);
void close();
RemoteHandle &setStdin();
RemoteHandle &setStdout();
RemoteHandle &setStderr();
private:
RemoteHandle dupImpl(RemoteWorker *target, BOOL bInheritHandle);
public:
RemoteHandle dup(RemoteWorker &target, BOOL bInheritHandle=FALSE) {
return dupImpl(&target, bInheritHandle);
}
RemoteHandle dup(BOOL bInheritHandle=FALSE) {
return dupImpl(nullptr, bInheritHandle);
}
static RemoteHandle dup(HANDLE h, RemoteWorker &target,
BOOL bInheritHandle=FALSE);
CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo();
bool tryScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO *info=nullptr);
DWORD flags();
bool tryFlags(DWORD *flags=nullptr);
void setFlags(DWORD mask, DWORD flags);
bool trySetFlags(DWORD mask, DWORD flags);
bool inheritable() {
return flags() & HANDLE_FLAG_INHERIT;
}
void setInheritable(bool inheritable) {
auto success = trySetInheritable(inheritable);
ASSERT(success && "setInheritable failed");
}
bool trySetInheritable(bool inheritable) {
return trySetFlags(HANDLE_FLAG_INHERIT,
inheritable ? HANDLE_FLAG_INHERIT : 0);
}
wchar_t firstChar();
RemoteHandle &setFirstChar(wchar_t ch);
bool tryNumberOfConsoleInputEvents(DWORD *ret=nullptr);
HANDLE value() const { return m_value; }
uint64_t uvalue() const { return reinterpret_cast<uint64_t>(m_value); }
bool isTraditionalConsole() const { return (uvalue() & 3) == 3; }
RemoteWorker &worker() const { return *m_worker; }
private:
HANDLE m_value;
RemoteWorker *m_worker;
};
std::vector<RemoteHandle> inheritableHandles(
const std::vector<RemoteHandle> &vec);
std::vector<uint64_t> handleInts(const std::vector<RemoteHandle> &vec);
std::vector<HANDLE> handleValues(const std::vector<RemoteHandle> &vec);
std::vector<RemoteHandle> stdHandles(RemoteWorker &worker);
void setStdHandles(std::vector<RemoteHandle> handles);
bool allInheritable(const std::vector<RemoteHandle> &vec);

View File

@ -1,156 +0,0 @@
#include "RemoteWorker.h"
#include <string>
#include "UnicodeConversions.h"
#include "Util.h"
#include <DebugClient.h>
#include <WinptyAssert.h>
DWORD RemoteWorker::dwDefaultCreationFlags = CREATE_NEW_CONSOLE;
RemoteWorker::RemoteWorker(decltype(DoNotSpawn)) :
m_name(makeTempName("WinptyBufferTests")),
m_parcel(m_name + "-shmem", ShmemParcel::CreateNew),
m_startEvent(m_name + "-start"),
m_finishEvent(m_name + "-finish")
{
m_finishEvent.set();
}
RemoteWorker::RemoteWorker(SpawnParams params) : RemoteWorker(DoNotSpawn) {
SpawnFailure dummy;
m_process = spawn(m_name, params, dummy);
ASSERT(m_process != nullptr && "Could not create RemoteWorker");
m_valid = true;
// Perform an RPC just to ensure that the worker process is ready, and
// the console exists, before returning.
rpc(Command::Hello);
}
RemoteWorker RemoteWorker::child(SpawnParams params) {
auto ret = tryChild(params);
ASSERT(ret.valid() && "Could not spawn child worker");
return ret;
}
RemoteWorker RemoteWorker::tryChild(SpawnParams params, SpawnFailure *failure) {
RemoteWorker ret(DoNotSpawn);
cmd().u.spawn.spawnName = ret.m_name;
cmd().u.spawn.spawnParams = params;
rpc(Command::SpawnChild);
if (cmd().handle == nullptr) {
if (failure != nullptr) {
*failure = cmd().u.spawn.spawnFailure;
}
} else {
BOOL dupSuccess = DuplicateHandle(
m_process,
cmd().handle,
GetCurrentProcess(),
&ret.m_process,
0, FALSE, DUPLICATE_SAME_ACCESS);
ASSERT(dupSuccess && "RemoteWorker::child: DuplicateHandle failed");
rpc(Command::CloseQuietly);
ASSERT(cmd().success && "RemoteWorker::child: CloseHandle failed");
ret.m_valid = true;
// Perform an RPC just to ensure that the worker process is ready, and
// the console exists, before returning.
ret.rpc(Command::Hello);
}
return ret;
}
void RemoteWorker::exit() {
cmd().dword = 0;
rpcAsync(Command::Exit);
DWORD result = WaitForSingleObject(m_process, INFINITE);
ASSERT(result == WAIT_OBJECT_0 &&
"WaitForSingleObject failed while killing worker");
CloseHandle(m_process);
m_valid = false;
}
CONSOLE_SELECTION_INFO RemoteWorker::selectionInfo() {
rpc(Command::GetConsoleSelectionInfo);
ASSERT(cmd().success);
return cmd().u.consoleSelectionInfo;
}
void RemoteWorker::dumpConsoleHandles(BOOL writeToEach) {
cmd().writeToEach = writeToEach;
rpc(Command::DumpConsoleHandles);
}
std::vector<RemoteHandle> RemoteWorker::scanForConsoleHandles() {
rpc(Command::ScanForConsoleHandles);
auto &rpcTable = cmd().u.scanForConsoleHandles;
std::vector<RemoteHandle> ret;
for (int i = 0; i < rpcTable.count; ++i) {
ret.push_back(RemoteHandle(rpcTable.table[i], *this));
}
return ret;
}
bool RemoteWorker::setTitleInternal(const std::wstring &wstr) {
ASSERT(wstr.size() < cmd().u.consoleTitle.size());
ASSERT(wstr.size() == wcslen(wstr.c_str()));
wcscpy(cmd().u.consoleTitle.data(), wstr.c_str());
rpc(Command::SetConsoleTitle);
return cmd().success;
}
std::string RemoteWorker::title() {
std::array<wchar_t, 1024> buf;
DWORD ret = titleInternal(buf, buf.size());
ret = std::min<DWORD>(ret, buf.size() - 1);
buf[std::min<size_t>(buf.size() - 1, ret)] = L'\0';
return narrowString(std::wstring(buf.data()));
}
// This API is more low-level than typical, because GetConsoleTitleW is buggy
// in older versions of Windows, and this method is used to test the bugs.
DWORD RemoteWorker::titleInternal(std::array<wchar_t, 1024> &buf, DWORD bufSize) {
cmd().dword = bufSize;
cmd().u.consoleTitle = buf;
rpc(Command::GetConsoleTitle);
buf = cmd().u.consoleTitle;
return cmd().dword;
}
std::vector<DWORD> RemoteWorker::consoleProcessList() {
rpc(Command::GetConsoleProcessList);
DWORD count = cmd().dword;
ASSERT(count <= cmd().u.processList.size());
return std::vector<DWORD>(
&cmd().u.processList[0],
&cmd().u.processList[count]);
}
uint64_t RemoteWorker::lookupKernelObject(DWORD pid, HANDLE h) {
const uint64_t h64 = reinterpret_cast<uint64_t>(h);
cmd().lookupKernelObject.pid = pid;
memcpy(&cmd().lookupKernelObject.handle, &h64, sizeof(h64));
rpc(Command::LookupKernelObject);
uint64_t ret;
memcpy(&ret, &cmd().lookupKernelObject.kernelObject, sizeof(ret));
return ret;
}
void RemoteWorker::rpc(Command::Kind kind) {
rpcImpl(kind);
m_finishEvent.wait();
}
void RemoteWorker::rpcAsync(Command::Kind kind) {
rpcImpl(kind);
}
void RemoteWorker::rpcImpl(Command::Kind kind) {
ASSERT(m_valid && "Cannot perform an RPC on an invalid RemoteWorker");
m_finishEvent.wait();
m_finishEvent.reset();
cmd().kind = kind;
m_startEvent.set();
}

View File

@ -1,138 +0,0 @@
#pragma once
#include <windows.h>
#include <string>
#include <vector>
#include "Command.h"
#include "Event.h"
#include "RemoteHandle.h"
#include "ShmemParcel.h"
#include "Spawn.h"
#include "UnicodeConversions.h"
class RemoteWorker {
friend class RemoteHandle;
friend uint64_t wow64LookupKernelObject(DWORD pid, HANDLE handle);
static DWORD dwDefaultCreationFlags;
public:
static void setDefaultCreationFlags(DWORD flags) {
dwDefaultCreationFlags = flags;
}
static DWORD defaultCreationFlags() {
return dwDefaultCreationFlags;
}
public:
struct {} static constexpr DoNotSpawn = {};
explicit RemoteWorker(decltype(DoNotSpawn));
explicit RemoteWorker() : RemoteWorker({false, dwDefaultCreationFlags}) {}
explicit RemoteWorker(SpawnParams params);
RemoteWorker child(SpawnParams params={});
RemoteWorker tryChild(SpawnParams params={}, SpawnFailure *failure=nullptr);
~RemoteWorker() { cleanup(); }
bool valid() { return m_valid; }
void exit();
private:
void cleanup() { if (m_valid) { exit(); } }
public:
// basic worker info
HANDLE processHandle() { return m_process; }
DWORD pid() { return GetProcessId(m_process); }
// allow moving
RemoteWorker(RemoteWorker &&other) :
m_valid(std::move(other.m_valid)),
m_name(std::move(other.m_name)),
m_parcel(std::move(other.m_parcel)),
m_startEvent(std::move(other.m_startEvent)),
m_finishEvent(std::move(other.m_finishEvent)),
m_process(std::move(other.m_process))
{
other.m_valid = false;
other.m_process = nullptr;
}
RemoteWorker &operator=(RemoteWorker &&other) {
cleanup();
m_valid = std::move(other.m_valid);
m_name = std::move(other.m_name);
m_parcel = std::move(other.m_parcel);
m_startEvent = std::move(other.m_startEvent);
m_finishEvent = std::move(other.m_finishEvent);
m_process = std::move(other.m_process);
other.m_valid = false;
other.m_process = nullptr;
return *this;
}
// Commands
RemoteHandle getStdin() { rpc(Command::GetStdin); return RemoteHandle(cmd().handle, *this); }
RemoteHandle getStdout() { rpc(Command::GetStdout); return RemoteHandle(cmd().handle, *this); }
RemoteHandle getStderr() { rpc(Command::GetStderr); return RemoteHandle(cmd().handle, *this); }
bool detach() { rpc(Command::FreeConsole); return cmd().success; }
bool attach(RemoteWorker &worker) { cmd().dword = GetProcessId(worker.m_process); rpc(Command::AttachConsole); return cmd().success; }
bool alloc() { rpc(Command::AllocConsole); return cmd().success; }
void dumpStandardHandles() { rpc(Command::DumpStandardHandles); }
int system(const std::string &arg) { cmd().u.systemText = arg; rpc(Command::System); return cmd().dword; }
HWND consoleWindow() { rpc(Command::GetConsoleWindow); return cmd().hwnd; }
CONSOLE_SELECTION_INFO selectionInfo();
void dumpConsoleHandles(BOOL writeToEach=FALSE);
std::vector<RemoteHandle> scanForConsoleHandles();
void setTitle(const std::string &str) { auto b = setTitleInternal(widenString(str)); ASSERT(b && "setTitle failed"); }
bool setTitleInternal(const std::wstring &str);
std::string title();
DWORD titleInternal(std::array<wchar_t, 1024> &buf, DWORD bufSize);
std::vector<DWORD> consoleProcessList();
RemoteHandle openConin(BOOL bInheritHandle=FALSE) {
cmd().bInheritHandle = bInheritHandle;
rpc(Command::OpenConin);
return RemoteHandle(cmd().handle, *this);
}
RemoteHandle openConout(BOOL bInheritHandle=FALSE) {
cmd().bInheritHandle = bInheritHandle;
rpc(Command::OpenConout);
return RemoteHandle(cmd().handle, *this);
}
RemoteHandle newBuffer(BOOL bInheritHandle=FALSE, wchar_t firstChar=L'\0') {
cmd().bInheritHandle = bInheritHandle;
rpc(Command::NewBuffer);
auto h = RemoteHandle(cmd().handle, *this);
if (firstChar != L'\0') {
h.setFirstChar(firstChar);
}
return h;
}
private:
uint64_t lookupKernelObject(DWORD pid, HANDLE h);
private:
Command &cmd() { return m_parcel.value()[0]; }
void rpc(Command::Kind kind);
void rpcAsync(Command::Kind kind);
void rpcImpl(Command::Kind kind);
private:
bool m_valid = false;
std::string m_name;
// HACK: Use Command[2] instead of Command. To accommodate WOW64, we need
// to have a 32-bit test program communicate with a 64-bit worker to query
// kernel handles. The sizes of the parcels will not match, but it's
// mostly OK as long as the creation size is larger than the open size, and
// the 32-bit program creates the parcel. In principle, a better fix might
// be to use parcels of different sizes or make the Command struct's size
// independent of architecture, but those changes are hard.
ShmemParcelTyped<Command[2]> m_parcel;
Event m_startEvent;
Event m_finishEvent;
HANDLE m_process = NULL;
};

View File

@ -1,42 +0,0 @@
#include "ShmemParcel.h"
#include "UnicodeConversions.h"
#include <DebugClient.h>
#include <WinptyAssert.h>
ShmemParcel::ShmemParcel(
const std::string &name,
CreationDisposition disposition,
size_t size)
{
if (disposition == CreateNew) {
SetLastError(0);
m_hfile = CreateFileMappingW(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
size,
widenString(name).c_str());
ASSERT(m_hfile != NULL && GetLastError() == 0 &&
"Failed to create shared memory");
} else if (disposition == OpenExisting) {
m_hfile = OpenFileMappingW(
FILE_MAP_ALL_ACCESS,
FALSE,
widenString(name).c_str());
ASSERT(m_hfile != NULL && "Failed to open shared memory");
} else {
ASSERT(false && "Invalid disposition value");
}
m_view = MapViewOfFile(m_hfile, FILE_MAP_ALL_ACCESS, 0, 0, size);
ASSERT(m_view != NULL && "Failed to map view of shared memory to create it");
}
ShmemParcel::~ShmemParcel()
{
if (m_hfile != NULL) {
UnmapViewOfFile(m_view);
CloseHandle(m_hfile);
}
}

View File

@ -1,61 +0,0 @@
#pragma once
#include <windows.h>
#include <cstdlib>
#include <string>
#include <utility>
class ShmemParcel {
public:
enum CreationDisposition {
CreateNew,
OpenExisting,
};
public:
ShmemParcel(
const std::string &name,
CreationDisposition disposition,
size_t size);
~ShmemParcel();
// no copying
ShmemParcel(const ShmemParcel &other) = delete;
ShmemParcel &operator=(const ShmemParcel &other) = delete;
// moving is okay
ShmemParcel(ShmemParcel &&other) {
*this = std::move(other);
}
ShmemParcel &operator=(ShmemParcel &&other) {
m_hfile = other.m_hfile;
m_view = other.m_view;
other.m_hfile = NULL;
other.m_view = NULL;
return *this;
}
void *view() { return m_view; }
private:
HANDLE m_hfile;
void *m_view;
};
template <typename T>
class ShmemParcelTyped {
public:
ShmemParcelTyped(
const std::string &name,
ShmemParcel::CreationDisposition disposition) :
m_parcel(name, disposition, sizeof(T))
{
}
T &value() { return *static_cast<T*>(m_parcel.view()); }
private:
ShmemParcel m_parcel;
};

View File

@ -1,131 +0,0 @@
#include "Spawn.h"
#include <string.h>
#include "UnicodeConversions.h"
#include "Util.h"
#include <DebugClient.h>
#include <OsModule.h>
#include <WinptyAssert.h>
namespace {
static std::vector<wchar_t> wstrToWVector(const std::wstring &str) {
std::vector<wchar_t> ret;
ret.resize(str.size() + 1);
wmemcpy(ret.data(), str.c_str(), str.size() + 1);
return ret;
}
} // anonymous namespace
HANDLE spawn(const std::string &workerName,
const SpawnParams &params,
SpawnFailure &error) {
auto exeBaseName = (isWow64() && params.nativeWorkerBitness)
? "Worker64.exe" // always 64-bit binary, used to escape WOW64 environ
: "Worker.exe"; // 32 or 64-bit binary, same arch as test program
auto workerPath =
pathDirName(getModuleFileName(NULL)) + "\\" + exeBaseName;
const std::wstring workerPathWStr = widenString(workerPath);
const std::string cmdLine = "\"" + workerPath + "\" " + workerName;
auto cmdLineWVec = wstrToWVector(widenString(cmdLine));
STARTUPINFOEXW suix = { params.sui };
ASSERT(suix.StartupInfo.cb == sizeof(STARTUPINFOW) ||
suix.StartupInfo.cb == sizeof(STARTUPINFOEXW));
std::unique_ptr<char[]> attrListBuffer;
auto inheritList = params.inheritList;
OsModule kernel32(L"kernel32.dll");
#define DECL_API_FUNC(name) decltype(name) *p##name = nullptr;
DECL_API_FUNC(InitializeProcThreadAttributeList);
DECL_API_FUNC(UpdateProcThreadAttribute);
DECL_API_FUNC(DeleteProcThreadAttributeList);
#undef DECL_API_FUNC
struct AttrList {
decltype(DeleteProcThreadAttributeList) *cleanup = nullptr;
LPPROC_THREAD_ATTRIBUTE_LIST v = nullptr;
~AttrList() {
if (v != nullptr) {
ASSERT(cleanup != nullptr);
cleanup(v);
}
}
} attrList;
if (params.inheritCount != SpawnParams::NoInheritList) {
// Add PROC_THREAD_ATTRIBUTE_HANDLE_LIST to the STARTUPINFOEX. Use
// dynamic binding, because this code must run on XP, which does not
// have this functionality.
ASSERT(params.inheritCount < params.inheritList.size());
#define GET_API_FUNC(name) p##name = reinterpret_cast<decltype(name)*>(kernel32.proc(#name));
GET_API_FUNC(InitializeProcThreadAttributeList);
GET_API_FUNC(UpdateProcThreadAttribute);
GET_API_FUNC(DeleteProcThreadAttributeList);
#undef GET_API_FUNC
if (pInitializeProcThreadAttributeList == nullptr ||
pUpdateProcThreadAttribute == nullptr ||
pDeleteProcThreadAttributeList == nullptr) {
trace("Error: skipping PROC_THREAD_ATTRIBUTE_HANDLE_LIST "
"due to missing APIs");
} else {
SIZE_T bufferSize = 0;
auto success = pInitializeProcThreadAttributeList(
NULL, 1, 0, &bufferSize);
if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// The InitializeProcThreadAttributeList API "fails" with
// ERROR_INSUFFICIENT_BUFFER.
success = TRUE;
}
ASSERT(success &&
"First InitializeProcThreadAttributeList call failed");
attrListBuffer = std::unique_ptr<char[]>(new char[bufferSize]);
attrList.cleanup = pDeleteProcThreadAttributeList;
suix.lpAttributeList = attrList.v =
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
attrListBuffer.get());
success = pInitializeProcThreadAttributeList(
suix.lpAttributeList, 1, 0, &bufferSize);
ASSERT(success &&
"Second InitializeProcThreadAttributeList call failed");
success = pUpdateProcThreadAttribute(
suix.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
inheritList.data(),
params.inheritCount * sizeof(HANDLE),
nullptr, nullptr);
if (!success) {
error.kind = SpawnFailure::UpdateProcThreadAttribute;
error.errCode = GetLastError();
trace("UpdateProcThreadAttribute failed: %s",
errorString(error.errCode).c_str());
return nullptr;
}
}
}
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
auto success = CreateProcessW(workerPathWStr.c_str(), cmdLineWVec.data(),
NULL, NULL,
/*bInheritHandles=*/params.bInheritHandles,
/*dwCreationFlags=*/params.dwCreationFlags,
NULL, NULL,
&suix.StartupInfo, &pi);
if (!success) {
error.kind = SpawnFailure::CreateProcess;
error.errCode = GetLastError();
trace("CreateProcessW failed: %s",
errorString(error.errCode).c_str());
return nullptr;
}
error.kind = SpawnFailure::Success;
error.errCode = 0;
CloseHandle(pi.hThread);
return pi.hProcess;
}

View File

@ -1,45 +0,0 @@
#pragma once
#include <windows.h>
#include <string>
#include "RemoteHandle.h"
struct SpawnParams {
BOOL bInheritHandles = FALSE;
DWORD dwCreationFlags = 0;
STARTUPINFOW sui = { sizeof(STARTUPINFOW), 0 };
static const size_t NoInheritList = static_cast<size_t>(~0);
size_t inheritCount = NoInheritList;
std::array<HANDLE, 1024> inheritList = {};
bool nativeWorkerBitness = false;
SpawnParams(bool bInheritHandles=false, DWORD dwCreationFlags=0) :
bInheritHandles(bInheritHandles),
dwCreationFlags(dwCreationFlags)
{
}
SpawnParams(bool bInheritHandles, DWORD dwCreationFlags,
std::vector<RemoteHandle> stdHandles) :
bInheritHandles(bInheritHandles),
dwCreationFlags(dwCreationFlags)
{
ASSERT(stdHandles.size() == 3);
sui.dwFlags |= STARTF_USESTDHANDLES;
sui.hStdInput = stdHandles[0].value();
sui.hStdOutput = stdHandles[1].value();
sui.hStdError = stdHandles[2].value();
}
};
struct SpawnFailure {
enum Kind { Success, CreateProcess, UpdateProcThreadAttribute };
Kind kind = Success;
DWORD errCode = 0;
};
HANDLE spawn(const std::string &workerName,
const SpawnParams &params,
SpawnFailure &error);

View File

@ -1,31 +0,0 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <cwchar>
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "NtHandleQuery.h"
#include "OsVersion.h"
#include "RemoteHandle.h"
#include "RemoteWorker.h"
#include "TestUtil.h"
#include "UnicodeConversions.h"
#include "Util.h"
#include <DebugClient.h>
#include <WinptyAssert.h>
#include <winpty_wcsnlen.h>
using Handle = RemoteHandle;
using Worker = RemoteWorker;

View File

@ -1,327 +0,0 @@
#include "TestUtil.h"
#include <array>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
#include "OsVersion.h"
#include "NtHandleQuery.h"
#include "RemoteHandle.h"
#include "RemoteWorker.h"
#include "UnicodeConversions.h"
#include "Util.h"
#include <DebugClient.h>
#include <OsModule.h>
#include <WinptyAssert.h>
static RegistrationTable *g_testFunctions;
static std::unordered_set<std::string> g_testFailures;
void printTestName(const std::string &name) {
trace("----------------------------------------------------------");
trace("%s", name.c_str());
printf("%s\n", name.c_str());
fflush(stdout);
}
void recordFailure(const std::string &name) {
g_testFailures.insert(name);
}
std::vector<std::string> failedTests() {
std::vector<std::string> ret(g_testFailures.begin(), g_testFailures.end());
std::sort(ret.begin(), ret.end());
return ret;
}
void registerTest(const std::string &name, bool (&cond)(), void (&func)()) {
if (g_testFunctions == nullptr) {
g_testFunctions = new RegistrationTable {};
}
for (auto &entry : *g_testFunctions) {
// I think the compiler catches duplicates already, but just in case.
ASSERT(&cond != std::get<1>(entry) || &func != std::get<2>(entry));
}
g_testFunctions->push_back(std::make_tuple(name, &cond, &func));
}
RegistrationTable registeredTests() {
return *g_testFunctions;
}
static bool hasBuiltinCompareObjectHandles() {
static auto kernelbase = LoadLibraryW(L"KernelBase.dll");
if (kernelbase != nullptr) {
static auto proc = GetProcAddress(kernelbase, "CompareObjectHandles");
if (proc != nullptr) {
return true;
}
}
return false;
}
static bool needsWow64HandleLookup() {
// The Worker.exe and the test programs must always be the same bitness.
// However, in WOW64 mode, prior to Windows 7 64-bit, the WOW64 version of
// NtQuerySystemInformation returned almost no handle information. Even
// in Windows 7, the pointers are truncated to 32-bits, so for maximum
// reliability, use the RPC technique there too. Windows 10 has a proper
// API.
static bool value = isWow64();
return value;
}
static RemoteWorker makeLookupWorker() {
SpawnParams sp(false, DETACHED_PROCESS);
sp.nativeWorkerBitness = true;
return RemoteWorker(sp);
}
uint64_t wow64LookupKernelObject(DWORD pid, HANDLE handle) {
static auto lookupWorker = makeLookupWorker();
return lookupWorker.lookupKernelObject(pid, handle);
}
static bool builtinCompareObjectHandles(RemoteHandle h1, RemoteHandle h2) {
static OsModule kernelbase(L"KernelBase.dll");
static auto comp =
reinterpret_cast<BOOL(WINAPI*)(HANDLE,HANDLE)>(
kernelbase.proc("CompareObjectHandles"));
ASSERT(comp != nullptr);
HANDLE h1local = nullptr;
HANDLE h2local = nullptr;
bool dup1 = DuplicateHandle(
h1.worker().processHandle(),
h1.value(),
GetCurrentProcess(),
&h1local,
0, false, DUPLICATE_SAME_ACCESS);
bool dup2 = DuplicateHandle(
h2.worker().processHandle(),
h2.value(),
GetCurrentProcess(),
&h2local,
0, false, DUPLICATE_SAME_ACCESS);
bool ret = dup1 && dup2 && comp(h1local, h2local);
if (dup1) {
CloseHandle(h1local);
}
if (dup2) {
CloseHandle(h2local);
}
return ret;
}
bool compareObjectHandles(RemoteHandle h1, RemoteHandle h2) {
ObjectSnap snap;
return snap.eq(h1, h2);
}
ObjectSnap::ObjectSnap() {
if (!hasBuiltinCompareObjectHandles() && !needsWow64HandleLookup()) {
m_table = queryNtHandles();
m_hasTable = true;
}
}
uint64_t ObjectSnap::object(RemoteHandle h) {
if (needsWow64HandleLookup()) {
return wow64LookupKernelObject(h.worker().pid(), h.value());
}
if (!m_hasTable) {
m_table = queryNtHandles();
}
return reinterpret_cast<uint64_t>(ntHandlePointer(
m_table, h.worker().pid(), h.value()));
}
bool ObjectSnap::eq(std::initializer_list<RemoteHandle> handles) {
if (handles.size() < 2) {
return true;
}
if (hasBuiltinCompareObjectHandles()) {
for (auto i = handles.begin() + 1; i < handles.end(); ++i) {
if (!builtinCompareObjectHandles(*handles.begin(), *i)) {
return false;
}
}
} else {
auto first = object(*handles.begin());
for (auto i = handles.begin() + 1; i < handles.end(); ++i) {
if (first != object(*i)) {
return false;
}
}
}
return true;
}
std::tuple<RemoteHandle, RemoteHandle> newPipe(
RemoteWorker &w, BOOL inheritable) {
HANDLE readPipe, writePipe;
auto ret = CreatePipe(&readPipe, &writePipe, NULL, 0);
ASSERT(ret && "CreatePipe failed");
auto p1 = RemoteHandle::dup(readPipe, w, inheritable);
auto p2 = RemoteHandle::dup(writePipe, w, inheritable);
trace("Opened pipe in pid %u: rh=0x%I64x wh=0x%I64x",
w.pid(), p1.uvalue(), p2.uvalue());
CloseHandle(readPipe);
CloseHandle(writePipe);
return std::make_tuple(p1, p2);
}
std::string windowText(HWND hwnd) {
std::array<wchar_t, 256> buf;
DWORD ret = GetWindowTextW(hwnd, buf.data(), buf.size());
ASSERT(ret >= 0 && ret <= buf.size() - 1);
buf[ret] = L'\0';
return narrowString(std::wstring(buf.data()));
}
// Verify that the process' open console handle set is as expected from
// attaching to a new console.
// * The set of console handles is exactly (0x3, 0x7, 0xb).
// * The console handles are inheritable.
void checkInitConsoleHandleSet(RemoteWorker &proc) {
CHECK(isTraditionalConio() && "checkInitConsoleHandleSet is not valid "
"with modern conio");
auto actualHandles = proc.scanForConsoleHandles();
auto correctHandles = std::vector<uint64_t> { 0x3, 0x7, 0xb };
if (handleInts(actualHandles) == correctHandles &&
allInheritable(actualHandles)) {
return;
}
proc.dumpConsoleHandles();
CHECK(false && "checkInitConsoleHandleSet failed");
}
// Verify that the child's open console handle set is as expected from having
// just attached to or spawned from a source worker.
// * The set of child handles should exactly match the set of inheritable
// source handles.
// * Every open child handle should be inheritable.
void checkInitConsoleHandleSet(RemoteWorker &child, RemoteWorker &source) {
ASSERT(isTraditionalConio() && "checkInitConsoleHandleSet is not valid "
"with modern conio");
auto cvec = child.scanForConsoleHandles();
auto cvecInherit = inheritableHandles(cvec);
auto svecInherit = inheritableHandles(source.scanForConsoleHandles());
auto hv = &handleValues;
if (hv(cvecInherit) == hv(svecInherit) && allInheritable(cvec)) {
return;
}
source.dumpConsoleHandles();
child.dumpConsoleHandles();
CHECK(false && "checkInitConsoleHandleSet failed");
}
// Returns true if the handle is a "usable console handle":
// * The handle must be open.
// * It must be a console handle.
// * The process must have an attached console.
// * With modern conio, the handle must be "unbound" or bound to the
// currently attached console.
bool isUsableConsoleHandle(RemoteHandle h) {
// XXX: It would be more efficient/elegant to use GetConsoleMode instead.
return h.tryNumberOfConsoleInputEvents() || h.tryScreenBufferInfo();
}
bool isUsableConsoleInputHandle(RemoteHandle h) {
return h.tryNumberOfConsoleInputEvents();
}
bool isUsableConsoleOutputHandle(RemoteHandle h) {
return h.tryScreenBufferInfo();
}
bool isUnboundConsoleObject(RemoteHandle h) {
// XXX: Consider what happens here with NULL, INVALID_HANDLE_OBJECT, junk,
// etc. I *think* it should work.
ASSERT(isModernConio() && "isUnboundConsoleObject is not valid with "
"traditional conio");
static RemoteWorker other{ SpawnParams {false, CREATE_NO_WINDOW} };
auto dup = h.dup(other);
bool ret = isUsableConsoleHandle(dup);
dup.close();
return ret;
}
// Verify that an optional subset of the STDIN/STDOUT/STDERR standard
// handles are new handles referring to new Unbound console objects.
void checkModernConsoleHandleInit(RemoteWorker &proc,
bool in, bool out, bool err) {
// List all the usable console handles that weren't just opened.
std::vector<RemoteHandle> preExistingHandles;
for (auto h : proc.scanForConsoleHandles()) {
if ((in && h.value() == proc.getStdin().value()) ||
(out && h.value() == proc.getStdout().value()) ||
(err && h.value() == proc.getStderr().value())) {
continue;
}
preExistingHandles.push_back(h);
}
ObjectSnap snap;
auto checkNonReuse = [&](RemoteHandle h) {
// The Unbound console objects that were just opened should not be
// inherited from anywhere else -- they should be brand new objects.
for (auto other : preExistingHandles) {
CHECK(!snap.eq(h, other));
}
};
if (in) {
CHECK(isUsableConsoleInputHandle(proc.getStdin()));
CHECK(isUnboundConsoleObject(proc.getStdin()));
checkNonReuse(proc.getStdin());
}
if (out) {
CHECK(isUsableConsoleOutputHandle(proc.getStdout()));
CHECK(isUnboundConsoleObject(proc.getStdout()));
checkNonReuse(proc.getStdout());
}
if (err) {
CHECK(isUsableConsoleOutputHandle(proc.getStderr()));
CHECK(isUnboundConsoleObject(proc.getStderr()));
checkNonReuse(proc.getStderr());
}
if (out && err) {
ObjectSnap snap;
CHECK(proc.getStdout().value() != proc.getStderr().value());
CHECK(snap.eq(proc.getStdout(), proc.getStderr()));
}
}
// Wrapper around RemoteWorker::child that does the bare minimum to use an
// inherit list.
//
// If `dummyPipeInInheritList` is true, it also creates an inheritable pipe,
// closes one end, and specifies the other end in an inherit list. It closes
// the final pipe end in the parent and child before returning.
//
// This function is useful for testing the modern bInheritHandles=TRUE handle
// duplication functionality.
//
RemoteWorker childWithDummyInheritList(RemoteWorker &p, SpawnParams sp,
bool dummyPipeInInheritList) {
sp.bInheritHandles = true;
sp.dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
sp.sui.cb = sizeof(STARTUPINFOEXW);
sp.inheritCount = 1;
if (dummyPipeInInheritList) {
auto pipe = newPipe(p, true);
std::get<0>(pipe).close();
auto dummy = std::get<1>(pipe);
sp.inheritList = { dummy.value() };
auto c = p.child(sp);
RemoteHandle::invent(dummy.value(), c).close();
dummy.close();
return c;
} else {
sp.inheritList = { NULL };
return p.child(sp);
}
}

View File

@ -1,90 +0,0 @@
#pragma once
#include <windows.h>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include "NtHandleQuery.h"
#include "RemoteHandle.h"
#include "Spawn.h"
class RemoteWorker;
#define CHECK(cond) \
do { \
if (!(cond)) { \
recordFailure(__FUNCTION__); \
trace("%s:%d: ERROR: check failed: " #cond, __FILE__, __LINE__); \
std::cout << __FILE__ << ":" << __LINE__ \
<< (": ERROR: check failed: " #cond) \
<< std::endl; \
} \
} while(0)
#define CHECK_EQ(actual, expected) \
do { \
auto a = (actual); \
auto e = (expected); \
if (a != e) { \
recordFailure(__FUNCTION__); \
trace("%s:%d: ERROR: check failed " \
"(" #actual " != " #expected ")", __FILE__, __LINE__); \
std::cout << __FILE__ << ":" << __LINE__ \
<< ": ERROR: check failed " \
<< ("(" #actual " != " #expected "): ") \
<< a << " != " << e \
<< std::endl; \
} \
} while(0)
#define REGISTER(name, cond) \
static void name(); \
int g_register_ ## cond ## _ ## name = (registerTest(#name, cond, name), 0)
template <typename T>
static void extendVector(std::vector<T> &base, const std::vector<T> &to_add) {
base.insert(base.end(), to_add.begin(), to_add.end());
}
// Test registration
void printTestName(const std::string &name);
void recordFailure(const std::string &name);
std::vector<std::string> failedTests();
void registerTest(const std::string &name, bool(&cond)(), void(&func)());
using RegistrationTable = std::vector<std::tuple<std::string, bool(*)(), void(*)()>>;
RegistrationTable registeredTests();
inline bool always() { return true; }
bool compareObjectHandles(RemoteHandle h1, RemoteHandle h2);
// NT kernel handle->object snapshot
class ObjectSnap {
public:
ObjectSnap();
uint64_t object(RemoteHandle h);
bool eq(std::initializer_list<RemoteHandle> handles);
bool eq(RemoteHandle h1, RemoteHandle h2) { return eq({h1, h2}); }
private:
bool m_hasTable = false;
std::vector<SYSTEM_HANDLE_ENTRY> m_table;
};
// Misc
std::tuple<RemoteHandle, RemoteHandle> newPipe(
RemoteWorker &w, BOOL inheritable=FALSE);
std::string windowText(HWND hwnd);
// "domain-specific" routines: perhaps these belong outside the harness?
void checkInitConsoleHandleSet(RemoteWorker &child);
void checkInitConsoleHandleSet(RemoteWorker &child, RemoteWorker &source);
bool isUsableConsoleHandle(RemoteHandle h);
bool isUsableConsoleInputHandle(RemoteHandle h);
bool isUsableConsoleOutputHandle(RemoteHandle h);
bool isUnboundConsoleObject(RemoteHandle h);
void checkModernConsoleHandleInit(RemoteWorker &proc,
bool in, bool out, bool err);
RemoteWorker childWithDummyInheritList(RemoteWorker &p, SpawnParams sp,
bool dummyPipeInInheritList);

View File

@ -1,44 +0,0 @@
#include "UnicodeConversions.h"
#include <windows.h>
#include <vector>
#include <WinptyAssert.h>
std::string narrowString(const std::wstring &input)
{
int mblen = WideCharToMultiByte(
CP_UTF8, 0,
input.data(), input.size(),
NULL, 0, NULL, NULL);
if (mblen <= 0) {
return std::string();
}
std::vector<char> tmp(mblen);
int mblen2 = WideCharToMultiByte(
CP_UTF8, 0,
input.data(), input.size(),
tmp.data(), tmp.size(),
NULL, NULL);
ASSERT(mblen2 == mblen);
return std::string(tmp.data(), tmp.size());
}
std::wstring widenString(const std::string &input)
{
int widelen = MultiByteToWideChar(
CP_UTF8, 0,
input.data(), input.size(),
NULL, 0);
if (widelen <= 0) {
return std::wstring();
}
std::vector<wchar_t> tmp(widelen);
int widelen2 = MultiByteToWideChar(
CP_UTF8, 0,
input.data(), input.size(),
tmp.data(), tmp.size());
ASSERT(widelen2 == widelen);
return std::wstring(tmp.data(), tmp.size());
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <string>
std::string narrowString(const std::wstring &input);
std::wstring widenString(const std::string &input);

View File

@ -1,126 +0,0 @@
#include "Util.h"
#include <windows.h>
#include <cstdint>
#include <sstream>
#include <string>
#include "UnicodeConversions.h"
#include <OsModule.h>
#include <WinptyAssert.h>
namespace {
static std::string timeString() {
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
auto ret = ((uint64_t)fileTime.dwHighDateTime << 32) |
fileTime.dwLowDateTime;
return std::to_string(ret);
}
} // anonymous namespace
std::string pathDirName(const std::string &path)
{
std::string::size_type pos = path.find_last_of("\\/");
if (pos == std::string::npos) {
return std::string();
} else {
return path.substr(0, pos);
}
}
// Wrapper for GetModuleFileNameW. Returns a UTF-8 string. Aborts on error.
std::string getModuleFileName(HMODULE module)
{
const DWORD size = 4096;
wchar_t filename[size];
DWORD actual = GetModuleFileNameW(module, filename, size);
ASSERT(actual > 0 && actual < size);
return narrowString(filename);
}
// Convert GetLastError()'s error code to a presentable message such as:
//
// <87:The parameter is incorrect.>
//
std::string errorString(DWORD errCode) {
// MSDN has this note about "Windows 10":
//
// Windows 10:
//
// LocalFree is not in the modern SDK, so it cannot be used to free
// the result buffer. Instead, use HeapFree (GetProcessHeap(),
// allocatedMessage). In this case, this is the same as calling
// LocalFree on memory.
//
// Important: LocalAlloc() has different options: LMEM_FIXED, and
// LMEM_MOVABLE. FormatMessage() uses LMEM_FIXED, so HeapFree can be
// used. If LMEM_MOVABLE is used, HeapFree cannot be used.
//
// My interpretation of this note is:
// * "Windows 10" really just means, "the latest MS SDK", which supports
// Windows 10, as well as older releases.
// * In every NT kernel ever, HeapFree is perfectly fine to use with
// LocalAlloc LMEM_FIXED allocations.
// * In every NT kernel ever, the FormatMessage buffer can be freed with
// HeapFree.
// The note is clumsy, though. Without clarity, I can't safely use
// HeapFree, but apparently LocalFree calls stop compiling in the newest
// SDK.
//
// Instead, I'll use a fixed-size buffer.
std::stringstream ss;
ss << "<" << errCode << ":";
std::vector<wchar_t> msgBuf(1024);
DWORD ret = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msgBuf.data(),
msgBuf.size(),
nullptr);
if (ret == 0) {
ss << "FormatMessageW failed:";
ss << GetLastError();
} else {
msgBuf[msgBuf.size() - 1] = L'\0';
std::string msg = narrowString(std::wstring(msgBuf.data()));
if (msg.size() >= 2 && msg.substr(msg.size() - 2) == "\r\n") {
msg.resize(msg.size() - 2);
}
ss << msg;
}
ss << ">";
return ss.str();
}
bool isWow64() {
static bool valueInitialized = false;
static bool value = false;
if (!valueInitialized) {
OsModule kernel32(L"kernel32.dll");
auto proc = reinterpret_cast<decltype(IsWow64Process)*>(
kernel32.proc("IsWow64Process"));
BOOL isWow64 = FALSE;
BOOL ret = proc(GetCurrentProcess(), &isWow64);
value = ret && isWow64;
valueInitialized = true;
}
return value;
}
std::string makeTempName(const std::string &baseName) {
static int workerCounter = 0;
static auto initialTimeString = timeString();
return baseName + "-" +
std::to_string(static_cast<int>(GetCurrentProcessId())) + "-" +
initialTimeString + "-" +
std::to_string(++workerCounter);
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <windows.h>
#include <string>
#include <vector>
std::string pathDirName(const std::string &path);
std::string getModuleFileName(HMODULE module);
std::string errorString(DWORD errCode);
bool isWow64();
std::string makeTempName(const std::string &baseName);

View File

@ -1,355 +0,0 @@
#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include "Command.h"
#include "Event.h"
#include "NtHandleQuery.h"
#include "OsVersion.h"
#include "ShmemParcel.h"
#include "Spawn.h"
#include <DebugClient.h>
static const char *g_prefix = "";
static const char *successOrFail(BOOL ret) {
return ret ? "ok" : "FAILED";
}
static HANDLE openConHandle(const wchar_t *name, BOOL bInheritHandle) {
// If sa isn't provided, the handle defaults to not-inheritable.
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = bInheritHandle;
trace("%sOpening %ls...", g_prefix, name);
HANDLE conout = CreateFileW(name,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING, 0, NULL);
trace("%sOpening %ls... 0x%I64x", g_prefix, name, (int64_t)conout);
return conout;
}
static HANDLE createBuffer(BOOL bInheritHandle) {
// If sa isn't provided, the handle defaults to not-inheritable.
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = bInheritHandle;
trace("%sCreating a new buffer...", g_prefix);
HANDLE conout = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa,
CONSOLE_TEXTMODE_BUFFER, NULL);
trace("%sCreating a new buffer... 0x%I64x", g_prefix, (int64_t)conout);
return conout;
}
static void writeTest(HANDLE conout, const char *msg) {
char writeData[256];
sprintf(writeData, "%s%s\n", g_prefix, msg);
trace("%sWriting to 0x%I64x: '%s'...",
g_prefix, (int64_t)conout, msg);
DWORD actual = 0;
BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
trace("%sWriting to 0x%I64x: '%s'... %s",
g_prefix, (int64_t)conout, msg,
successOrFail(ret && actual == strlen(writeData)));
}
static void setConsoleActiveScreenBuffer(HANDLE conout) {
trace("SetConsoleActiveScreenBuffer(0x%I64x) called...",
(int64_t)conout);
trace("SetConsoleActiveScreenBuffer(0x%I64x) called... %s",
(int64_t)conout,
successOrFail(SetConsoleActiveScreenBuffer(conout)));
}
static void dumpStandardHandles() {
trace("stdin=0x%I64x stdout=0x%I64x stderr=0x%I64x",
(int64_t)GetStdHandle(STD_INPUT_HANDLE),
(int64_t)GetStdHandle(STD_OUTPUT_HANDLE),
(int64_t)GetStdHandle(STD_ERROR_HANDLE));
}
static std::vector<HANDLE> scanForConsoleHandles() {
std::vector<HANDLE> ret;
if (isModernConio()) {
// As of Windows 8, console handles are real kernel handles.
for (unsigned int i = 0x4; i <= 0x1000; i += 4) {
HANDLE h = reinterpret_cast<HANDLE>(i);
DWORD mode;
if (GetConsoleMode(h, &mode)) {
ret.push_back(h);
}
}
} else {
for (unsigned int i = 0x3; i < 0x3 + 100 * 4; i += 4) {
HANDLE h = reinterpret_cast<HANDLE>(i);
DWORD mode;
if (GetConsoleMode(h, &mode)) {
ret.push_back(h);
}
}
}
return ret;
}
static void dumpConsoleHandles(bool writeToEach) {
std::string dumpLine = "";
for (HANDLE h : scanForConsoleHandles()) {
char buf[32];
sprintf(buf, "0x%I64x", (int64_t)h);
dumpLine += buf;
dumpLine.push_back('(');
CONSOLE_SCREEN_BUFFER_INFO info;
bool is_output = false;
DWORD count;
if (GetNumberOfConsoleInputEvents(h, &count)) {
dumpLine.push_back('I');
}
if (GetConsoleScreenBufferInfo(h, &info)) {
is_output = true;
dumpLine.push_back('O');
CHAR_INFO charInfo;
SMALL_RECT readRegion = {};
if (ReadConsoleOutputW(h, &charInfo, {1,1}, {0,0}, &readRegion)) {
wchar_t ch = charInfo.Char.UnicodeChar;
if (ch != L' ') {
dumpLine.push_back((char)ch);
}
}
}
{
DWORD flags = 0;
if (GetHandleInformation(h, &flags)) {
dumpLine.push_back((flags & HANDLE_FLAG_INHERIT) ? '^' : '_');
}
}
dumpLine += ") ";
if (writeToEach && is_output) {
char msg[256];
sprintf(msg, "%d: Writing to 0x%I64x",
(int)GetCurrentProcessId(), (int64_t)h);
writeTest(h, msg);
}
}
trace("Valid console handles:%s", dumpLine.c_str());
}
template <typename T>
void handleConsoleIoCommand(Command &cmd, T func) {
const auto sz = cmd.u.consoleIo.bufferSize;
ASSERT(static_cast<size_t>(sz.X) * sz.Y <= cmd.u.consoleIo.buffer.size());
cmd.success = func(cmd.handle, cmd.u.consoleIo.buffer.data(),
cmd.u.consoleIo.bufferSize, cmd.u.consoleIo.bufferCoord,
&cmd.u.consoleIo.ioRegion);
}
int main(int argc, char *argv[]) {
std::string workerName = argv[1];
ShmemParcelTyped<Command> parcel(workerName + "-shmem", ShmemParcel::OpenExisting);
Event startEvent(workerName + "-start");
Event finishEvent(workerName + "-finish");
Command &cmd = parcel.value();
dumpStandardHandles();
while (true) {
startEvent.wait();
startEvent.reset();
switch (cmd.kind) {
case Command::AllocConsole:
trace("Calling AllocConsole...");
cmd.success = AllocConsole();
trace("Calling AllocConsole... %s",
successOrFail(cmd.success));
break;
case Command::AttachConsole:
trace("Calling AttachConsole(%u)...",
(unsigned int)cmd.dword);
cmd.success = AttachConsole(cmd.dword);
trace("Calling AttachConsole(%u)... %s",
(unsigned int)cmd.dword, successOrFail(cmd.success));
break;
case Command::Close:
trace("closing 0x%I64x...",
(int64_t)cmd.handle);
cmd.success = CloseHandle(cmd.handle);
trace("closing 0x%I64x... %s",
(int64_t)cmd.handle, successOrFail(cmd.success));
break;
case Command::CloseQuietly:
cmd.success = CloseHandle(cmd.handle);
break;
case Command::DumpStandardHandles:
dumpStandardHandles();
break;
case Command::DumpConsoleHandles:
dumpConsoleHandles(cmd.writeToEach);
break;
case Command::Duplicate: {
HANDLE sourceHandle = cmd.handle;
cmd.success = DuplicateHandle(
GetCurrentProcess(),
sourceHandle,
cmd.targetProcess,
&cmd.handle,
0, cmd.bInheritHandle, DUPLICATE_SAME_ACCESS);
if (!cmd.success) {
cmd.handle = INVALID_HANDLE_VALUE;
}
trace("dup 0x%I64x to pid %u... %s, 0x%I64x",
(int64_t)sourceHandle,
(unsigned int)GetProcessId(cmd.targetProcess),
successOrFail(cmd.success),
(int64_t)cmd.handle);
break;
}
case Command::Exit:
trace("exiting");
ExitProcess(cmd.dword);
break;
case Command::FreeConsole:
trace("Calling FreeConsole...");
cmd.success = FreeConsole();
trace("Calling FreeConsole... %s", successOrFail(cmd.success));
break;
case Command::GetConsoleProcessList:
cmd.dword = GetConsoleProcessList(cmd.u.processList.data(),
cmd.u.processList.size());
break;
case Command::GetConsoleScreenBufferInfo:
cmd.u.consoleScreenBufferInfo = {};
cmd.success = GetConsoleScreenBufferInfo(
cmd.handle, &cmd.u.consoleScreenBufferInfo);
break;
case Command::GetConsoleSelectionInfo:
cmd.u.consoleSelectionInfo = {};
cmd.success = GetConsoleSelectionInfo(&cmd.u.consoleSelectionInfo);
break;
case Command::GetConsoleTitle:
// GetConsoleTitle is buggy, so make the worker API for it very
// explicit so we can test its bugginess.
ASSERT(cmd.dword <= cmd.u.consoleTitle.size());
cmd.dword = GetConsoleTitleW(cmd.u.consoleTitle.data(), cmd.dword);
break;
case Command::GetConsoleWindow:
cmd.hwnd = GetConsoleWindow();
break;
case Command::GetHandleInformation:
cmd.success = GetHandleInformation(cmd.handle, &cmd.dword);
break;
case Command::GetNumberOfConsoleInputEvents:
cmd.success = GetNumberOfConsoleInputEvents(cmd.handle, &cmd.dword);
break;
case Command::GetStdin:
cmd.handle = GetStdHandle(STD_INPUT_HANDLE);
break;
case Command::GetStderr:
cmd.handle = GetStdHandle(STD_ERROR_HANDLE);
break;
case Command::GetStdout:
cmd.handle = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case Command::Hello:
// NOOP for Worker startup synchronization.
break;
case Command::LookupKernelObject: {
uint64_t h64;
memcpy(&h64, &cmd.lookupKernelObject.handle, sizeof(h64));
auto handles = queryNtHandles();
uint64_t result =
reinterpret_cast<uint64_t>(
ntHandlePointer(
handles, cmd.lookupKernelObject.pid,
reinterpret_cast<HANDLE>(h64)));
memcpy(&cmd.lookupKernelObject.kernelObject,
&result, sizeof(result));
trace("LOOKUP: p%d: 0x%I64x => 0x%I64x",
(int)cmd.lookupKernelObject.pid,
h64,
result);
break;
}
case Command::NewBuffer:
cmd.handle = createBuffer(cmd.bInheritHandle);
break;
case Command::OpenConin:
cmd.handle = openConHandle(L"CONIN$", cmd.bInheritHandle);
break;
case Command::OpenConout:
cmd.handle = openConHandle(L"CONOUT$", cmd.bInheritHandle);
break;
case Command::ReadConsoleOutput:
handleConsoleIoCommand(cmd, ReadConsoleOutputW);
break;
case Command::ScanForConsoleHandles: {
auto ret = scanForConsoleHandles();
ASSERT(ret.size() <= cmd.u.scanForConsoleHandles.table.size());
cmd.u.scanForConsoleHandles.count = ret.size();
std::copy(ret.begin(), ret.end(),
cmd.u.scanForConsoleHandles.table.begin());
break;
}
case Command::SetConsoleTitle: {
auto nul = std::find(cmd.u.consoleTitle.begin(),
cmd.u.consoleTitle.end(), L'\0');
ASSERT(nul != cmd.u.consoleTitle.end());
cmd.success = SetConsoleTitleW(cmd.u.consoleTitle.data());
break;
}
case Command::SetHandleInformation:
cmd.success = SetHandleInformation(
cmd.handle, cmd.u.setFlags.mask, cmd.u.setFlags.flags);
break;
case Command::SetStdin:
SetStdHandle(STD_INPUT_HANDLE, cmd.handle);
trace("setting stdin to 0x%I64x", (int64_t)cmd.handle);
break;
case Command::SetStderr:
SetStdHandle(STD_ERROR_HANDLE, cmd.handle);
trace("setting stderr to 0x%I64x", (int64_t)cmd.handle);
break;
case Command::SetStdout:
SetStdHandle(STD_OUTPUT_HANDLE, cmd.handle);
trace("setting stdout to 0x%I64x", (int64_t)cmd.handle);
break;
case Command::SetActiveBuffer:
setConsoleActiveScreenBuffer(cmd.handle);
break;
case Command::SpawnChild:
trace("Spawning child...");
cmd.handle = spawn(cmd.u.spawn.spawnName.str(),
cmd.u.spawn.spawnParams,
cmd.u.spawn.spawnFailure);
if (cmd.handle != nullptr) {
trace("Spawning child... pid %u",
(unsigned int)GetProcessId(cmd.handle));
}
break;
case Command::System:
cmd.dword = system(cmd.u.systemText.c_str());
break;
case Command::WriteConsoleOutput:
handleConsoleIoCommand(cmd, WriteConsoleOutputW);
break;
case Command::WriteText:
writeTest(cmd.handle, cmd.u.writeText.c_str());
break;
}
finishEvent.set();
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More