|
@@ -1,806 +0,0 @@
|
|
|
-/* 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(char const* unused)
|
|
|
-{
|
|
|
- static_cast<void>(unused);
|
|
|
- return std::cerr.rdbuf();
|
|
|
-}
|
|
|
-
|
|
|
-std::basic_streambuf<wchar_t>* errstream(wchar_t const* unused)
|
|
|
-{
|
|
|
- static_cast<void>(unused);
|
|
|
- return std::wcerr.rdbuf();
|
|
|
-}
|
|
|
-
|
|
|
-template <typename T>
|
|
|
-static void dumpBuffers(T const* expected, T const* 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 (std::runtime_error const& ex) {
|
|
|
- DWORD lastError = GetLastError();
|
|
|
- std::cerr << "In function testPipe, line " << __LINE__ << ": "
|
|
|
- << ex.what() << std::endl;
|
|
|
- displayError(lastError);
|
|
|
- }
|
|
|
- finishProcess(didFail == 0);
|
|
|
- }
|
|
|
- } catch (std::runtime_error const& 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 (std::runtime_error const& ex) {
|
|
|
- DWORD lastError = GetLastError();
|
|
|
- std::cerr << "In function testFile, line " << __LINE__ << ": "
|
|
|
- << ex.what() << std::endl;
|
|
|
- displayError(lastError);
|
|
|
- }
|
|
|
- finishProcess(didFail == 0);
|
|
|
- }
|
|
|
- } catch (std::runtime_error const& 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
|
|
|
- bool const 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 (std::runtime_error const& 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;
|
|
|
-}
|