KWSys 2016-09-14 (e736efa1)
Code extracted from: http://public.kitware.com/KWSys.git at commit e736efa13ad42a4245b95774d114720ad0877c5b (master). Upstream Shortlog ----------------- Brad King (1): e736efa1 ConsoleBuf: Always compile test source for host Windows version Dāvis Mosāns (1): 669e3a06 ConsoleBuf: Use a custom std::streambuf for console output on Windows
This commit is contained in:
parent
fcc532470a
commit
b80d613632
|
@ -123,6 +123,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||||
SET(KWSYS_USE_FStream 1)
|
SET(KWSYS_USE_FStream 1)
|
||||||
SET(KWSYS_USE_String 1)
|
SET(KWSYS_USE_String 1)
|
||||||
SET(KWSYS_USE_SystemInformation 1)
|
SET(KWSYS_USE_SystemInformation 1)
|
||||||
|
SET(KWSYS_USE_ConsoleBuf 1)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# Enforce component dependencies.
|
# Enforce component dependencies.
|
||||||
|
@ -154,6 +155,9 @@ ENDIF()
|
||||||
IF(KWSYS_USE_FStream)
|
IF(KWSYS_USE_FStream)
|
||||||
SET(KWSYS_USE_Encoding 1)
|
SET(KWSYS_USE_Encoding 1)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
IF(KWSYS_USE_ConsoleBuf)
|
||||||
|
SET(KWSYS_USE_Encoding 1)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
# Setup the large file support default.
|
# Setup the large file support default.
|
||||||
IF(KWSYS_LFS_DISABLE)
|
IF(KWSYS_LFS_DISABLE)
|
||||||
|
@ -673,7 +677,7 @@ SET(KWSYS_HXX_FILES Configure String
|
||||||
# Add selected C++ classes.
|
# Add selected C++ classes.
|
||||||
SET(cppclasses
|
SET(cppclasses
|
||||||
Directory DynamicLoader Encoding Glob RegularExpression SystemTools
|
Directory DynamicLoader Encoding Glob RegularExpression SystemTools
|
||||||
CommandLineArguments IOStream FStream SystemInformation
|
CommandLineArguments IOStream FStream SystemInformation ConsoleBuf
|
||||||
)
|
)
|
||||||
FOREACH(cpp ${cppclasses})
|
FOREACH(cpp ${cppclasses})
|
||||||
IF(KWSYS_USE_${cpp})
|
IF(KWSYS_USE_${cpp})
|
||||||
|
@ -926,6 +930,20 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||||
testFStream
|
testFStream
|
||||||
)
|
)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
IF(KWSYS_USE_ConsoleBuf)
|
||||||
|
ADD_EXECUTABLE(testConsoleBufChild testConsoleBufChild.cxx)
|
||||||
|
SET_PROPERTY(TARGET testConsoleBufChild PROPERTY LABELS ${KWSYS_LABELS_EXE})
|
||||||
|
TARGET_LINK_LIBRARIES(testConsoleBufChild ${KWSYS_NAMESPACE})
|
||||||
|
SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
|
||||||
|
testConsoleBuf
|
||||||
|
)
|
||||||
|
IF("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" AND
|
||||||
|
NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0")
|
||||||
|
set_property(SOURCE testConsoleBuf.cxx testConsoleBufChild.cxx PROPERTY COMPILE_FLAGS /utf-8)
|
||||||
|
ENDIF()
|
||||||
|
SET_PROPERTY(SOURCE testConsoleBuf.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
||||||
|
KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
|
||||||
|
ENDIF()
|
||||||
IF(KWSYS_USE_SystemInformation)
|
IF(KWSYS_USE_SystemInformation)
|
||||||
SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation)
|
SET(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
|
@ -0,0 +1,348 @@
|
||||||
|
/*============================================================================
|
||||||
|
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.
|
||||||
|
============================================================================*/
|
||||||
|
#ifndef @KWSYS_NAMESPACE@_ConsoleBuf_hxx
|
||||||
|
#define @KWSYS_NAMESPACE@_ConsoleBuf_hxx
|
||||||
|
|
||||||
|
#include <@KWSYS_NAMESPACE@/Configure.hxx>
|
||||||
|
#include <@KWSYS_NAMESPACE@/Encoding.hxx>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# include <windows.h>
|
||||||
|
# if __cplusplus >= 201103L
|
||||||
|
# include <system_error>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace @KWSYS_NAMESPACE@
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
template<class CharT, class Traits = std::char_traits<CharT> >
|
||||||
|
class @KWSYS_NAMESPACE@_EXPORT BasicConsoleBuf :
|
||||||
|
public std::basic_streambuf<CharT, Traits> {
|
||||||
|
public:
|
||||||
|
typedef typename Traits::int_type int_type;
|
||||||
|
typedef typename Traits::char_type char_type;
|
||||||
|
|
||||||
|
class Manager {
|
||||||
|
public:
|
||||||
|
Manager(std::basic_ios<CharT, Traits> &ios, const bool err = false)
|
||||||
|
: m_consolebuf(0)
|
||||||
|
{
|
||||||
|
m_ios = &ios;
|
||||||
|
try {
|
||||||
|
m_consolebuf = new BasicConsoleBuf<CharT, Traits>(err);
|
||||||
|
m_streambuf = m_ios->rdbuf(m_consolebuf);
|
||||||
|
} catch (const std::runtime_error& ex) {
|
||||||
|
std::cerr << "Failed to create ConsoleBuf!" << std::endl
|
||||||
|
<< ex.what() << std::endl;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
~Manager()
|
||||||
|
{
|
||||||
|
if (m_consolebuf) {
|
||||||
|
delete m_consolebuf;
|
||||||
|
m_ios->rdbuf(m_streambuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::basic_ios<CharT, Traits> *m_ios;
|
||||||
|
std::basic_streambuf<CharT, Traits> *m_streambuf;
|
||||||
|
BasicConsoleBuf<CharT, Traits> *m_consolebuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
BasicConsoleBuf(const bool err = false) :
|
||||||
|
flush_on_newline(true),
|
||||||
|
input_pipe_codepage(0),
|
||||||
|
output_pipe_codepage(0),
|
||||||
|
input_file_codepage(CP_UTF8),
|
||||||
|
output_file_codepage(CP_UTF8),
|
||||||
|
m_consolesCodepage(0)
|
||||||
|
{
|
||||||
|
m_hInput = ::GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
checkHandle(true, "STD_INPUT_HANDLE");
|
||||||
|
if (!setActiveInputCodepage()) {
|
||||||
|
throw std::runtime_error("setActiveInputCodepage failed!");
|
||||||
|
}
|
||||||
|
m_hOutput = err ? ::GetStdHandle(STD_ERROR_HANDLE) :
|
||||||
|
::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
checkHandle(false, err ? "STD_ERROR_HANDLE" : "STD_OUTPUT_HANDLE");
|
||||||
|
if (!setActiveOutputCodepage()) {
|
||||||
|
throw std::runtime_error("setActiveOutputCodepage failed!");
|
||||||
|
}
|
||||||
|
_setg();
|
||||||
|
_setp();
|
||||||
|
}
|
||||||
|
|
||||||
|
~BasicConsoleBuf() throw()
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool activateCodepageChange()
|
||||||
|
{
|
||||||
|
return setActiveInputCodepage() && setActiveOutputCodepage();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int sync() {
|
||||||
|
bool success = true;
|
||||||
|
if (m_hInput && m_isConsoleInput &&
|
||||||
|
::FlushConsoleInputBuffer(m_hInput) == 0) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (m_hOutput && !m_obuffer.empty()) {
|
||||||
|
const std::wstring wbuffer = getBuffer(m_obuffer);
|
||||||
|
if (m_isConsoleOutput) {
|
||||||
|
DWORD charsWritten;
|
||||||
|
success = ::WriteConsoleW(m_hOutput, wbuffer.c_str(),
|
||||||
|
(DWORD)wbuffer.size(), &charsWritten,
|
||||||
|
NULL) == 0 ? false : true;
|
||||||
|
} else {
|
||||||
|
DWORD bytesWritten;
|
||||||
|
std::string buffer;
|
||||||
|
success = encodeOutputBuffer(wbuffer, buffer);
|
||||||
|
if (success) {
|
||||||
|
success = ::WriteFile(m_hOutput, buffer.c_str(),
|
||||||
|
(DWORD)buffer.size(), &bytesWritten,
|
||||||
|
NULL) == 0 ? false : true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_ibuffer.clear();
|
||||||
|
m_obuffer.clear();
|
||||||
|
_setg();
|
||||||
|
_setp();
|
||||||
|
return success ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int_type underflow() {
|
||||||
|
if (this->gptr() >= this->egptr()) {
|
||||||
|
if (!m_hInput) {
|
||||||
|
_setg(true);
|
||||||
|
return Traits::eof();
|
||||||
|
}
|
||||||
|
if (m_isConsoleInput) {
|
||||||
|
wchar_t wbuffer[128];
|
||||||
|
DWORD charsRead;
|
||||||
|
if (::ReadConsoleW(m_hInput, wbuffer, (sizeof(wbuffer) / sizeof(wbuffer[0])) - 1,
|
||||||
|
&charsRead, NULL) == 0 || charsRead == 0) {
|
||||||
|
_setg(true);
|
||||||
|
return Traits::eof();
|
||||||
|
}
|
||||||
|
wbuffer[charsRead] = L'\0';
|
||||||
|
setBuffer(wbuffer, m_ibuffer);
|
||||||
|
} else {
|
||||||
|
std::wstring totalBuffer;
|
||||||
|
std::wstring wbuffer;
|
||||||
|
char buffer[128];
|
||||||
|
DWORD bytesRead;
|
||||||
|
while (::ReadFile(m_hInput, buffer, (sizeof(buffer) / sizeof(buffer[0])) - 1,
|
||||||
|
&bytesRead, NULL) == 0) {
|
||||||
|
if (::GetLastError() == ERROR_MORE_DATA) {
|
||||||
|
buffer[bytesRead] = '\0';
|
||||||
|
if (decodeInputBuffer(buffer, wbuffer)) {
|
||||||
|
totalBuffer += wbuffer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_setg(true);
|
||||||
|
return Traits::eof();
|
||||||
|
}
|
||||||
|
buffer[bytesRead] = '\0';
|
||||||
|
if (!decodeInputBuffer(buffer, wbuffer)) {
|
||||||
|
_setg(true);
|
||||||
|
return Traits::eof();
|
||||||
|
}
|
||||||
|
totalBuffer += wbuffer;
|
||||||
|
setBuffer(totalBuffer, m_ibuffer);
|
||||||
|
}
|
||||||
|
_setg();
|
||||||
|
}
|
||||||
|
return Traits::to_int_type(*this->gptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int_type overflow(int_type ch = Traits::eof()) {
|
||||||
|
if (!Traits::eq_int_type(ch, Traits::eof())) {
|
||||||
|
char_type chr = Traits::to_char_type(ch);
|
||||||
|
m_obuffer += chr;
|
||||||
|
if ((flush_on_newline && Traits::eq(chr, '\n')) ||
|
||||||
|
Traits::eq_int_type(ch, 0x00)) {
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
sync();
|
||||||
|
return Traits::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool flush_on_newline;
|
||||||
|
UINT input_pipe_codepage;
|
||||||
|
UINT output_pipe_codepage;
|
||||||
|
UINT input_file_codepage;
|
||||||
|
UINT output_file_codepage;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HANDLE m_hInput;
|
||||||
|
HANDLE m_hOutput;
|
||||||
|
std::basic_string<char_type> m_ibuffer;
|
||||||
|
std::basic_string<char_type> m_obuffer;
|
||||||
|
bool m_isConsoleInput;
|
||||||
|
bool m_isConsoleOutput;
|
||||||
|
UINT m_activeInputCodepage;
|
||||||
|
UINT m_activeOutputCodepage;
|
||||||
|
UINT m_consolesCodepage;
|
||||||
|
void checkHandle(bool input, std::string handleName) {
|
||||||
|
if ((input && m_hInput == INVALID_HANDLE_VALUE) ||
|
||||||
|
(!input && m_hOutput == INVALID_HANDLE_VALUE)) {
|
||||||
|
std::string errmsg = "GetStdHandle(" + handleName +
|
||||||
|
") returned INVALID_HANDLE_VALUE";
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
throw std::system_error(::GetLastError(),
|
||||||
|
std::system_category(), errmsg);
|
||||||
|
#else
|
||||||
|
throw std::runtime_error(errmsg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UINT getConsolesCodepage() {
|
||||||
|
if (!m_consolesCodepage) {
|
||||||
|
m_consolesCodepage = GetConsoleCP();
|
||||||
|
if (!m_consolesCodepage) {
|
||||||
|
m_consolesCodepage = GetACP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_consolesCodepage;
|
||||||
|
}
|
||||||
|
bool setActiveInputCodepage() {
|
||||||
|
m_isConsoleInput = false;
|
||||||
|
switch (GetFileType(m_hInput)) {
|
||||||
|
case FILE_TYPE_DISK:
|
||||||
|
m_activeInputCodepage = input_file_codepage;
|
||||||
|
break;
|
||||||
|
case FILE_TYPE_CHAR:
|
||||||
|
m_isConsoleInput = true;
|
||||||
|
break;
|
||||||
|
case FILE_TYPE_PIPE:
|
||||||
|
m_activeInputCodepage = input_pipe_codepage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_isConsoleInput && m_activeInputCodepage == 0) {
|
||||||
|
m_activeInputCodepage = getConsolesCodepage();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool setActiveOutputCodepage() {
|
||||||
|
m_isConsoleOutput = false;
|
||||||
|
switch (GetFileType(m_hOutput)) {
|
||||||
|
case FILE_TYPE_DISK:
|
||||||
|
m_activeOutputCodepage = output_file_codepage;
|
||||||
|
break;
|
||||||
|
case FILE_TYPE_CHAR:
|
||||||
|
m_isConsoleOutput = true;
|
||||||
|
break;
|
||||||
|
case FILE_TYPE_PIPE:
|
||||||
|
m_activeOutputCodepage = output_pipe_codepage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_isConsoleOutput && m_activeOutputCodepage == 0) {
|
||||||
|
m_activeOutputCodepage = getConsolesCodepage();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void _setg(bool empty = false) {
|
||||||
|
if (!empty) {
|
||||||
|
this->setg((char_type *)m_ibuffer.data(),
|
||||||
|
(char_type *)m_ibuffer.data(),
|
||||||
|
(char_type *)m_ibuffer.data() + m_ibuffer.size());
|
||||||
|
} else {
|
||||||
|
this->setg((char_type *)m_ibuffer.data(),
|
||||||
|
(char_type *)m_ibuffer.data() + m_ibuffer.size(),
|
||||||
|
(char_type *)m_ibuffer.data() + m_ibuffer.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void _setp() {
|
||||||
|
this->setp((char_type *)m_obuffer.data(),
|
||||||
|
(char_type *)m_obuffer.data() + m_obuffer.size());
|
||||||
|
}
|
||||||
|
bool encodeOutputBuffer(const std::wstring wbuffer,
|
||||||
|
std::string &buffer) {
|
||||||
|
const int length = WideCharToMultiByte(m_activeOutputCodepage, 0,
|
||||||
|
wbuffer.c_str(),
|
||||||
|
(int)wbuffer.size(), NULL, 0,
|
||||||
|
NULL, NULL);
|
||||||
|
char *buf = new char[length + 1];
|
||||||
|
const bool success = WideCharToMultiByte(m_activeOutputCodepage, 0,
|
||||||
|
wbuffer.c_str(),
|
||||||
|
(int)wbuffer.size(), buf,
|
||||||
|
length, NULL, NULL) > 0
|
||||||
|
? true : false;
|
||||||
|
buf[length] = '\0';
|
||||||
|
buffer = buf;
|
||||||
|
delete[] buf;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
bool decodeInputBuffer(const char *buffer, std::wstring &wbuffer) {
|
||||||
|
int actualCodepage = m_activeInputCodepage;
|
||||||
|
const char BOM_UTF8[] = { char(0xEF), char(0xBB), char(0xBF) };
|
||||||
|
if (std::memcmp(buffer, BOM_UTF8, sizeof(BOM_UTF8)) == 0) {
|
||||||
|
// PowerShell uses UTF-8 with BOM for pipes
|
||||||
|
actualCodepage = CP_UTF8;
|
||||||
|
buffer += sizeof(BOM_UTF8);
|
||||||
|
}
|
||||||
|
const int wlength = MultiByteToWideChar(actualCodepage, 0, buffer,
|
||||||
|
-1, NULL, 0);
|
||||||
|
wchar_t *wbuf = new wchar_t[wlength];
|
||||||
|
const bool success = MultiByteToWideChar(actualCodepage, 0, buffer,
|
||||||
|
-1, wbuf, wlength) > 0
|
||||||
|
? true : false;
|
||||||
|
wbuffer = wbuf;
|
||||||
|
delete[] wbuf;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
std::wstring getBuffer(const std::basic_string<char> buffer) {
|
||||||
|
return Encoding::ToWide(buffer);
|
||||||
|
}
|
||||||
|
std::wstring getBuffer(const std::basic_string<wchar_t> buffer) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
void setBuffer(const std::wstring wbuffer,
|
||||||
|
std::basic_string<char> &target) {
|
||||||
|
target = Encoding::ToNarrow(wbuffer);
|
||||||
|
}
|
||||||
|
void setBuffer(const std::wstring wbuffer,
|
||||||
|
std::basic_string<wchar_t> &target) {
|
||||||
|
target = wbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // BasicConsoleBuf class
|
||||||
|
|
||||||
|
typedef BasicConsoleBuf<char> ConsoleBuf;
|
||||||
|
typedef BasicConsoleBuf<wchar_t> WConsoleBuf;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
} // KWSYS_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*============================================================================
|
||||||
|
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.
|
||||||
|
============================================================================*/
|
||||||
|
#ifndef testConsoleBuf_hxx
|
||||||
|
#define testConsoleBuf_hxx
|
||||||
|
|
||||||
|
static const wchar_t cmdConsoleBufChild[] = L"testConsoleBufChild";
|
||||||
|
|
||||||
|
static const wchar_t SyncEventName[] = L"SyncEvent";
|
||||||
|
|
||||||
|
// यूनिकोड είναι здорово!
|
||||||
|
static const wchar_t UnicodeTestString[] = L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 "
|
||||||
|
L"\u03B5\u03AF\u03BD\u03B1\u03B9 "
|
||||||
|
L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!";
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*============================================================================
|
||||||
|
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"
|
||||||
|
|
||||||
|
#include KWSYS_HEADER(ConsoleBuf.hxx)
|
||||||
|
#include KWSYS_HEADER(Encoding.hxx)
|
||||||
|
|
||||||
|
// Work-around CMake dependency scanning limitation. This must
|
||||||
|
// duplicate the above list of headers.
|
||||||
|
#if 0
|
||||||
|
# include "ConsoleBuf.hxx.in"
|
||||||
|
# include "Encoding.hxx.in"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "testConsoleBuf.hxx"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int main(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
kwsys::ConsoleBuf::Manager out(std::cout);
|
||||||
|
kwsys::ConsoleBuf::Manager err(std::cerr, true);
|
||||||
|
kwsys::ConsoleBuf::Manager in(std::cin);
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
std::cout << argv[1] << std::endl;
|
||||||
|
std::cerr << argv[1] << std::endl;
|
||||||
|
} else {
|
||||||
|
std::string str = kwsys::Encoding::ToNarrow(UnicodeTestString);
|
||||||
|
std::cout << str << std::endl;
|
||||||
|
std::cerr << str << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string input;
|
||||||
|
HANDLE syncEvent = OpenEventW(EVENT_MODIFY_STATE, FALSE, SyncEventName);
|
||||||
|
if (syncEvent) {
|
||||||
|
SetEvent(syncEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cin >> input;
|
||||||
|
std::cout << input << std::endl;
|
||||||
|
if (syncEvent) {
|
||||||
|
SetEvent(syncEvent);
|
||||||
|
CloseHandle(syncEvent);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static_cast<void>(argc);
|
||||||
|
static_cast<void>(argv);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue