Compare commits

..

26 Commits

Author SHA1 Message Date
Ryan Prichard
9675a911a1 Merge branch 'master' into libwinpty-rewrite 2016-02-21 01:35:57 -06:00
Ryan Prichard
41762f3851 Link statically to the MSVC runtime library in configurations.gypi. 2016-01-24 02:26:58 -06:00
Ryan Prichard
b844635158 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 apparantly orphaned on 2014-09-18 [5] and are
still orphaned [6].

[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-01-23 06:19:56 -06:00
Ryan Prichard
9b69f67f56 Use precompiled headers when compiling with GCC.
The unix-adapter doesn't use PCH, because MSYS1's compiler doesn't support
them.
2016-01-23 05:50:30 -06:00
Ryan Prichard
e3c669ae56 Avoid invoking dirname and git for each compiled C++ source file 2016-01-23 04:36:44 -06:00
Ryan Prichard
7f1045972c Work around the MinGW -std=c++11 bug using -std=gnu++11. 2016-01-23 04:36:04 -06:00
Ryan Prichard
cfb5f81de3 Adjust error handling in libwinpty
* Use ASSERT() where the API is being misused.  Also use it when
   WaitForMultipleObjects or WriteFile return a value I don't expect to
   ever see.

 * Allow passing err to winpty_error_{code,msg}, though.  Instead of
   complaining about the NULL error pointer, instead return
   WINPTY_ERROR_SUCCESS and L"Success".
2016-01-23 04:12:25 -06:00
Ryan Prichard
6deb5e2c26 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-01-18 01:41:59 -06:00
Ryan Prichard
873733faa4 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-01-18 00:28:15 -06:00
Ryan Prichard
3da3381859 Stop spawning cat/tr for each compiled source file 2016-01-18 00:16:45 -06:00
Ryan Prichard
80d4faa72c 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-01-18 00:10:02 -06:00
Ryan Prichard
834195074a The debugserver also needs FILE_FLAG_FIRST_PIPE_INSTANCE now. 2016-01-17 17:58:53 -06:00
Ryan Prichard
413f17a81e Rename c99_snprintf to winpty_snprintf and expand the header file.
* The c99_snprintf function was not actually a C99-conformant snprintf
   function, except perhaps when compiled for Cygwin/MSYS.

 * MSVCRT.DLL exposes _vsnprintf, _vswprintf, _vscprintf, and _vscwprintf,
   which are the four most relevant and fundamental printf functions.  It
   *doesn't* expose a function like _scprintf, even though MSDN documents
   it.  It seems that the simple wrapper functions are linked statically
   even when linking dynamically to the MS CRT.

   It appears that MinGW and MinGW-w64 generallY provide these four
   functions in all configurations.  Therefore, for predictable behavior,
   always use these functions.

 * Examples of non-predictability:

    - MinGW declares _vscprintf but not _scprintf.  MinGW-w64 declares
      both.
    - MinGW and MinGW-w64 sometimes override some printf functions with
      their own versions.  (It's easy to tell when this happens because
      "%zd" starts working.)  With MinGW, -std=c99 enables the overrides,
      as does -std=c++11.  -std=gnu++11 does NOT enable the overrides.
    - When the overrides are not in effect, MinGW declares swprintf as
      taking no size parameter.  When the overrides ARE in effect, MinGW
      does not declare swprintf.  MinGW-w64 and MSVC prefer swprintf with
      a size parameter, but they overload it in C++ mode to allow calling
      it either way.  Cygwin and ISO C require the size parameter.
    - With overrides off, MinGW's snprintf is still the MinGW override
      function.  MinGW-w64's snprintf instead calls MSVCRT.DLL _snprintf.
      MinGW-w64 changes its PRI??? strings when overrides are enabled, but
      MinGW does not.

 * In the native Windows mode, which I'm more interested in anyway, it's
   fairly easy to calculate the length of a formatted string before
   outputting it.  Add a few wrapper functions that do this.  I could
   enhance StringBuilder with easy ways to add stdio-formatted strings.
   Of course, these sizes are only valid if they're computed by the same
   printf implementation that writes into the string, and they will be.

Work around a MinGW header-file non-idempotency bug w.r.t _vscwprintf
2016-01-17 17:58:25 -06:00
Ryan Prichard
ede3244c26 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.
2016-01-17 00:40:28 -06:00
Ryan Prichard
cf76665cfe Add a StringBuilder class that resembles an efficient std::stringstream 2016-01-16 23:46:15 -06:00
Ryan Prichard
ba0e3a49fd Remove a few unused assert.h includes. 2016-01-16 15:09:24 -06:00
Ryan Prichard
f2c4f6a59b Add a clean-msvs target for cleaning up after gyp and VisualStudio/msbuild 2016-01-16 13:27:52 -06:00
Ryan Prichard
f04774eb06 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-01-16 12:49:05 -06:00
Ryan Prichard
840847624d Split error codes and flags into a separate file for the agent's sake. 2016-01-16 02:45:33 -06:00
Ryan Prichard
f6c6f0558e Add __attribute__((format)) to trace and c99_snprintf, fix two mismatches 2016-01-16 00:03:27 -06:00
Ryan Prichard
a532a7ce3b 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-01-15 23:41:52 -06:00
Ryan Prichard
1ec380ad61 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.

 * Also define WINVER=0x0501, because MinGW's sddl.h only defines
   ConvertSidToStringSidW and ConvertStringSidToSidW if WINVER is defined.
   (Ordinarily, defining _WIN32_WINNT is sufficient, and it's even
   sufficient with the i686-pc-mingw32-g++ compiler packaged with Cygwin.)

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

I tested this commit with all of the supported compilers: MSYS1 MinGW,
Cygwin MinGW, MSYS2 MinGW-w64, Cygwin MinGW-w64, MSVC2013, and MSVC2015.
The code compiles and runs in all of them.  I also examined the DACL
attached to the control pipe, and its SDDL string looked correct.
2016-01-15 23:35:01 -06:00
Ryan Prichard
1fafbc2ef5 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-01-15 18:55:43 -06:00
Ryan Prichard
95be1c291f Remove the ASSERT from OsModule to make it more suitable for winpty.dll 2016-01-15 18:50:24 -06:00
Ryan Prichard
fe60984ce4 Work around -std=c++11 MinGW compiler problem
This bug was already fixed 5 months ago, but AFAICT MinGW.org hasn't
released it yet.  MinGW-w64 is unaffected, as is the MinGW compiler
packaged with Cygwin.
2016-01-15 03:21:50 -06:00
Ryan Prichard
6b31bd9d8c Initial draft of a rewritten libwinpty with a new 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.

   (There are still many open design issues in this part of the API.)

 * 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.  There are various flags I'm planning to add, which are
   currently documented but not implemented.

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

As of this libwinpty rewrite, all code outside the UNIX adapter uses a
subset of C++11.  The code will build with MSVC2013 or newer.  The oldest
MinGW G++ I see on my machine is the somewhat broken MinGW (not MinGW-w64)
compiler distributed with Cygwin.  That compiler is G++ 4.7.3, which is
new enough.
2016-01-15 03:21:50 -06:00
215 changed files with 9207 additions and 12724 deletions

3
.gitattributes vendored
View File

@ -1,11 +1,9 @@
* 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
@ -17,3 +15,4 @@ configure text
*.sh eol=lf
configure eol=lf
VERSION.txt eol=lf
shared/GetCommitHash.cmd text eol=crlf

5
.gitignore vendored
View File

@ -2,15 +2,10 @@
*.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-2016 Ryan Prichard
Copyright (c) 2011-2015 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,42 +20,35 @@
# 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
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 :=
USE_PCH ?= 1
PREFIX ?= /usr/local
UNIX_ADAPTER_EXE ?= console.exe
# 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 := $(COMMON_CXXFLAGS) \
-DWINPTY_VERSION=$(shell cat VERSION.txt | tr -d '\r\n') \
-DWINPTY_VERSION_SUFFIX=$(VERSION_SUFFIX) \
-DWINPTY_COMMIT_HASH=$(COMMIT_HASH) \
-MMD -Wall \
-DUNICODE \
-D_UNICODE \
-D_WIN32_WINNT=0x0501 \
-Ibuild/gen
-DWINVER=0x0501 \
-D_WIN32_WINNT=0x0501
UNIX_CXXFLAGS += \
UNIX_CXXFLAGS := $(UNIX_CXXFLAGS) \
$(COMMON_CXXFLAGS)
MINGW_CXXFLAGS += \
MINGW_CXXFLAGS := $(MINGW_CXXFLAGS) \
$(COMMON_CXXFLAGS) \
-O2 \
$(MINGW_ENABLE_CXX11_FLAG)
@ -63,104 +56,67 @@ 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 | $$$$(@D)/.mkdir
$$(info Compiling $$<)
build/$1/%.o : src/%.cc VERSION.txt
@echo Compiling $$<
@mkdir -p $$(dir $$@)
@$$(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
build/$1/%.o : src/%.cc $$(PCH_DEP) | $$$$(@D)/.mkdir
$$(info Compiling $$<)
@$$(MINGW_CXX) $$(MINGW_CXXFLAGS) $2 -I src/include -c -o $$@ $$<
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 $$@ $$<
endef
include src/subdir.mk
.PHONY : all
all : $(ALL_TARGETS)
.PHONY : tests
tests : $(TEST_PROGRAMS)
.PHONY : install-bin
install-bin : all
install : 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
.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
clean-msvs :
rm -fr src/Default
rm -f src/*.vcxproj
rm -f src/*.vcxproj.filters
rm -f src/*.sln
.PHONY : distclean
distclean : clean
rm -f config.mk
.PRECIOUS : %.mkdir
%.mkdir :
$(info Creating directory $(dir $@))
@mkdir -p $(dir $@)
@touch $@
.PHONY : default all tests install clean clean-msvs distclean
src/%.h :
@echo "Missing header file $@ (stale dependency file?)"

151
README.md
View File

@ -1,151 +0,0 @@
# 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.

127
README.rst Normal file
View File

@ -0,0 +1,127 @@
======
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 Normal file → Executable file
View File

@ -1,174 +1,7 @@
# Next Version
# Next version
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`.
* Fix a bug that could have generated spurious mouse input records when an
incomplete mouse escape sequence was seen.
# Version 0.2.1 (2015-12-19)

2
VERSION.txt Normal file → Executable file
View File

@ -1 +1 @@
0.4.4-dev
0.2.2

View File

@ -1,16 +0,0 @@
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,19 +149,29 @@ 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 gnu++11
# mode instead. The bug was fixed on 2015-07-31, but as of 2016-02-26, the
# 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
# 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
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
# 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
else
echo "Commit info: none"
echo 'COMMIT_HASH := none' >> config.mk
echo 'COMMIT_HASH_DEP := config.mk' >> config.mk
echo "Build info: none"
echo 'VERSION_SUFFIX ?= -dev' >> config.mk
echo 'COMMIT_HASH ?= none' >> config.mk
echo 'BUILD_INFO_DEP = config.mk' >> config.mk
fi

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

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

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

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

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

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

View File

@ -1,117 +0,0 @@
#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;
}

View File

@ -1,116 +0,0 @@
#
# 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"
}

View File

@ -1,113 +0,0 @@
#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 Executable file → Normal file
View File

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

View File

@ -1,46 +0,0 @@
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

@ -1,528 +0,0 @@
==================================
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

@ -1,633 +0,0 @@
==================================
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

@ -1,630 +0,0 @@
=======================================
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

@ -1,630 +0,0 @@
==========================================================
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

@ -1,630 +0,0 @@
=====================================
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

@ -1,630 +0,0 @@
===========================================================
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

@ -1,16 +0,0 @@
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

@ -1,4 +0,0 @@
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

@ -1,144 +0,0 @@
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.

View File

@ -1,100 +0,0 @@
#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);
}

View File

@ -1,62 +0,0 @@
#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;
}

View File

@ -1,20 +0,0 @@
#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;
}

View File

@ -1,41 +0,0 @@
#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;
}

View File

@ -1,261 +0,0 @@
#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;
}

View File

@ -1,51 +0,0 @@
#
# 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?)"
}
}

View File

@ -1,87 +0,0 @@
// 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 Normal file → Executable file
View File

View File

@ -1,34 +0,0 @@
#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;
}

75
misc/QueryFont.cc Normal file
View File

@ -0,0 +1,75 @@
#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

@ -1,101 +0,0 @@
//
// 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 Normal file → Executable file
View File

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

View File

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

View File

@ -1,90 +0,0 @@
#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;
}

View File

@ -1,32 +0,0 @@
#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 Normal file → Executable file
View File

@ -1,6 +1,7 @@
#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,21 +5,32 @@
#include <string>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.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
// 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);
}
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");
@ -30,10 +41,8 @@ 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");
@ -53,6 +62,7 @@ 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);
@ -88,9 +98,6 @@ 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") {
@ -114,13 +121,11 @@ int main() {
} else if (arg == L"-decorative") {
fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_DECORATIVE;
} else if (arg == L"-face-gothic") {
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));
// ゴシック
const wchar_t gothicFace[] = {
0xFF2D, 0xFF33, 0x20, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0x0
};
wcsncpy(fontex.FaceName, gothicFace, COUNT_OF(fontex.FaceName));
} else {
cprintf(L"Unrecognized argument: %ls\n", arg.c_str());
exit(1);
@ -136,7 +141,7 @@ int main() {
fontex.FaceName);
BOOL ret = SetCurrentConsoleFontEx(
conout,
GetStdHandle(STD_OUTPUT_HANDLE),
FALSE,
&fontex);
cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret);

View File

@ -1,36 +0,0 @@
#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 Executable file → Normal file
View File

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

@ -9,11 +9,6 @@
#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) {
@ -161,12 +156,3 @@ 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,6 +16,7 @@
#include <string.h>
#include "TestUtil.cc"
#include "../src/shared/DebugClient.cc"
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))

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

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

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

@ -8,6 +8,7 @@
#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 = {static_cast<short>(readBuffer.size()), 1};
COORD bufSize = {readBuffer.size(), 1};
COORD bufCoord = {0, 0};
SMALL_RECT topLeft = {0, 0, static_cast<short>(readBuffer.size() - 1), 0};
SMALL_RECT topLeft = {0, 0, readBuffer.size() - 1, 0};
ret = ReadConsoleOutputW(
GetStdHandle(STD_OUTPUT_HANDLE), readBuffer.data(),
bufSize, bufCoord, &topLeft);

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

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

2
misc/Win10ResizeWhileFrozen.cc Normal file → Executable 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 Normal file → Executable file
View File

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

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

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

View File

@ -1,27 +0,0 @@
// 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;
}

View File

@ -1,106 +0,0 @@
#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;
}

1
misc/buffer-tests/.gitignore vendored Executable file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,101 @@
#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

@ -0,0 +1,66 @@
#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

@ -0,0 +1,212 @@
#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

@ -0,0 +1,81 @@
#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

@ -0,0 +1,47 @@
#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

@ -0,0 +1,53 @@
#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

@ -0,0 +1,388 @@
#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

@ -0,0 +1,53 @@
#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

@ -0,0 +1,190 @@
#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

@ -0,0 +1,223 @@
#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

@ -0,0 +1,240 @@
#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

@ -0,0 +1,307 @@
#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

@ -0,0 +1,117 @@
#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

@ -0,0 +1,33 @@
#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;
}

112
misc/buffer-tests/Makefile Normal file
View File

@ -0,0 +1,112 @@
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

@ -0,0 +1,119 @@
// 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

@ -0,0 +1,32 @@
#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

@ -0,0 +1,56 @@
#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

@ -0,0 +1,94 @@
#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

@ -0,0 +1,31 @@
#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

@ -0,0 +1,30 @@
#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

@ -0,0 +1,37 @@
#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

@ -0,0 +1,74 @@
#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

@ -0,0 +1,23 @@
#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

@ -0,0 +1,47 @@
#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

@ -0,0 +1,226 @@
#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

@ -0,0 +1,82 @@
#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

@ -0,0 +1,156 @@
#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

@ -0,0 +1,138 @@
#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

@ -0,0 +1,42 @@
#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

@ -0,0 +1,61 @@
#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

@ -0,0 +1,131 @@
#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

@ -0,0 +1,45 @@
#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

@ -0,0 +1,31 @@
#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

@ -0,0 +1,327 @@
#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

@ -0,0 +1,90 @@
#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

@ -0,0 +1,44 @@
#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

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

View File

@ -0,0 +1,126 @@
#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

@ -0,0 +1,12 @@
#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

@ -0,0 +1,355 @@
#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