| /* 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 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); |
| std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); |
| 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; |
| } |