1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
3 #include "kwsysPrivate.h"
4
5 // Ignore Windows version levels defined by command-line flags. This
6 // source needs access to all APIs available on the host in order for
7 // the test to run properly. The test binary is not installed anyway.
8 #undef _WIN32_WINNT
9 #undef NTDDI_VERSION
10
11 #include KWSYS_HEADER(Encoding.hxx)
12
13 // Work-around CMake dependency scanning limitation. This must
14 // duplicate the above list of headers.
15 #if 0
16 # include "Encoding.hxx.in"
17 #endif
18
19 #if defined(_WIN32)
20
21 # include <algorithm>
22 # include <iomanip>
23 # include <iostream>
24 # include <stdexcept>
25 # include <string.h>
26 # include <wchar.h>
27 # include <windows.h>
28
29 # include "testConsoleBuf.hxx"
30
31 # if defined(_MSC_VER) && _MSC_VER >= 1800
32 # define KWSYS_WINDOWS_DEPRECATED_GetVersion
33 # endif
34 // يونيكود
35 static const WCHAR UnicodeInputTestString[] =
36 L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!";
37 static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE;
38
39 static const DWORD waitTimeout = 10 * 1000;
40 static STARTUPINFO startupInfo;
41 static PROCESS_INFORMATION processInfo;
42 static HANDLE beforeInputEvent;
43 static HANDLE afterOutputEvent;
44 static std::string encodedInputTestString;
45 static std::string encodedTestString;
46
displayError(DWORD errorCode)47 static void displayError(DWORD errorCode)
48 {
49 std::cerr.setf(std::ios::hex, std::ios::basefield);
50 std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl;
51 LPWSTR message;
52 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
53 FORMAT_MESSAGE_FROM_SYSTEM,
54 nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) {
55 std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message)
56 << std::endl;
57 HeapFree(GetProcessHeap(), 0, message);
58 } else {
59 std::cerr << "FormatMessage() failed with error: 0x" << GetLastError()
60 << "!" << std::endl;
61 }
62 std::cerr.unsetf(std::ios::hex);
63 }
64
errstream(const char * unused)65 std::basic_streambuf<char>* errstream(const char* unused)
66 {
67 static_cast<void>(unused);
68 return std::cerr.rdbuf();
69 }
70
errstream(const wchar_t * unused)71 std::basic_streambuf<wchar_t>* errstream(const wchar_t* unused)
72 {
73 static_cast<void>(unused);
74 return std::wcerr.rdbuf();
75 }
76
77 template <typename T>
dumpBuffers(const T * expected,const T * received,size_t size)78 static void dumpBuffers(const T* expected, const T* received, size_t size)
79 {
80 std::basic_ostream<T> err(errstream(expected));
81 err << "Expected output: '" << std::basic_string<T>(expected, size) << "'"
82 << std::endl;
83 if (err.fail()) {
84 err.clear();
85 err << "--- Error while outputting ---" << std::endl;
86 }
87 err << "Received output: '" << std::basic_string<T>(received, size) << "'"
88 << std::endl;
89 if (err.fail()) {
90 err.clear();
91 err << "--- Error while outputting ---" << std::endl;
92 }
93 std::cerr << "Expected output | Received output" << std::endl;
94 for (size_t i = 0; i < size; i++) {
95 std::cerr << std::setbase(16) << std::setfill('0') << " "
96 << "0x" << std::setw(8) << static_cast<unsigned int>(expected[i])
97 << " | "
98 << "0x" << std::setw(8)
99 << static_cast<unsigned int>(received[i]);
100 if (static_cast<unsigned int>(expected[i]) !=
101 static_cast<unsigned int>(received[i])) {
102 std::cerr << " MISMATCH!";
103 }
104 std::cerr << std::endl;
105 }
106 std::cerr << std::endl;
107 }
108
createProcess(HANDLE hIn,HANDLE hOut,HANDLE hErr)109 static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr)
110 {
111 BOOL bInheritHandles = FALSE;
112 DWORD dwCreationFlags = 0;
113 memset(&processInfo, 0, sizeof(processInfo));
114 memset(&startupInfo, 0, sizeof(startupInfo));
115 startupInfo.cb = sizeof(startupInfo);
116 startupInfo.dwFlags = STARTF_USESHOWWINDOW;
117 startupInfo.wShowWindow = SW_HIDE;
118 if (hIn || hOut || hErr) {
119 startupInfo.dwFlags |= STARTF_USESTDHANDLES;
120 startupInfo.hStdInput = hIn;
121 startupInfo.hStdOutput = hOut;
122 startupInfo.hStdError = hErr;
123 bInheritHandles = TRUE;
124 }
125
126 WCHAR cmd[MAX_PATH];
127 if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) {
128 std::cerr << "GetModuleFileName failed!" << std::endl;
129 return false;
130 }
131 WCHAR* p = cmd + wcslen(cmd);
132 while (p > cmd && *p != L'\\')
133 p--;
134 *(p + 1) = 0;
135 wcscat(cmd, cmdConsoleBufChild);
136 wcscat(cmd, L".exe");
137
138 bool success =
139 CreateProcessW(nullptr, // No module name (use command line)
140 cmd, // Command line
141 nullptr, // Process handle not inheritable
142 nullptr, // Thread handle not inheritable
143 bInheritHandles, // Set handle inheritance
144 dwCreationFlags,
145 nullptr, // Use parent's environment block
146 nullptr, // Use parent's starting directory
147 &startupInfo, // Pointer to STARTUPINFO structure
148 &processInfo) !=
149 0; // Pointer to PROCESS_INFORMATION structure
150 if (!success) {
151 DWORD lastError = GetLastError();
152 std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")"
153 << std::endl;
154 displayError(lastError);
155 }
156 return success;
157 }
158
finishProcess(bool success)159 static void finishProcess(bool success)
160 {
161 if (success) {
162 success =
163 WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0;
164 };
165 if (!success) {
166 TerminateProcess(processInfo.hProcess, 1);
167 }
168 CloseHandle(processInfo.hProcess);
169 CloseHandle(processInfo.hThread);
170 }
171
createPipe(PHANDLE readPipe,PHANDLE writePipe)172 static bool createPipe(PHANDLE readPipe, PHANDLE writePipe)
173 {
174 SECURITY_ATTRIBUTES securityAttributes;
175 securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
176 securityAttributes.bInheritHandle = TRUE;
177 securityAttributes.lpSecurityDescriptor = nullptr;
178 return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false
179 : true;
180 }
181
finishPipe(HANDLE readPipe,HANDLE writePipe)182 static void finishPipe(HANDLE readPipe, HANDLE writePipe)
183 {
184 if (readPipe != INVALID_HANDLE_VALUE) {
185 CloseHandle(readPipe);
186 }
187 if (writePipe != INVALID_HANDLE_VALUE) {
188 CloseHandle(writePipe);
189 }
190 }
191
createFile(LPCWSTR fileName)192 static HANDLE createFile(LPCWSTR fileName)
193 {
194 SECURITY_ATTRIBUTES securityAttributes;
195 securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
196 securityAttributes.bInheritHandle = TRUE;
197 securityAttributes.lpSecurityDescriptor = nullptr;
198
199 HANDLE file =
200 CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE,
201 0, // do not share
202 &securityAttributes,
203 CREATE_ALWAYS, // overwrite existing
204 FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
205 nullptr); // no template
206 if (file == INVALID_HANDLE_VALUE) {
207 DWORD lastError = GetLastError();
208 std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")"
209 << std::endl;
210 displayError(lastError);
211 }
212 return file;
213 }
214
finishFile(HANDLE file)215 static void finishFile(HANDLE file)
216 {
217 if (file != INVALID_HANDLE_VALUE) {
218 CloseHandle(file);
219 }
220 }
221
222 # ifndef MAPVK_VK_TO_VSC
223 # define MAPVK_VK_TO_VSC (0)
224 # endif
225
writeInputKeyEvent(INPUT_RECORD inputBuffer[],WCHAR chr)226 static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr)
227 {
228 inputBuffer[0].EventType = KEY_EVENT;
229 inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE;
230 inputBuffer[0].Event.KeyEvent.wRepeatCount = 1;
231 SHORT keyCode = VkKeyScanW(chr);
232 if (keyCode == -1) {
233 // Character can't be entered with current keyboard layout
234 // Just set any, it doesn't really matter
235 keyCode = 'K';
236 }
237 inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode);
238 inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(
239 inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC);
240 inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr;
241 inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0;
242 if ((HIBYTE(keyCode) & 1) == 1) {
243 inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
244 }
245 if ((HIBYTE(keyCode) & 2) == 2) {
246 inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED;
247 }
248 if ((HIBYTE(keyCode) & 4) == 4) {
249 inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED;
250 }
251 inputBuffer[1].EventType = inputBuffer[0].EventType;
252 inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE;
253 inputBuffer[1].Event.KeyEvent.wRepeatCount = 1;
254 inputBuffer[1].Event.KeyEvent.wVirtualKeyCode =
255 inputBuffer[0].Event.KeyEvent.wVirtualKeyCode;
256 inputBuffer[1].Event.KeyEvent.wVirtualScanCode =
257 inputBuffer[0].Event.KeyEvent.wVirtualScanCode;
258 inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar =
259 inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar;
260 inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0;
261 }
262
testPipe()263 static int testPipe()
264 {
265 int didFail = 1;
266 HANDLE inPipeRead = INVALID_HANDLE_VALUE;
267 HANDLE inPipeWrite = INVALID_HANDLE_VALUE;
268 HANDLE outPipeRead = INVALID_HANDLE_VALUE;
269 HANDLE outPipeWrite = INVALID_HANDLE_VALUE;
270 HANDLE errPipeRead = INVALID_HANDLE_VALUE;
271 HANDLE errPipeWrite = INVALID_HANDLE_VALUE;
272 UINT currentCodepage = GetConsoleCP();
273 char buffer[200];
274 char buffer2[200];
275 try {
276 if (!createPipe(&inPipeRead, &inPipeWrite) ||
277 !createPipe(&outPipeRead, &outPipeWrite) ||
278 !createPipe(&errPipeRead, &errPipeWrite)) {
279 throw std::runtime_error("createFile failed!");
280 }
281 if (TestCodepage == CP_ACP) {
282 TestCodepage = GetACP();
283 }
284 if (!SetConsoleCP(TestCodepage)) {
285 throw std::runtime_error("SetConsoleCP failed!");
286 }
287
288 DWORD bytesWritten = 0;
289 if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(),
290 (DWORD)encodedInputTestString.size(), &bytesWritten,
291 nullptr) ||
292 bytesWritten == 0) {
293 throw std::runtime_error("WriteFile failed!");
294 }
295
296 if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) {
297 try {
298 DWORD status;
299 if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
300 WAIT_OBJECT_0) {
301 std::cerr.setf(std::ios::hex, std::ios::basefield);
302 std::cerr << "WaitForSingleObject returned unexpected status 0x"
303 << status << std::endl;
304 std::cerr.unsetf(std::ios::hex);
305 throw std::runtime_error("WaitForSingleObject failed!");
306 }
307 DWORD bytesRead = 0;
308 if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead,
309 nullptr) ||
310 bytesRead == 0) {
311 throw std::runtime_error("ReadFile#1 failed!");
312 }
313 buffer[bytesRead] = 0;
314 if ((bytesRead <
315 encodedTestString.size() + 1 + encodedInputTestString.size() &&
316 !ReadFile(outPipeRead, buffer + bytesRead,
317 sizeof(buffer) - bytesRead, &bytesRead, nullptr)) ||
318 bytesRead == 0) {
319 throw std::runtime_error("ReadFile#2 failed!");
320 }
321 if (memcmp(buffer, encodedTestString.c_str(),
322 encodedTestString.size()) == 0 &&
323 memcmp(buffer + encodedTestString.size() + 1,
324 encodedInputTestString.c_str(),
325 encodedInputTestString.size()) == 0) {
326 bytesRead = 0;
327 if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead,
328 nullptr) ||
329 bytesRead == 0) {
330 throw std::runtime_error("ReadFile#3 failed!");
331 }
332 buffer2[bytesRead] = 0;
333 didFail = encodedTestString.compare(0, std::string::npos, buffer2,
334 encodedTestString.size()) == 0
335 ? 0
336 : 1;
337 }
338 if (didFail != 0) {
339 std::cerr << "Pipe's output didn't match expected output!"
340 << std::endl;
341 dumpBuffers<char>(encodedTestString.c_str(), buffer,
342 encodedTestString.size());
343 dumpBuffers<char>(encodedInputTestString.c_str(),
344 buffer + encodedTestString.size() + 1,
345 encodedInputTestString.size());
346 dumpBuffers<char>(encodedTestString.c_str(), buffer2,
347 encodedTestString.size());
348 }
349 } catch (const std::runtime_error& ex) {
350 DWORD lastError = GetLastError();
351 std::cerr << "In function testPipe, line " << __LINE__ << ": "
352 << ex.what() << std::endl;
353 displayError(lastError);
354 }
355 finishProcess(didFail == 0);
356 }
357 } catch (const std::runtime_error& ex) {
358 DWORD lastError = GetLastError();
359 std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what()
360 << std::endl;
361 displayError(lastError);
362 }
363 finishPipe(inPipeRead, inPipeWrite);
364 finishPipe(outPipeRead, outPipeWrite);
365 finishPipe(errPipeRead, errPipeWrite);
366 SetConsoleCP(currentCodepage);
367 return didFail;
368 }
369
testFile()370 static int testFile()
371 {
372 int didFail = 1;
373 HANDLE inFile = INVALID_HANDLE_VALUE;
374 HANDLE outFile = INVALID_HANDLE_VALUE;
375 HANDLE errFile = INVALID_HANDLE_VALUE;
376 try {
377 if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE ||
378 (outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE ||
379 (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) {
380 throw std::runtime_error("createFile failed!");
381 }
382 DWORD bytesWritten = 0;
383 char buffer[200];
384 char buffer2[200];
385
386 int length;
387 if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString,
388 -1, buffer, sizeof(buffer), nullptr,
389 nullptr)) == 0) {
390 throw std::runtime_error("WideCharToMultiByte failed!");
391 }
392 buffer[length - 1] = '\n';
393 if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) ||
394 bytesWritten == 0) {
395 throw std::runtime_error("WriteFile failed!");
396 }
397 if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
398 throw std::runtime_error("SetFilePointer failed!");
399 }
400
401 if (createProcess(inFile, outFile, errFile)) {
402 DWORD bytesRead = 0;
403 try {
404 DWORD status;
405 if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
406 WAIT_OBJECT_0) {
407 std::cerr.setf(std::ios::hex, std::ios::basefield);
408 std::cerr << "WaitForSingleObject returned unexpected status 0x"
409 << status << std::endl;
410 std::cerr.unsetf(std::ios::hex);
411 throw std::runtime_error("WaitForSingleObject failed!");
412 }
413 if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) ==
414 INVALID_SET_FILE_POINTER) {
415 throw std::runtime_error("SetFilePointer#1 failed!");
416 }
417 if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) ||
418 bytesRead == 0) {
419 throw std::runtime_error("ReadFile#1 failed!");
420 }
421 buffer[bytesRead] = 0;
422 if (memcmp(buffer, encodedTestString.c_str(),
423 encodedTestString.size()) == 0 &&
424 memcmp(buffer + encodedTestString.size() + 1,
425 encodedInputTestString.c_str(),
426 encodedInputTestString.size()) == 0) {
427 bytesRead = 0;
428 if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) ==
429 INVALID_SET_FILE_POINTER) {
430 throw std::runtime_error("SetFilePointer#2 failed!");
431 }
432
433 if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead,
434 nullptr) ||
435 bytesRead == 0) {
436 throw std::runtime_error("ReadFile#2 failed!");
437 }
438 buffer2[bytesRead] = 0;
439 didFail = encodedTestString.compare(0, std::string::npos, buffer2,
440 encodedTestString.size()) == 0
441 ? 0
442 : 1;
443 }
444 if (didFail != 0) {
445 std::cerr << "File's output didn't match expected output!"
446 << std::endl;
447 dumpBuffers<char>(encodedTestString.c_str(), buffer,
448 encodedTestString.size());
449 dumpBuffers<char>(encodedInputTestString.c_str(),
450 buffer + encodedTestString.size() + 1,
451 encodedInputTestString.size());
452 dumpBuffers<char>(encodedTestString.c_str(), buffer2,
453 encodedTestString.size());
454 }
455 } catch (const std::runtime_error& ex) {
456 DWORD lastError = GetLastError();
457 std::cerr << "In function testFile, line " << __LINE__ << ": "
458 << ex.what() << std::endl;
459 displayError(lastError);
460 }
461 finishProcess(didFail == 0);
462 }
463 } catch (const std::runtime_error& ex) {
464 DWORD lastError = GetLastError();
465 std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what()
466 << std::endl;
467 displayError(lastError);
468 }
469 finishFile(inFile);
470 finishFile(outFile);
471 finishFile(errFile);
472 return didFail;
473 }
474
475 # ifndef _WIN32_WINNT_VISTA
476 # define _WIN32_WINNT_VISTA 0x0600
477 # endif
478
testConsole()479 static int testConsole()
480 {
481 int didFail = 1;
482 HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE);
483 HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE);
484 HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE);
485 HANDLE hIn = parentIn;
486 HANDLE hOut = parentOut;
487 DWORD consoleMode;
488 bool newConsole = false;
489 bool forceNewConsole = false;
490 bool restoreConsole = false;
491 LPCWSTR TestFaceName = L"Lucida Console";
492 const DWORD TestFontFamily = 0x00000036;
493 const DWORD TestFontSize = 0x000c0000;
494 HKEY hConsoleKey;
495 WCHAR FaceName[200];
496 FaceName[0] = 0;
497 DWORD FaceNameSize = sizeof(FaceName);
498 DWORD FontFamily = TestFontFamily;
499 DWORD FontSize = TestFontSize;
500 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
501 # pragma warning(push)
502 # ifdef __INTEL_COMPILER
503 # pragma warning(disable : 1478)
504 # elif defined __clang__
505 # pragma clang diagnostic push
506 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
507 # else
508 # pragma warning(disable : 4996)
509 # endif
510 # endif
511 const bool isVistaOrGreater =
512 LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA);
513 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
514 # ifdef __clang__
515 # pragma clang diagnostic pop
516 # else
517 # pragma warning(pop)
518 # endif
519 # endif
520 if (!isVistaOrGreater) {
521 if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE,
522 &hConsoleKey) == ERROR_SUCCESS) {
523 DWORD dwordSize = sizeof(DWORD);
524 if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr,
525 (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
526 if (FontFamily != TestFontFamily) {
527 RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr,
528 (LPBYTE)FaceName, &FaceNameSize);
529 RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr,
530 (LPBYTE)&FontSize, &dwordSize);
531
532 RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
533 (BYTE*)&TestFontFamily, sizeof(TestFontFamily));
534 RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ,
535 (BYTE*)TestFaceName,
536 (DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR)));
537 RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD,
538 (BYTE*)&TestFontSize, sizeof(TestFontSize));
539
540 restoreConsole = true;
541 forceNewConsole = true;
542 }
543 } else {
544 std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl;
545 }
546 RegCloseKey(hConsoleKey);
547 } else {
548 std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!"
549 << std::endl;
550 }
551 }
552 if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) {
553 // Not a real console, let's create new one.
554 FreeConsole();
555 if (!AllocConsole()) {
556 std::cerr << "AllocConsole failed!" << std::endl;
557 return didFail;
558 }
559 SECURITY_ATTRIBUTES securityAttributes;
560 securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
561 securityAttributes.bInheritHandle = TRUE;
562 securityAttributes.lpSecurityDescriptor = nullptr;
563 hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
564 FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
565 OPEN_EXISTING, 0, nullptr);
566 if (hIn == INVALID_HANDLE_VALUE) {
567 DWORD lastError = GetLastError();
568 std::cerr << "CreateFile(CONIN$)" << std::endl;
569 displayError(lastError);
570 }
571 hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
572 FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
573 OPEN_EXISTING, 0, nullptr);
574 if (hOut == INVALID_HANDLE_VALUE) {
575 DWORD lastError = GetLastError();
576 std::cerr << "CreateFile(CONOUT$)" << std::endl;
577 displayError(lastError);
578 }
579 SetStdHandle(STD_INPUT_HANDLE, hIn);
580 SetStdHandle(STD_OUTPUT_HANDLE, hOut);
581 SetStdHandle(STD_ERROR_HANDLE, hOut);
582 newConsole = true;
583 }
584
585 # if _WIN32_WINNT >= _WIN32_WINNT_VISTA
586 if (isVistaOrGreater) {
587 CONSOLE_FONT_INFOEX consoleFont;
588 memset(&consoleFont, 0, sizeof(consoleFont));
589 consoleFont.cbSize = sizeof(consoleFont);
590 HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
591 typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)(
592 HANDLE hConsoleOutput, BOOL bMaximumWindow,
593 PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
594 typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)(
595 HANDLE hConsoleOutput, BOOL bMaximumWindow,
596 PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
597 GetCurrentConsoleFontExFunc getConsoleFont =
598 (GetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
599 "GetCurrentConsoleFontEx");
600 SetCurrentConsoleFontExFunc setConsoleFont =
601 (SetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
602 "SetCurrentConsoleFontEx");
603 if (getConsoleFont(hOut, FALSE, &consoleFont)) {
604 if (consoleFont.FontFamily != TestFontFamily) {
605 consoleFont.FontFamily = TestFontFamily;
606 wcscpy(consoleFont.FaceName, TestFaceName);
607 if (!setConsoleFont(hOut, FALSE, &consoleFont)) {
608 std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl;
609 }
610 }
611 } else {
612 std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl;
613 }
614 } else {
615 # endif
616 if (restoreConsole &&
617 RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE,
618 &hConsoleKey) == ERROR_SUCCESS) {
619 RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
620 (BYTE*)&FontFamily, sizeof(FontFamily));
621 if (FaceName[0] != 0) {
622 RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName,
623 FaceNameSize);
624 } else {
625 RegDeleteValueW(hConsoleKey, L"FaceName");
626 }
627 RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize,
628 sizeof(FontSize));
629 RegCloseKey(hConsoleKey);
630 }
631 # if _WIN32_WINNT >= _WIN32_WINNT_VISTA
632 }
633 # endif
634
635 if (createProcess(nullptr, nullptr, nullptr)) {
636 try {
637 DWORD status;
638 if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) !=
639 WAIT_OBJECT_0) {
640 std::cerr.setf(std::ios::hex, std::ios::basefield);
641 std::cerr << "WaitForSingleObject returned unexpected status 0x"
642 << status << std::endl;
643 std::cerr.unsetf(std::ios::hex);
644 throw std::runtime_error("WaitForSingleObject#1 failed!");
645 }
646 INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) /
647 sizeof(UnicodeInputTestString[0])) *
648 2];
649 memset(&inputBuffer, 0, sizeof(inputBuffer));
650 unsigned int i;
651 for (i = 0; i < (sizeof(UnicodeInputTestString) /
652 sizeof(UnicodeInputTestString[0]) -
653 1);
654 i++) {
655 writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]);
656 }
657 writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN);
658 DWORD eventsWritten = 0;
659 // We need to wait a bit before writing to console so child process have
660 // started waiting for input on stdin.
661 Sleep(300);
662 if (!WriteConsoleInputW(hIn, inputBuffer,
663 sizeof(inputBuffer) / sizeof(inputBuffer[0]),
664 &eventsWritten) ||
665 eventsWritten == 0) {
666 throw std::runtime_error("WriteConsoleInput failed!");
667 }
668 if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
669 WAIT_OBJECT_0) {
670 std::cerr.setf(std::ios::hex, std::ios::basefield);
671 std::cerr << "WaitForSingleObject returned unexpected status 0x"
672 << status << std::endl;
673 std::cerr.unsetf(std::ios::hex);
674 throw std::runtime_error("WaitForSingleObject#2 failed!");
675 }
676 CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
677 if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) {
678 throw std::runtime_error("GetConsoleScreenBufferInfo failed!");
679 }
680
681 COORD coord;
682 DWORD charsRead = 0;
683 coord.X = 0;
684 coord.Y = screenBufferInfo.dwCursorPosition.Y - 4;
685 WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4];
686 if (!ReadConsoleOutputCharacterW(hOut, outputBuffer,
687 screenBufferInfo.dwSize.X * 4, coord,
688 &charsRead) ||
689 charsRead == 0) {
690 delete[] outputBuffer;
691 throw std::runtime_error("ReadConsoleOutputCharacter failed!");
692 }
693 std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString);
694 std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' ');
695 std::wstring wideInputTestString =
696 kwsys::Encoding::ToWide(encodedInputTestString);
697 if (memcmp(outputBuffer, wideTestString.c_str(),
698 wideTestString.size() * sizeof(wchar_t)) == 0 &&
699 memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1,
700 wideTestString.c_str(),
701 wideTestString.size() * sizeof(wchar_t)) == 0 &&
702 memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2,
703 UnicodeInputTestString,
704 sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 &&
705 memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3,
706 wideInputTestString.c_str(),
707 (wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) {
708 didFail = 0;
709 } else {
710 std::cerr << "Console's output didn't match expected output!"
711 << std::endl;
712 dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer,
713 wideTestString.size());
714 dumpBuffers<wchar_t>(wideTestString.c_str(),
715 outputBuffer + screenBufferInfo.dwSize.X * 1,
716 wideTestString.size());
717 dumpBuffers<wchar_t>(
718 UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2,
719 (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR));
720 dumpBuffers<wchar_t>(wideInputTestString.c_str(),
721 outputBuffer + screenBufferInfo.dwSize.X * 3,
722 wideInputTestString.size() - 1);
723 }
724 delete[] outputBuffer;
725 } catch (const std::runtime_error& ex) {
726 DWORD lastError = GetLastError();
727 std::cerr << "In function testConsole, line " << __LINE__ << ": "
728 << ex.what() << std::endl;
729 displayError(lastError);
730 }
731 finishProcess(didFail == 0);
732 }
733 if (newConsole) {
734 SetStdHandle(STD_INPUT_HANDLE, parentIn);
735 SetStdHandle(STD_OUTPUT_HANDLE, parentOut);
736 SetStdHandle(STD_ERROR_HANDLE, parentErr);
737 CloseHandle(hIn);
738 CloseHandle(hOut);
739 FreeConsole();
740 }
741 return didFail;
742 }
743
744 #endif
745
testConsoleBuf(int,char * [])746 int testConsoleBuf(int, char* [])
747 {
748 int ret = 0;
749
750 #if defined(_WIN32)
751 beforeInputEvent = CreateEventW(nullptr,
752 FALSE, // auto-reset event
753 FALSE, // initial state is nonsignaled
754 BeforeInputEventName); // object name
755 if (!beforeInputEvent) {
756 std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl;
757 return 1;
758 }
759
760 afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName);
761 if (!afterOutputEvent) {
762 std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl;
763 return 1;
764 }
765
766 encodedTestString = kwsys::Encoding::ToNarrow(std::wstring(
767 UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1));
768 encodedInputTestString = kwsys::Encoding::ToNarrow(
769 std::wstring(UnicodeInputTestString,
770 sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1));
771 encodedInputTestString += "\n";
772
773 ret |= testPipe();
774 ret |= testFile();
775 ret |= testConsole();
776
777 CloseHandle(beforeInputEvent);
778 CloseHandle(afterOutputEvent);
779 #endif
780
781 return ret;
782 }
783