You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cmake/Source/kwsys/testConsoleBuf.cxx

807 lines
29 KiB

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
#include "kwsysPrivate.h"
// Ignore Windows version levels defined by command-line flags. This
// source needs access to all APIs available on the host in order for
// the test to run properly. The test binary is not installed anyway.
#undef _WIN32_WINNT
#undef NTDDI_VERSION
#include KWSYS_HEADER(Encoding.hxx)
// Work-around CMake dependency scanning limitation. This must
// duplicate the above list of headers.
#if 0
# include "Encoding.hxx.in"
#endif
#if defined(_WIN32)
# include <algorithm>
# include <iomanip>
# include <iostream>
# include <stdexcept>
# include <string.h>
# include <wchar.h>
# include <windows.h>
# include "testConsoleBuf.hxx"
# if defined(_MSC_VER) && _MSC_VER >= 1800
# define KWSYS_WINDOWS_DEPRECATED_GetVersion
# endif
// يونيكود
static const WCHAR UnicodeInputTestString[] =
L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!";
static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE;
static const DWORD waitTimeout = 10 * 1000;
static STARTUPINFO startupInfo;
static PROCESS_INFORMATION processInfo;
static HANDLE beforeInputEvent;
static HANDLE afterOutputEvent;
static std::string encodedInputTestString;
static std::string encodedTestString;
static void displayError(DWORD errorCode)
{
std::cerr.setf(std::ios::hex, std::ios::basefield);
std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl;
LPWSTR message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) {
std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message)
<< std::endl;
HeapFree(GetProcessHeap(), 0, message);
} else {
std::cerr << "FormatMessage() failed with error: 0x" << GetLastError()
<< "!" << std::endl;
}
std::cerr.unsetf(std::ios::hex);
}
std::basic_streambuf<char>* errstream(const char* unused)
{
static_cast<void>(unused);
return std::cerr.rdbuf();
}
std::basic_streambuf<wchar_t>* errstream(const wchar_t* unused)
{
static_cast<void>(unused);
return std::wcerr.rdbuf();
}
template <typename T>
static void dumpBuffers(const T* expected, const T* received, size_t size)
{
std::basic_ostream<T> err(errstream(expected));
err << "Expected output: '" << std::basic_string<T>(expected, size) << "'"
<< std::endl;
if (err.fail()) {
err.clear();
err << "--- Error while outputting ---" << std::endl;
}
err << "Received output: '" << std::basic_string<T>(received, size) << "'"
<< std::endl;
if (err.fail()) {
err.clear();
err << "--- Error while outputting ---" << std::endl;
}
std::cerr << "Expected output | Received output" << std::endl;
for (size_t i = 0; i < size; i++) {
std::cerr << std::setbase(16) << std::setfill('0') << " "
<< "0x" << std::setw(8) << static_cast<unsigned int>(expected[i])
<< " | "
<< "0x" << std::setw(8)
<< static_cast<unsigned int>(received[i]);
if (static_cast<unsigned int>(expected[i]) !=
static_cast<unsigned int>(received[i])) {
std::cerr << " MISMATCH!";
}
std::cerr << std::endl;
}
std::cerr << std::endl;
}
static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr)
{
BOOL bInheritHandles = FALSE;
DWORD dwCreationFlags = 0;
memset(&processInfo, 0, sizeof(processInfo));
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_HIDE;
if (hIn || hOut || hErr) {
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
startupInfo.hStdInput = hIn;
startupInfo.hStdOutput = hOut;
startupInfo.hStdError = hErr;
bInheritHandles = TRUE;
}
WCHAR cmd[MAX_PATH];
if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) {
std::cerr << "GetModuleFileName failed!" << std::endl;
return false;
}
WCHAR* p = cmd + wcslen(cmd);
while (p > cmd && *p != L'\\')
p--;
*(p + 1) = 0;
wcscat(cmd, cmdConsoleBufChild);
wcscat(cmd, L".exe");
bool success =
CreateProcessW(nullptr, // No module name (use command line)
cmd, // Command line
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
bInheritHandles, // Set handle inheritance
dwCreationFlags,
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&processInfo) !=
0; // Pointer to PROCESS_INFORMATION structure
if (!success) {
DWORD lastError = GetLastError();
std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")"
<< std::endl;
displayError(lastError);
}
return success;
}
static void finishProcess(bool success)
{
if (success) {
success =
WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0;
};
if (!success) {
TerminateProcess(processInfo.hProcess, 1);
}
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
static bool createPipe(PHANDLE readPipe, PHANDLE writePipe)
{
SECURITY_ATTRIBUTES securityAttributes;
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttributes.bInheritHandle = TRUE;
securityAttributes.lpSecurityDescriptor = nullptr;
return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false
: true;
}
static void finishPipe(HANDLE readPipe, HANDLE writePipe)
{
if (readPipe != INVALID_HANDLE_VALUE) {
CloseHandle(readPipe);
}
if (writePipe != INVALID_HANDLE_VALUE) {
CloseHandle(writePipe);
}
}
static HANDLE createFile(LPCWSTR fileName)
{
SECURITY_ATTRIBUTES securityAttributes;
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttributes.bInheritHandle = TRUE;
securityAttributes.lpSecurityDescriptor = nullptr;
HANDLE file =
CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE,
0, // do not share
&securityAttributes,
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
nullptr); // no template
if (file == INVALID_HANDLE_VALUE) {
DWORD lastError = GetLastError();
std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")"
<< std::endl;
displayError(lastError);
}
return file;
}
static void finishFile(HANDLE file)
{
if (file != INVALID_HANDLE_VALUE) {
CloseHandle(file);
}
}
# ifndef MAPVK_VK_TO_VSC
# define MAPVK_VK_TO_VSC (0)
# endif
static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr)
{
inputBuffer[0].EventType = KEY_EVENT;
inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE;
inputBuffer[0].Event.KeyEvent.wRepeatCount = 1;
SHORT keyCode = VkKeyScanW(chr);
if (keyCode == -1) {
// Character can't be entered with current keyboard layout
// Just set any, it doesn't really matter
keyCode = 'K';
}
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode);
inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC);
inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr;
inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0;
if ((HIBYTE(keyCode) & 1) == 1) {
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
}
if ((HIBYTE(keyCode) & 2) == 2) {
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED;
}
if ((HIBYTE(keyCode) & 4) == 4) {
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED;
}
inputBuffer[1].EventType = inputBuffer[0].EventType;
inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE;
inputBuffer[1].Event.KeyEvent.wRepeatCount = 1;
inputBuffer[1].Event.KeyEvent.wVirtualKeyCode =
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode;
inputBuffer[1].Event.KeyEvent.wVirtualScanCode =
inputBuffer[0].Event.KeyEvent.wVirtualScanCode;
inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar =
inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar;
inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0;
}
static int testPipe()
{
int didFail = 1;
HANDLE inPipeRead = INVALID_HANDLE_VALUE;
HANDLE inPipeWrite = INVALID_HANDLE_VALUE;
HANDLE outPipeRead = INVALID_HANDLE_VALUE;
HANDLE outPipeWrite = INVALID_HANDLE_VALUE;
HANDLE errPipeRead = INVALID_HANDLE_VALUE;
HANDLE errPipeWrite = INVALID_HANDLE_VALUE;
UINT currentCodepage = GetConsoleCP();
char buffer[200];
char buffer2[200];
try {
if (!createPipe(&inPipeRead, &inPipeWrite) ||
!createPipe(&outPipeRead, &outPipeWrite) ||
!createPipe(&errPipeRead, &errPipeWrite)) {
throw std::runtime_error("createFile failed!");
}
if (TestCodepage == CP_ACP) {
TestCodepage = GetACP();
}
if (!SetConsoleCP(TestCodepage)) {
throw std::runtime_error("SetConsoleCP failed!");
}
DWORD bytesWritten = 0;
if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(),
(DWORD)encodedInputTestString.size(), &bytesWritten,
nullptr) ||
bytesWritten == 0) {
throw std::runtime_error("WriteFile failed!");
}
if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) {
try {
DWORD status;
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
WAIT_OBJECT_0) {
std::cerr.setf(std::ios::hex, std::ios::basefield);
std::cerr << "WaitForSingleObject returned unexpected status 0x"
<< status << std::endl;
std::cerr.unsetf(std::ios::hex);
throw std::runtime_error("WaitForSingleObject failed!");
}
DWORD bytesRead = 0;
if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead,
nullptr) ||
bytesRead == 0) {
throw std::runtime_error("ReadFile#1 failed!");
}
buffer[bytesRead] = 0;
if ((bytesRead <
encodedTestString.size() + 1 + encodedInputTestString.size() &&
!ReadFile(outPipeRead, buffer + bytesRead,
sizeof(buffer) - bytesRead, &bytesRead, nullptr)) ||
bytesRead == 0) {
throw std::runtime_error("ReadFile#2 failed!");
}
if (memcmp(buffer, encodedTestString.c_str(),
encodedTestString.size()) == 0 &&
memcmp(buffer + encodedTestString.size() + 1,
encodedInputTestString.c_str(),
encodedInputTestString.size()) == 0) {
bytesRead = 0;
if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead,
nullptr) ||
bytesRead == 0) {
throw std::runtime_error("ReadFile#3 failed!");
}
buffer2[bytesRead] = 0;
didFail = encodedTestString.compare(0, std::string::npos, buffer2,
encodedTestString.size()) == 0
? 0
: 1;
}
if (didFail != 0) {
std::cerr << "Pipe's output didn't match expected output!"
<< std::endl;
dumpBuffers<char>(encodedTestString.c_str(), buffer,
encodedTestString.size());
dumpBuffers<char>(encodedInputTestString.c_str(),
buffer + encodedTestString.size() + 1,
encodedInputTestString.size());
dumpBuffers<char>(encodedTestString.c_str(), buffer2,
encodedTestString.size());
}
} catch (const std::runtime_error& ex) {
DWORD lastError = GetLastError();
std::cerr << "In function testPipe, line " << __LINE__ << ": "
<< ex.what() << std::endl;
displayError(lastError);
}
finishProcess(didFail == 0);
}
} catch (const std::runtime_error& ex) {
DWORD lastError = GetLastError();
std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what()
<< std::endl;
displayError(lastError);
}
finishPipe(inPipeRead, inPipeWrite);
finishPipe(outPipeRead, outPipeWrite);
finishPipe(errPipeRead, errPipeWrite);
SetConsoleCP(currentCodepage);
return didFail;
}
static int testFile()
{
int didFail = 1;
HANDLE inFile = INVALID_HANDLE_VALUE;
HANDLE outFile = INVALID_HANDLE_VALUE;
HANDLE errFile = INVALID_HANDLE_VALUE;
try {
if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE ||
(outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE ||
(errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) {
throw std::runtime_error("createFile failed!");
}
DWORD bytesWritten = 0;
char buffer[200];
char buffer2[200];
int length;
if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString,
-1, buffer, sizeof(buffer), nullptr,
nullptr)) == 0) {
throw std::runtime_error("WideCharToMultiByte failed!");
}
buffer[length - 1] = '\n';
if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) ||
bytesWritten == 0) {
throw std::runtime_error("WriteFile failed!");
}
if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
throw std::runtime_error("SetFilePointer failed!");
}
if (createProcess(inFile, outFile, errFile)) {
DWORD bytesRead = 0;
try {
DWORD status;
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
WAIT_OBJECT_0) {
std::cerr.setf(std::ios::hex, std::ios::basefield);
std::cerr << "WaitForSingleObject returned unexpected status 0x"
<< status << std::endl;
std::cerr.unsetf(std::ios::hex);
throw std::runtime_error("WaitForSingleObject failed!");
}
if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) ==
INVALID_SET_FILE_POINTER) {
throw std::runtime_error("SetFilePointer#1 failed!");
}
if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) ||
bytesRead == 0) {
throw std::runtime_error("ReadFile#1 failed!");
}
buffer[bytesRead] = 0;
if (memcmp(buffer, encodedTestString.c_str(),
encodedTestString.size()) == 0 &&
memcmp(buffer + encodedTestString.size() + 1,
encodedInputTestString.c_str(),
encodedInputTestString.size()) == 0) {
bytesRead = 0;
if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) ==
INVALID_SET_FILE_POINTER) {
throw std::runtime_error("SetFilePointer#2 failed!");
}
if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead,
nullptr) ||
bytesRead == 0) {
throw std::runtime_error("ReadFile#2 failed!");
}
buffer2[bytesRead] = 0;
didFail = encodedTestString.compare(0, std::string::npos, buffer2,
encodedTestString.size()) == 0
? 0
: 1;
}
if (didFail != 0) {
std::cerr << "File's output didn't match expected output!"
<< std::endl;
dumpBuffers<char>(encodedTestString.c_str(), buffer,
encodedTestString.size());
dumpBuffers<char>(encodedInputTestString.c_str(),
buffer + encodedTestString.size() + 1,
encodedInputTestString.size());
dumpBuffers<char>(encodedTestString.c_str(), buffer2,
encodedTestString.size());
}
} catch (const std::runtime_error& ex) {
DWORD lastError = GetLastError();
std::cerr << "In function testFile, line " << __LINE__ << ": "
<< ex.what() << std::endl;
displayError(lastError);
}
finishProcess(didFail == 0);
}
} catch (const std::runtime_error& ex) {
DWORD lastError = GetLastError();
std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what()
<< std::endl;
displayError(lastError);
}
finishFile(inFile);
finishFile(outFile);
finishFile(errFile);
return didFail;
}
# ifndef _WIN32_WINNT_VISTA
# define _WIN32_WINNT_VISTA 0x0600
# endif
static bool consoleIsConhost()
{
wchar_t consoleClassNameBuf[64];
int const consoleClassNameLen = GetClassNameW(
GetConsoleWindow(), &consoleClassNameBuf[0], sizeof(consoleClassNameBuf));
// Windows Console Host: ConsoleWindowClass
// Windows Terminal / ConPTY: PseudoConsoleWindow (undocumented)
return (consoleClassNameLen > 0 &&
wcscmp(consoleClassNameBuf, L"ConsoleWindowClass") == 0);
}
static bool charIsNUL(wchar_t c)
{
return c == 0;
}
static int testConsole()
{
int didFail = 1;
HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE);
HANDLE hIn = parentIn;
HANDLE hOut = parentOut;
DWORD consoleMode;
bool newConsole = false;
bool forceNewConsole = false;
bool restoreConsole = false;
LPCWSTR TestFaceName = L"Lucida Console";
const DWORD TestFontFamily = 0x00000036;
const DWORD TestFontSize = 0x000c0000;
HKEY hConsoleKey;
WCHAR FaceName[200];
FaceName[0] = 0;
DWORD FaceNameSize = sizeof(FaceName);
DWORD FontFamily = TestFontFamily;
DWORD FontSize = TestFontSize;
# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
# pragma warning(push)
# ifdef __INTEL_COMPILER
# pragma warning(disable : 1478)
# elif defined __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# else
# pragma warning(disable : 4996)
# endif
# endif
const bool isVistaOrGreater =
LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA);
# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma warning(pop)
# endif
# endif
if (!isVistaOrGreater) {
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE,
&hConsoleKey) == ERROR_SUCCESS) {
DWORD dwordSize = sizeof(DWORD);
if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr,
(LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
if (FontFamily != TestFontFamily) {
RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr,
(LPBYTE)FaceName, &FaceNameSize);
RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr,
(LPBYTE)&FontSize, &dwordSize);
RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
(BYTE*)&TestFontFamily, sizeof(TestFontFamily));
RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ,
(BYTE*)TestFaceName,
(DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR)));
RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD,
(BYTE*)&TestFontSize, sizeof(TestFontSize));
restoreConsole = true;
forceNewConsole = true;
}
} else {
std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl;
}
RegCloseKey(hConsoleKey);
} else {
std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!"
<< std::endl;
}
}
if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) {
// Not a real console, let's create new one.
FreeConsole();
if (!AllocConsole()) {
std::cerr << "AllocConsole failed!" << std::endl;
return didFail;
}
SECURITY_ATTRIBUTES securityAttributes;
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttributes.bInheritHandle = TRUE;
securityAttributes.lpSecurityDescriptor = nullptr;
hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
OPEN_EXISTING, 0, nullptr);
if (hIn == INVALID_HANDLE_VALUE) {
DWORD lastError = GetLastError();
std::cerr << "CreateFile(CONIN$)" << std::endl;
displayError(lastError);
}
hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
OPEN_EXISTING, 0, nullptr);
if (hOut == INVALID_HANDLE_VALUE) {
DWORD lastError = GetLastError();
std::cerr << "CreateFile(CONOUT$)" << std::endl;
displayError(lastError);
}
SetStdHandle(STD_INPUT_HANDLE, hIn);
SetStdHandle(STD_OUTPUT_HANDLE, hOut);
SetStdHandle(STD_ERROR_HANDLE, hOut);
newConsole = true;
}
# if _WIN32_WINNT >= _WIN32_WINNT_VISTA
if (isVistaOrGreater) {
CONSOLE_FONT_INFOEX consoleFont;
memset(&consoleFont, 0, sizeof(consoleFont));
consoleFont.cbSize = sizeof(consoleFont);
HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)(
HANDLE hConsoleOutput, BOOL bMaximumWindow,
PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)(
HANDLE hConsoleOutput, BOOL bMaximumWindow,
PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
GetCurrentConsoleFontExFunc getConsoleFont =
(GetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
"GetCurrentConsoleFontEx");
SetCurrentConsoleFontExFunc setConsoleFont =
(SetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
"SetCurrentConsoleFontEx");
if (getConsoleFont(hOut, FALSE, &consoleFont)) {
if (consoleFont.FontFamily != TestFontFamily) {
consoleFont.FontFamily = TestFontFamily;
wcscpy(consoleFont.FaceName, TestFaceName);
if (!setConsoleFont(hOut, FALSE, &consoleFont)) {
std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl;
}
}
} else {
std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl;
}
} else {
# endif
if (restoreConsole &&
RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE,
&hConsoleKey) == ERROR_SUCCESS) {
RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
(BYTE*)&FontFamily, sizeof(FontFamily));
if (FaceName[0] != 0) {
RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName,
FaceNameSize);
} else {
RegDeleteValueW(hConsoleKey, L"FaceName");
}
RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize,
sizeof(FontSize));
RegCloseKey(hConsoleKey);
}
# if _WIN32_WINNT >= _WIN32_WINNT_VISTA
}
# endif
if (createProcess(nullptr, nullptr, nullptr)) {
try {
DWORD status;
if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) !=
WAIT_OBJECT_0) {
std::cerr.setf(std::ios::hex, std::ios::basefield);
std::cerr << "WaitForSingleObject returned unexpected status 0x"
<< status << std::endl;
std::cerr.unsetf(std::ios::hex);
throw std::runtime_error("WaitForSingleObject#1 failed!");
}
INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) /
sizeof(UnicodeInputTestString[0])) *
2];
memset(&inputBuffer, 0, sizeof(inputBuffer));
unsigned int i;
for (i = 0; i < (sizeof(UnicodeInputTestString) /
sizeof(UnicodeInputTestString[0]) -
1);
i++) {
writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]);
}
writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN);
DWORD eventsWritten = 0;
// We need to wait a bit before writing to console so child process have
// started waiting for input on stdin.
Sleep(300);
if (!WriteConsoleInputW(hIn, inputBuffer,
sizeof(inputBuffer) / sizeof(inputBuffer[0]),
&eventsWritten) ||
eventsWritten == 0) {
throw std::runtime_error("WriteConsoleInput failed!");
}
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
WAIT_OBJECT_0) {
std::cerr.setf(std::ios::hex, std::ios::basefield);
std::cerr << "WaitForSingleObject returned unexpected status 0x"
<< status << std::endl;
std::cerr.unsetf(std::ios::hex);
throw std::runtime_error("WaitForSingleObject#2 failed!");
}
CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) {
throw std::runtime_error("GetConsoleScreenBufferInfo failed!");
}
COORD coord;
DWORD charsRead = 0;
coord.X = 0;
coord.Y = screenBufferInfo.dwCursorPosition.Y - 4;
WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4];
if (!ReadConsoleOutputCharacterW(hOut, outputBuffer,
screenBufferInfo.dwSize.X * 4, coord,
&charsRead) ||
charsRead == 0) {
delete[] outputBuffer;
throw std::runtime_error("ReadConsoleOutputCharacter failed!");
}
std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString);
if (consoleIsConhost()) {
// Windows Console Host converts NUL bytes to spaces.
std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' ');
} else {
// Windows Terminal / ConPTY removes NUL bytes.
wideTestString.erase(std::remove_if(wideTestString.begin(),
wideTestString.end(), charIsNUL),
wideTestString.end());
}
std::wstring wideInputTestString =
kwsys::Encoding::ToWide(encodedInputTestString);
if (memcmp(outputBuffer, wideTestString.c_str(),
wideTestString.size() * sizeof(wchar_t)) == 0 &&
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1,
wideTestString.c_str(),
wideTestString.size() * sizeof(wchar_t)) == 0 &&
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2,
UnicodeInputTestString,
sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 &&
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3,
wideInputTestString.c_str(),
(wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) {
didFail = 0;
} else {
std::cerr << "Console's output didn't match expected output!"
<< std::endl;
dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer,
wideTestString.size());
dumpBuffers<wchar_t>(wideTestString.c_str(),
outputBuffer + screenBufferInfo.dwSize.X * 1,
wideTestString.size());
dumpBuffers<wchar_t>(
UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2,
(sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR));
dumpBuffers<wchar_t>(wideInputTestString.c_str(),
outputBuffer + screenBufferInfo.dwSize.X * 3,
wideInputTestString.size() - 1);
}
delete[] outputBuffer;
} catch (const std::runtime_error& ex) {
DWORD lastError = GetLastError();
std::cerr << "In function testConsole, line " << __LINE__ << ": "
<< ex.what() << std::endl;
displayError(lastError);
}
finishProcess(didFail == 0);
}
if (newConsole) {
SetStdHandle(STD_INPUT_HANDLE, parentIn);
SetStdHandle(STD_OUTPUT_HANDLE, parentOut);
SetStdHandle(STD_ERROR_HANDLE, parentErr);
CloseHandle(hIn);
CloseHandle(hOut);
FreeConsole();
}
return didFail;
}
#endif
int testConsoleBuf(int, char*[])
{
int ret = 0;
#if defined(_WIN32)
beforeInputEvent = CreateEventW(nullptr,
FALSE, // auto-reset event
FALSE, // initial state is nonsignaled
BeforeInputEventName); // object name
if (!beforeInputEvent) {
std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl;
return 1;
}
afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName);
if (!afterOutputEvent) {
std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl;
return 1;
}
encodedTestString = kwsys::Encoding::ToNarrow(std::wstring(
UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1));
encodedInputTestString = kwsys::Encoding::ToNarrow(
std::wstring(UnicodeInputTestString,
sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1));
encodedInputTestString += "\n";
ret |= testPipe();
ret |= testFile();
ret |= testConsole();
CloseHandle(beforeInputEvent);
CloseHandle(afterOutputEvent);
#endif
return ret;
}