|
|
@@ -0,0 +1,609 @@
|
|
|
+/*============================================================================
|
|
|
+ KWSys - Kitware System Library
|
|
|
+ Copyright 2000-2016 Kitware, Inc., Insight Software Consortium
|
|
|
+
|
|
|
+ Distributed under the OSI-approved BSD License (the "License");
|
|
|
+ see accompanying file Copyright.txt for details.
|
|
|
+
|
|
|
+ This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
|
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
+ See the License for more information.
|
|
|
+============================================================================*/
|
|
|
+#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 <windows.h>
|
|
|
+#include <string.h>
|
|
|
+#include <wchar.h>
|
|
|
+#include <iostream>
|
|
|
+#include <stdexcept>
|
|
|
+#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 syncEvent;
|
|
|
+static std::string encodedInputTestString;
|
|
|
+static std::string encodedTestString;
|
|
|
+
|
|
|
+//----------------------------------------------------------------------------
|
|
|
+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(NULL, 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(NULL, // No module name (use command line)
|
|
|
+ cmd, // Command line
|
|
|
+ NULL, // Process handle not inheritable
|
|
|
+ NULL, // Thread handle not inheritable
|
|
|
+ bInheritHandles, // Set handle inheritance
|
|
|
+ dwCreationFlags,
|
|
|
+ NULL, // Use parent's environment block
|
|
|
+ NULL, // Use parent's starting directory
|
|
|
+ &startupInfo, // Pointer to STARTUPINFO structure
|
|
|
+ &processInfo) != 0; // Pointer to PROCESS_INFORMATION structure
|
|
|
+ if (!success) {
|
|
|
+ DWORD lastError = GetLastError();
|
|
|
+ std::cerr.setf(std::ios::hex, std::ios::basefield);
|
|
|
+ std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd)
|
|
|
+ << ") failed with error: 0x" << lastError << "!" << std::endl;
|
|
|
+ LPWSTR message;
|
|
|
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
|
+ NULL,
|
|
|
+ lastError,
|
|
|
+ 0,
|
|
|
+ (LPWSTR)&message, 0,
|
|
|
+ NULL)
|
|
|
+ ) {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ 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 = NULL;
|
|
|
+ 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 = NULL;
|
|
|
+
|
|
|
+ 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,
|
|
|
+ NULL); // no template
|
|
|
+ if (file == INVALID_HANDLE_VALUE) {
|
|
|
+ std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName)
|
|
|
+ << ") failed!" << std::endl;
|
|
|
+ }
|
|
|
+ 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];
|
|
|
+ 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, NULL)
|
|
|
+ || bytesWritten == 0) {
|
|
|
+ throw std::runtime_error("WriteFile failed!");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) {
|
|
|
+ try {
|
|
|
+ Sleep(100);
|
|
|
+ if (WaitForSingleObject(syncEvent, waitTimeout) != WAIT_OBJECT_0) {
|
|
|
+ throw std::runtime_error("WaitForSingleObject failed!");
|
|
|
+ }
|
|
|
+ DWORD bytesRead = 0;
|
|
|
+ if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead, NULL)
|
|
|
+ || bytesRead == 0) {
|
|
|
+ throw std::runtime_error("ReadFile failed!");
|
|
|
+ }
|
|
|
+ if ((bytesRead < encodedTestString.size() + 1 + encodedInputTestString.size()
|
|
|
+ && !ReadFile(outPipeRead, buffer + bytesRead,
|
|
|
+ sizeof(buffer) - bytesRead, &bytesRead, NULL))
|
|
|
+ || bytesRead == 0) {
|
|
|
+ throw std::runtime_error("ReadFile 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, buffer, sizeof(buffer), &bytesRead, NULL)
|
|
|
+ || bytesRead == 0) {
|
|
|
+ throw std::runtime_error("ReadFile failed!");
|
|
|
+ }
|
|
|
+ buffer[bytesRead - 1] = 0;
|
|
|
+ didFail = encodedTestString.compare(buffer) == 0 ? 0 : 1;
|
|
|
+ }
|
|
|
+ if (didFail != 0) {
|
|
|
+ std::cerr << "Pipe's output didn't match expected output!" << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ } catch (const std::runtime_error &ex) {
|
|
|
+ std::cerr << ex.what() << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ finishProcess(didFail == 0);
|
|
|
+ }
|
|
|
+ } catch (const std::runtime_error &ex) {
|
|
|
+ std::cerr << ex.what() << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ 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!");
|
|
|
+ }
|
|
|
+ int length = 0;
|
|
|
+ DWORD bytesWritten = 0;
|
|
|
+ char buffer[200];
|
|
|
+
|
|
|
+ if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, -1,
|
|
|
+ buffer, sizeof(buffer),
|
|
|
+ NULL, NULL)) == 0) {
|
|
|
+ throw std::runtime_error("WideCharToMultiByte failed!");
|
|
|
+ }
|
|
|
+ buffer[length - 1] = '\n';
|
|
|
+ if (!WriteFile(inFile, buffer, length, &bytesWritten, NULL)
|
|
|
+ || 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 {
|
|
|
+ Sleep(100);
|
|
|
+ if (WaitForSingleObject(syncEvent, waitTimeout) != WAIT_OBJECT_0) {
|
|
|
+ throw std::runtime_error("WaitForSingleObject failed!");
|
|
|
+ }
|
|
|
+ if (SetFilePointer(outFile, 0, 0, FILE_BEGIN)
|
|
|
+ == INVALID_SET_FILE_POINTER) {
|
|
|
+ throw std::runtime_error("SetFilePointer failed!");
|
|
|
+ }
|
|
|
+ if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, NULL)
|
|
|
+ || bytesRead == 0) {
|
|
|
+ throw std::runtime_error("ReadFile failed!");
|
|
|
+ }
|
|
|
+ buffer[bytesRead - 1] = 0;
|
|
|
+ if (memcmp(buffer, encodedTestString.c_str(),
|
|
|
+ encodedTestString.size()) == 0 &&
|
|
|
+ memcmp(buffer + encodedTestString.size() + 1,
|
|
|
+ encodedInputTestString.c_str(),
|
|
|
+ encodedInputTestString.size() - 1) == 0) {
|
|
|
+ bytesRead = 0;
|
|
|
+ if (SetFilePointer(errFile, 0, 0, FILE_BEGIN)
|
|
|
+ == INVALID_SET_FILE_POINTER) {
|
|
|
+ throw std::runtime_error("SetFilePointer failed!");
|
|
|
+ }
|
|
|
+ if (!ReadFile(errFile, buffer, sizeof(buffer), &bytesRead, NULL)
|
|
|
+ || bytesRead == 0) {
|
|
|
+ throw std::runtime_error("ReadFile failed!");
|
|
|
+ }
|
|
|
+ buffer[bytesRead - 1] = 0;
|
|
|
+ didFail = encodedTestString.compare(buffer) == 0 ? 0 : 1;
|
|
|
+ }
|
|
|
+ if (didFail != 0) {
|
|
|
+ std::cerr << "File's output didn't match expected output!" << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ } catch (const std::runtime_error &ex) {
|
|
|
+ std::cerr << ex.what() << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ finishProcess(didFail == 0);
|
|
|
+ }
|
|
|
+ } catch (const std::runtime_error &ex) {
|
|
|
+ std::cerr << ex.what() << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ 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];
|
|
|
+ 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)
|
|
|
+# else
|
|
|
+# pragma warning (disable:4996)
|
|
|
+# endif
|
|
|
+#endif
|
|
|
+ const bool isVistaOrGreater = LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA);
|
|
|
+#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
|
|
|
+# pragma warning (pop)
|
|
|
+#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", NULL, NULL,
|
|
|
+ (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
|
|
|
+ if (FontFamily != TestFontFamily) {
|
|
|
+ RegQueryValueExW(hConsoleKey, L"FaceName", NULL, NULL,
|
|
|
+ (LPBYTE)FaceName, &FaceNameSize);
|
|
|
+ RegQueryValueExW(hConsoleKey, L"FontSize", NULL, NULL,
|
|
|
+ (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 << std::flush;
|
|
|
+ }
|
|
|
+ RegCloseKey(hConsoleKey);
|
|
|
+ } else {
|
|
|
+ std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) {
|
|
|
+ // Not a real console, let's create new one.
|
|
|
+ FreeConsole();
|
|
|
+ if (!AllocConsole()) {
|
|
|
+ std::cerr << "AllocConsole failed!" << std::endl << std::flush;
|
|
|
+ return didFail;
|
|
|
+ }
|
|
|
+ SECURITY_ATTRIBUTES securityAttributes;
|
|
|
+ securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
+ securityAttributes.bInheritHandle = TRUE;
|
|
|
+ securityAttributes.lpSecurityDescriptor = NULL;
|
|
|
+ hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, 0,
|
|
|
+ &securityAttributes, OPEN_EXISTING, 0, NULL);
|
|
|
+ hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, 0,
|
|
|
+ &securityAttributes, OPEN_EXISTING, 0, NULL);
|
|
|
+ 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 << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ } 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));
|
|
|
+ RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ,
|
|
|
+ (BYTE *)FaceName, FaceNameSize);
|
|
|
+ RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD,
|
|
|
+ (BYTE *)&FontSize, sizeof(FontSize));
|
|
|
+ RegCloseKey(hConsoleKey);
|
|
|
+ }
|
|
|
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (createProcess(NULL, NULL, NULL)) {
|
|
|
+ try {
|
|
|
+ if (WaitForSingleObject(syncEvent, waitTimeout) != WAIT_OBJECT_0) {
|
|
|
+ throw std::runtime_error("WaitForSingleObject failed!");
|
|
|
+ }
|
|
|
+ INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) /
|
|
|
+ sizeof(UnicodeInputTestString[0])) * 2];
|
|
|
+ memset(&inputBuffer, 0, sizeof(inputBuffer));
|
|
|
+ unsigned int i = 0;
|
|
|
+ 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;
|
|
|
+ if (!WriteConsoleInputW(hIn, inputBuffer, sizeof(inputBuffer) /
|
|
|
+ sizeof(inputBuffer[0]),
|
|
|
+ &eventsWritten) || eventsWritten == 0) {
|
|
|
+ throw std::runtime_error("WriteConsoleInput failed!");
|
|
|
+ }
|
|
|
+ if (WaitForSingleObject(syncEvent, waitTimeout) != WAIT_OBJECT_0) {
|
|
|
+ throw std::runtime_error("WaitForSingleObject 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::wstring wideInputTestString = kwsys::Encoding::ToWide(encodedInputTestString);
|
|
|
+ if (memcmp(outputBuffer, wideTestString.c_str(), wideTestString.size()) == 0 &&
|
|
|
+ memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1,
|
|
|
+ wideTestString.c_str(), wideTestString.size()) == 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) == 0
|
|
|
+ ) {
|
|
|
+ didFail = 0;
|
|
|
+ } else {
|
|
|
+ std::cerr << "Console's output didn't match expected output!" << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ delete[] outputBuffer;
|
|
|
+ } catch (const std::runtime_error &ex) {
|
|
|
+ std::cerr << ex.what() << std::endl << std::flush;
|
|
|
+ }
|
|
|
+ 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)
|
|
|
+ syncEvent = CreateEventW(NULL,
|
|
|
+ FALSE, // auto-reset event
|
|
|
+ FALSE, // initial state is nonsignaled
|
|
|
+ SyncEventName); // object name
|
|
|
+ if (!syncEvent) {
|
|
|
+ std::cerr << "CreateEvent failed " << GetLastError() << std::endl;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ encodedTestString = kwsys::Encoding::ToNarrow(UnicodeTestString);
|
|
|
+ encodedInputTestString = kwsys::Encoding::ToNarrow(UnicodeInputTestString);
|
|
|
+ encodedInputTestString += "\n";
|
|
|
+
|
|
|
+ ret |= testPipe();
|
|
|
+ ret |= testFile();
|
|
|
+ ret |= testConsole();
|
|
|
+
|
|
|
+ CloseHandle(syncEvent);
|
|
|
+#endif
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|