123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053 |
- //---------------------------------------------------------------------------
- #include <stdexcept>
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- #include <io.h>
- #include <fcntl.h>
- #include "Console.h"
- #define MAX_ATTEMPTS 10
- //---------------------------------------------------------------------------
- #define LENOF(x) ( (sizeof((x))) / (sizeof(*(x))))
- //---------------------------------------------------------------------------
- using namespace std;
- HANDLE ConsoleInput = NULL;
- HANDLE ConsoleOutput = NULL;
- HANDLE ConsoleStandardOutput = NULL;
- HANDLE ConsoleErrorOutput = NULL;
- TConsoleCommStruct::TInitEvent::STDINOUT OutputFormat;
- TConsoleCommStruct::TInitEvent::STDINOUT InputFormat;
- HANDLE Child = NULL;
- HANDLE CancelEvent = NULL;
- HANDLE InputTimerEvent = NULL;
- bool SupportsUtf8ConsoleOutput = false;
- unsigned int OutputType = FILE_TYPE_UNKNOWN;
- unsigned int InputType = FILE_TYPE_UNKNOWN;
- enum { RESULT_GLOBAL_ERROR = 1, RESULT_INIT_ERROR = 2, RESULT_PROCESSING_ERROR = 3,
- RESULT_UNKNOWN_ERROR = 4 };
- const wchar_t* CONSOLE_CHILD_PARAM = L"consolechild";
- //---------------------------------------------------------------------------
- inline TConsoleCommStruct* GetCommStruct(HANDLE FileMapping)
- {
- TConsoleCommStruct* Result;
- Result = static_cast<TConsoleCommStruct*>(MapViewOfFile(FileMapping,
- FILE_MAP_ALL_ACCESS, 0, 0, 0));
- if (Result == NULL)
- {
- throw runtime_error("Cannot open mapping object.");
- }
- return Result;
- }
- //---------------------------------------------------------------------------
- inline void FreeCommStruct(TConsoleCommStruct* CommStruct)
- {
- UnmapViewOfFile(CommStruct);
- }
- //---------------------------------------------------------------------------
- void InitializeConsole(wchar_t* InstanceName, HANDLE& RequestEvent, HANDLE& ResponseEvent,
- HANDLE& CancelEvent, HANDLE& FileMapping, HANDLE& Job)
- {
- unsigned int Process = GetCurrentProcessId();
- int Attempts = 0;
- wchar_t Name[MAX_PATH];
- bool UniqEvent;
- do
- {
- if (Attempts > MAX_ATTEMPTS)
- {
- throw runtime_error("Cannot find unique name for event object.");
- }
- int InstanceNumber;
- #ifdef CONSOLE_TEST
- InstanceNumber = 1;
- #else
- InstanceNumber = random(1000);
- #endif
- swprintf(InstanceName, L"_%u_%d", Process, InstanceNumber);
- swprintf(Name, L"%s%s", CONSOLE_EVENT_REQUEST, InstanceName);
- HANDLE EventHandle = OpenEvent(EVENT_ALL_ACCESS, false, Name);
- UniqEvent = (EventHandle == NULL);
- if (!UniqEvent)
- {
- CloseHandle(EventHandle);
- }
- Attempts++;
- }
- while (!UniqEvent);
- RequestEvent = CreateEvent(NULL, false, false, Name);
- if (RequestEvent == NULL)
- {
- throw runtime_error("Cannot create request event object.");
- }
- swprintf(Name, L"%s%s", CONSOLE_EVENT_RESPONSE, InstanceName);
- ResponseEvent = CreateEvent(NULL, false, false, Name);
- if (ResponseEvent == NULL)
- {
- throw runtime_error("Cannot create response event object.");
- }
- swprintf(Name, L"%s%s", CONSOLE_EVENT_CANCEL, InstanceName);
- CancelEvent = CreateEvent(NULL, false, false, Name);
- if (CancelEvent == NULL)
- {
- throw runtime_error("Cannot create cancel event object.");
- }
- swprintf(Name, L"%s%s", CONSOLE_MAPPING, InstanceName);
- FileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE,
- 0, sizeof(TConsoleCommStruct), Name);
- if (FileMapping == NULL)
- {
- throw runtime_error("Cannot create mapping object.");
- }
- swprintf(Name, L"%s%s", CONSOLE_JOB, InstanceName);
- Job = CreateJobObject(NULL, Name);
- if (Job == NULL)
- {
- throw runtime_error("Cannot create job object.");
- }
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimitInformation;
- memset(&ExtendedLimitInformation, 0, sizeof(ExtendedLimitInformation));
- ExtendedLimitInformation.BasicLimitInformation.LimitFlags =
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- if (SetInformationJobObject(Job, JobObjectExtendedLimitInformation,
- &ExtendedLimitInformation, sizeof(ExtendedLimitInformation)) == 0)
- {
- CloseHandle(Job);
- Job = NULL;
- }
- TConsoleCommStruct* CommStruct = GetCommStruct(FileMapping);
- CommStruct->Size = sizeof(TConsoleCommStruct);
- CommStruct->Version = TConsoleCommStruct::CurrentVersion;
- CommStruct->Event = TConsoleCommStruct::NONE;
- FreeCommStruct(CommStruct);
- }
- //---------------------------------------------------------------------------
- // duplicated in Common.cpp
- bool __fastcall CutToken(const wchar_t*& Str, wchar_t* Token)
- {
- bool Result;
- // inspired by Putty's sftp_getcmd() from PSFTP.C
- int Length = wcslen(Str);
- int Index = 0;
- while ((Index < Length) &&
- ((Str[Index] == L' ') || (Str[Index] == L'\t')))
- {
- Index++;
- }
- if (Index < Length)
- {
- bool Quoting = false;
- while (Index < Length)
- {
- if (!Quoting && ((Str[Index] == L' ') || (Str[Index] == L'\t')))
- {
- break;
- }
- else if ((Str[Index] == L'"') && (Index + 1 < Length) &&
- (Str[Index + 1] == L'"'))
- {
- Index += 2;
- *Token = L'"';
- Token++;
- }
- else if (Str[Index] == L'"')
- {
- Index++;
- Quoting = !Quoting;
- }
- else
- {
- *Token = Str[Index];
- Token++;
- Index++;
- }
- }
- if (Index < Length)
- {
- Index++;
- }
- Str += Index;
- Result = true;
- }
- else
- {
- Result = false;
- Str += Length;
- }
- *Token = L'\0';
- return Result;
- }
- //---------------------------------------------------------------------------
- char* WideStringToString(const wchar_t* Message)
- {
- char* Buffer;
- int Size = WideCharToMultiByte(CP_UTF8, 0, Message, -1, 0, 0, 0, 0);
- if (Size > 0)
- {
- Buffer = new char[(Size * 2) + 1];
- if (WideCharToMultiByte(CP_UTF8, 0, Message, -1, Buffer, Size, 0, 0) > 0)
- {
- Buffer[Size] = '\0';
- }
- else
- {
- delete[] Buffer;
- Buffer = NULL;
- }
- }
- return Buffer;
- }
- //---------------------------------------------------------------------------
- void GetProductVersion(wchar_t* ProductVersion)
- {
- wchar_t Buffer[MAX_PATH];
- DWORD ModuleNameLen = GetModuleFileName(NULL, Buffer, MAX_PATH);
- if ((ModuleNameLen == 0) || (ModuleNameLen == MAX_PATH))
- {
- throw runtime_error("Error retrieving executable name.");
- }
- ProductVersion[0] = '\0';
- unsigned long Handle;
- unsigned int Size = GetFileVersionInfoSize(Buffer, &Handle);
- if (Size > 0)
- {
- void * VersionInfo = new char[Size];
- VS_FIXEDFILEINFO* FixedFileInfo;
- unsigned int Length;
- if (GetFileVersionInfo(Buffer, Handle, Size, VersionInfo))
- {
- if (VerQueryValue(VersionInfo, L"\\", (void**)&FixedFileInfo, &Length))
- {
- int ProductMajor = HIWORD(FixedFileInfo->dwProductVersionMS);
- int ProductMinor = LOWORD(FixedFileInfo->dwProductVersionMS);
- int ProductBuild = HIWORD(FixedFileInfo->dwProductVersionLS);
- if ((ProductMajor >= 1) && (ProductMajor <= 99) &&
- (ProductMinor >= 0) && (ProductMinor <= 99) &&
- (ProductBuild >= 0) && (ProductBuild <= 99))
- {
- wsprintf(ProductVersion, L"%d.%d.%d", ProductMajor, ProductMinor, ProductBuild);
- }
- }
- }
- delete[] VersionInfo;
- }
- if (ProductVersion[0] == L'\0')
- {
- throw runtime_error("Error retrieving product version.");
- }
- }
- //---------------------------------------------------------------------------
- void InitializeChild(const wchar_t* CommandLine, const wchar_t* InstanceName, HANDLE& Child)
- {
- int SkipParam = 0;
- wchar_t ChildPath[MAX_PATH] = L"";
- size_t CommandLineLen = wcslen(CommandLine);
- wchar_t* Buffer = new wchar_t[(CommandLineLen > MAX_PATH ? CommandLineLen : MAX_PATH) + 1];
- int Count = 0;
- const wchar_t* P = CommandLine;
- while (CutToken(P, Buffer))
- {
- if ((wcschr(L"-/", Buffer[0]) != NULL) &&
- (wcsncmpi(Buffer + 1, CONSOLE_CHILD_PARAM, wcslen(CONSOLE_CHILD_PARAM)) == 0) &&
- (Buffer[wcslen(CONSOLE_CHILD_PARAM) + 1] == L'='))
- {
- SkipParam = Count;
- wcscpy(ChildPath, Buffer + 1 + wcslen(CONSOLE_CHILD_PARAM) + 1);
- }
- ++Count;
- }
- if (wcslen(ChildPath) == 0)
- {
- DWORD ModuleNameLen = GetModuleFileName(NULL, Buffer, MAX_PATH);
- if ((ModuleNameLen == 0) || (ModuleNameLen == MAX_PATH))
- {
- throw runtime_error("Error retrieving executable name.");
- }
- const wchar_t* LastDelimiter = wcsrchr(Buffer, L'\\');
- const wchar_t* AppFileName;
- if (LastDelimiter != NULL)
- {
- wcsncpy(ChildPath, Buffer, LastDelimiter - Buffer + 1);
- ChildPath[LastDelimiter - Buffer + 1] = L'\0';
- AppFileName = LastDelimiter + 1;
- }
- else
- {
- ChildPath[0] = L'\0';
- AppFileName = Buffer;
- }
- const wchar_t* ExtensionStart = wcsrchr(AppFileName, L'.');
- if (ExtensionStart != NULL)
- {
- wchar_t* End = ChildPath + wcslen(ChildPath);
- wcsncpy(End, AppFileName, ExtensionStart - AppFileName);
- *(End + (ExtensionStart - AppFileName)) = L'\0';
- }
- else
- {
- wcscat(ChildPath, AppFileName);
- }
- wcscat(ChildPath, L".exe");
- }
- wchar_t ProductVersion[32];
- GetProductVersion(ProductVersion);
- wchar_t* Parameters = new wchar_t[(CommandLineLen * 2) + 100 + (Count * 3) + 1];
- wsprintf(Parameters, L"\"%s\" /console=%s /consoleinstance=%s ", ChildPath, ProductVersion, InstanceName);
- P = CommandLine;
- // skip executable path
- CutToken(P, Buffer);
- int i = 1;
- while (CutToken(P, Buffer))
- {
- if (i != SkipParam)
- {
- wcscat(Parameters, L"\"");
- wchar_t* P2 = Parameters + wcslen(Parameters);
- const wchar_t* P3 = Buffer;
- const wchar_t* BufferEnd = Buffer + wcslen(Buffer) + 1;
- while (P3 != BufferEnd)
- {
- *P2 = *P3;
- ++P2;
- if (*P3 == L'"')
- {
- *P2 = L'"';
- ++P2;
- }
- ++P3;
- }
- wcscat(Parameters, L"\" ");
- }
- ++i;
- }
- delete[] Buffer;
- STARTUPINFO StartupInfo = { sizeof(STARTUPINFO) };
- PROCESS_INFORMATION ProcessInfomation;
- BOOL Result =
- CreateProcess(ChildPath, Parameters, NULL, NULL, false, 0, NULL, NULL,
- &StartupInfo, &ProcessInfomation);
- delete[] Parameters;
- if (Result)
- {
- Child = ProcessInfomation.hProcess;
- }
- else
- {
- size_t Len = MAX_PATH + 1024;
- DWORD Error = GetLastError();
- wchar_t * Buffer = NULL;
- Len += FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, Error, 0, (LPTSTR)&Buffer, 0, NULL);
- wchar_t* Message = new wchar_t[Len];
- wsprintf(Message, L"Cannot start WinSCP application \"%s\".", ChildPath);
- if (Buffer != NULL)
- {
- wcscat(Message, L"\n");
- wcscat(Message, Buffer);
- LocalFree(Buffer);
- }
- char* MessageString = WideStringToString(Message);
- delete[] Message;
- std::string ErrorString(MessageString);
- delete[] MessageString;
- throw runtime_error(ErrorString);
- }
- }
- //---------------------------------------------------------------------------
- void FinalizeChild(HANDLE Child)
- {
- if (Child != NULL)
- {
- TerminateProcess(Child, 0);
- CloseHandle(Child);
- }
- }
- //---------------------------------------------------------------------------
- void FinalizeConsole(const wchar_t* /*InstanceName*/, HANDLE RequestEvent,
- HANDLE ResponseEvent, HANDLE CancelEvent, HANDLE FileMapping, HANDLE Job)
- {
- CloseHandle(RequestEvent);
- CloseHandle(ResponseEvent);
- CloseHandle(CancelEvent);
- CloseHandle(FileMapping);
- if (Job != NULL)
- {
- CloseHandle(Job);
- }
- }
- //---------------------------------------------------------------------------
- static wchar_t LastFromBeginning[sizeof(TConsoleCommStruct::TPrintEvent)] = L""; //???
- //---------------------------------------------------------------------------
- inline void Flush()
- {
- if ((OutputType == FILE_TYPE_DISK) || (OutputType == FILE_TYPE_PIPE))
- {
- fflush(stdout);
- }
- }
- //---------------------------------------------------------------------------
- void PrintException(const exception& e)
- {
- if (ConsoleOutput != NULL)
- {
- unsigned long Written;
- WriteFile(ConsoleOutput, e.what(), strlen(e.what()), &Written, NULL);
- }
- else
- {
- puts(e.what());
- }
- }
- //---------------------------------------------------------------------------
- void Print(const wchar_t* Message)
- {
- char* Buffer = WideStringToString(Message);
- if (Buffer != NULL)
- {
- char* Ptr = Buffer;
- while ((Ptr = strchr(Ptr, '\n')) != NULL)
- {
- memmove(Ptr + 1, Ptr, strlen(Ptr) + 1);
- *Ptr = '\r';
- Ptr += 2;
- }
- unsigned long Written;
- WriteFile(ConsoleOutput, Buffer, strlen(Buffer), &Written, NULL);
- delete[] Buffer;
- }
- }
- //---------------------------------------------------------------------------
- void Print(bool FromBeginning, const wchar_t* Message)
- {
- size_t Len = wcslen(Message);
- if ((OutputType == FILE_TYPE_DISK) || (OutputType == FILE_TYPE_PIPE))
- {
- if (FromBeginning && (Message[0] != L'\n'))
- {
- wcscpy(LastFromBeginning, Message);
- }
- else
- {
- if (LastFromBeginning[0] != L'\0')
- {
- Print(LastFromBeginning);
- LastFromBeginning[0] = L'\0';
- }
- if (FromBeginning && (Message[0] == L'\n'))
- {
- Print(L"\n");
- wcscpy(LastFromBeginning, Message + 1);
- }
- else
- {
- Print(Message);
- }
- Flush();
- }
- }
- else
- {
- unsigned long Written;
- if (FromBeginning)
- {
- WriteConsole(ConsoleOutput, L"\r", 1, &Written, NULL);
- }
- bool WriteResult =
- WriteConsole(ConsoleOutput, Message, Len, &Written, NULL);
- int Error = GetLastError();
- // The current console font does not support some characters in the message,
- // fall back to ansi-writting
- if (!WriteResult && (Error == ERROR_GEN_FAILURE))
- {
- int Size = WideCharToMultiByte(CP_ACP, 0, Message, -1, 0, 0, 0, 0);
- if (Size > 0)
- {
- char* Buffer = new char[Size];
- if (WideCharToMultiByte(CP_ACP, 0, Message, -1, Buffer, Size, 0, 0) > 0)
- {
- WriteConsoleA(ConsoleOutput, Buffer, strlen(Buffer), &Written, NULL);
- }
- delete[] Buffer;
- }
- }
- }
- }
- //---------------------------------------------------------------------------
- inline void ProcessPrintEvent(TConsoleCommStruct::TPrintEvent& Event)
- {
- Print(Event.FromBeginning, Event.Message);
- }
- //---------------------------------------------------------------------------
- void CancelInput()
- {
- SetEvent(CancelEvent);
- }
- //---------------------------------------------------------------------------
- void BreakInput()
- {
- FlushConsoleInputBuffer(ConsoleInput);
- // Signal cancel first, otherwise the main thread can get Enter before the event is set
- CancelInput();
- INPUT_RECORD InputRecord;
- memset(&InputRecord, 0, sizeof(InputRecord));
- InputRecord.EventType = KEY_EVENT;
- InputRecord.Event.KeyEvent.bKeyDown = true;
- InputRecord.Event.KeyEvent.wRepeatCount = 1;
- InputRecord.Event.KeyEvent.uChar.UnicodeChar = L'\r';
- unsigned long Written;
- WriteConsoleInput(ConsoleInput, &InputRecord, 1, &Written);
- }
- //---------------------------------------------------------------------------
- DWORD WINAPI InputTimerThreadProc(void* Parameter)
- {
- unsigned int Timer = reinterpret_cast<unsigned int>(Parameter);
- unsigned int Remaining = Timer;
- const unsigned int Step = 1000;
- const int FirstKey = VK_LBUTTON; // 0x01
- const int LastKey = VK_OEM_CLEAR; // 0xFE
- // reset key state
- for (int Key = FirstKey; Key <= LastKey; Key++)
- {
- GetAsyncKeyState(Key);
- }
- while (Remaining > 0)
- {
- unsigned long WaitResult = WaitForSingleObject(InputTimerEvent, Step);
- if (WaitResult == WAIT_OBJECT_0)
- {
- // input entered
- Remaining = 0;
- }
- else if (WaitResult == WAIT_TIMEOUT)
- {
- bool Input = false;
- for (int Key = FirstKey; Key <= LastKey; Key++)
- {
- if ((GetAsyncKeyState(Key) & 0x01) != 0)
- {
- Input = true;
- // Finishing the loop nevertheless to reset state of all keys
- }
- }
- if (Input)
- {
- // If we have new input, reset timer
- Remaining = Timer;
- }
- else if (Remaining > Step)
- {
- Remaining -= Step;
- }
- else
- {
- BreakInput();
- Remaining = 0;
- }
- }
- else
- {
- // abort input on (unlikely) error
- BreakInput();
- Remaining = 0;
- }
- }
- return 0;
- }
- //---------------------------------------------------------------------------
- void ProcessInputEvent(TConsoleCommStruct::TInputEvent& Event)
- {
- if ((InputType == FILE_TYPE_DISK) || (InputType == FILE_TYPE_PIPE))
- {
- unsigned long Bytes = 0;
- unsigned long Read;
- bool Result;
- char Ch;
- char Buf[LENOF(Event.Str) * 3];
- while (((Result = (ReadFile(ConsoleInput, &Ch, 1, &Read, NULL) != 0)) != false) &&
- (Read > 0) && (Bytes < LENOF(Buf) - 1) && (Ch != '\n'))
- {
- if (Ch != '\r')
- {
- Buf[Bytes] = Ch;
- Bytes++;
- }
- }
- Buf[Bytes] = L'\0';
- MultiByteToWideChar(CP_UTF8, 0, Buf, -1, Event.Str, LENOF(Event.Str) - 1);
- Event.Str[LENOF(Event.Str) - 1] = L'\0';
- Print(false, Event.Str);
- Print(false, L"\n");
- Event.Result = ((Result && (Read > 0)) || (Bytes > 0));
- }
- else
- {
- unsigned long PrevMode, NewMode;
- GetConsoleMode(ConsoleInput, &PrevMode);
- NewMode = PrevMode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
- if (Event.Echo)
- {
- NewMode |= ENABLE_ECHO_INPUT;
- }
- else
- {
- NewMode &= ~ENABLE_ECHO_INPUT;
- }
- SetConsoleMode(ConsoleInput, NewMode);
- HANDLE InputTimerThread = NULL;
- try
- {
- if (Event.Timer > 0)
- {
- unsigned long ThreadId;
- InputTimerEvent = CreateEvent(NULL, false, false, NULL);
- InputTimerThread = CreateThread(NULL, 0, InputTimerThreadProc,
- reinterpret_cast<void *>(Event.Timer), 0, &ThreadId);
- }
- unsigned long Read;
- Event.Result = ReadConsole(ConsoleInput, Event.Str, LENOF(Event.Str) - 1, &Read, NULL);
- Event.Str[Read] = L'\0';
- bool PendingCancel = (WaitForSingleObject(CancelEvent, 0) == WAIT_OBJECT_0);
- if (PendingCancel || !Event.Echo)
- {
- WriteFile(ConsoleOutput, "\n", 1, NULL, NULL);
- Flush();
- }
- if (PendingCancel || (Read == 0))
- {
- Event.Result = false;
- }
- }
- __finally
- {
- if (InputTimerThread != NULL)
- {
- SetEvent(InputTimerEvent);
- WaitForSingleObject(InputTimerThread, 100);
- CloseHandle(InputTimerEvent);
- InputTimerEvent = NULL;
- CloseHandle(InputTimerThread);
- }
- SetConsoleMode(ConsoleInput, PrevMode);
- }
- }
- }
- //---------------------------------------------------------------------------
- void ProcessChoiceEvent(TConsoleCommStruct::TChoiceEvent& Event)
- {
- // note that if output is redirected to file, input is still FILE_TYPE_CHAR
- if ((InputType == FILE_TYPE_DISK) || (InputType == FILE_TYPE_PIPE))
- {
- if (Event.Timeouting)
- {
- Sleep(Event.Timer);
- Event.Result = Event.Timeouted;
- }
- else
- {
- Event.Result = Event.Break;
- }
- }
- else
- {
- Event.Result = 0;
- unsigned long PrevMode, NewMode;
- GetConsoleMode(ConsoleInput, &PrevMode);
- NewMode = (PrevMode | ENABLE_PROCESSED_INPUT) & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
- SetConsoleMode(ConsoleInput, NewMode);
- unsigned int ATimer = Event.Timer;
- try
- {
- do
- {
- unsigned long Read;
- INPUT_RECORD Record;
- if ((PeekConsoleInput(ConsoleInput, &Record, 1, &Read) != 0) &&
- (Read == 1))
- {
- if ((ReadConsoleInput(ConsoleInput, &Record, 1, &Read) != 0) &&
- (Read == 1))
- {
- bool PendingCancel = (WaitForSingleObject(CancelEvent, 0) == WAIT_OBJECT_0);
- if (PendingCancel)
- {
- Event.Result = Event.Break;
- }
- else if ((Record.EventType == KEY_EVENT) &&
- Record.Event.KeyEvent.bKeyDown)
- {
- // This happens when Shift key is pressed
- if (Record.Event.KeyEvent.uChar.UnicodeChar != 0)
- {
- wchar_t CStr[2];
- CStr[0] = Record.Event.KeyEvent.uChar.UnicodeChar;
- CStr[1] = L'\0';
- CharUpperBuff(CStr, 1);
- wchar_t C = CStr[0];
- if (C == 27)
- {
- Event.Result = Event.Cancel;
- }
- else if ((wcschr(Event.Options, C) != NULL) &&
- ((Record.Event.KeyEvent.dwControlKeyState &
- (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | LEFT_ALT_PRESSED |
- RIGHT_ALT_PRESSED)) == 0))
- {
- Event.Result = wcschr(Event.Options, C) - Event.Options + 1;
- }
- }
- }
- }
- }
- if (Event.Result == 0)
- {
- unsigned int TimerSlice = 50;
- Sleep(TimerSlice);
- if (Event.Timer > 0)
- {
- if (ATimer > TimerSlice)
- {
- ATimer -= TimerSlice;
- }
- else
- {
- Event.Result = Event.Timeouted;
- }
- }
- }
- }
- while (Event.Result == 0);
- SetConsoleMode(ConsoleInput, PrevMode);
- }
- catch(...)
- {
- SetConsoleMode(ConsoleInput, PrevMode);
- throw;
- }
- }
- }
- //---------------------------------------------------------------------------
- inline void ProcessTitleEvent(TConsoleCommStruct::TTitleEvent& Event)
- {
- SetConsoleTitle(Event.Title);
- }
- //---------------------------------------------------------------------------
- inline void ProcessInitEvent(TConsoleCommStruct::TInitEvent& Event)
- {
- if (Event.UseStdErr)
- {
- ConsoleOutput = ConsoleErrorOutput;
- }
- OutputFormat = Event.OutputFormat;
- if (OutputFormat != TConsoleCommStruct::TInitEvent::OFF)
- {
- setmode(fileno(stdout), O_BINARY);
- }
- InputFormat = Event.InputFormat;
- if (InputFormat != TConsoleCommStruct::TInitEvent::OFF)
- {
- setmode(fileno(stdin), O_BINARY);
- }
- OutputType = GetFileType(ConsoleOutput);
- // Until now we should not have printed anything.
- // Only in case of a fatal failure, we might have printed a pure ASCII error messages (and never got here).
- if ((OutputType == FILE_TYPE_DISK) || (OutputType == FILE_TYPE_PIPE) ||
- SupportsUtf8ConsoleOutput)
- {
- SetConsoleOutputCP(CP_UTF8);
- }
- else
- {
- SetConsoleOutputCP(CP_ACP);
- }
- Event.InputType = InputType;
- Event.OutputType = OutputType;
- // default anyway
- Event.WantsProgress = false;
- }
- //---------------------------------------------------------------------------
- inline void ProcessTransferOutEvent(TConsoleCommStruct::TTransferEvent& Event)
- {
- if (OutputFormat == TConsoleCommStruct::TInitEvent::BINARY)
- {
- fwrite(Event.Data, 1, Event.Len, stdout);
- }
- else if (OutputFormat == TConsoleCommStruct::TInitEvent::CHUNKED)
- {
- fprintf(stdout, "%x\r\n", static_cast<int>(Event.Len));
- fwrite(Event.Data, 1, Event.Len, stdout);
- fputs("\r\n", stdout);
- }
- }
- //---------------------------------------------------------------------------
- inline void ProcessTransferInEvent(TConsoleCommStruct::TTransferEvent& Event)
- {
- size_t Read = fread(Event.Data, 1, Event.Len, stdin);
- if (Read != Event.Len)
- {
- if (ferror(stdin))
- {
- Event.Error = true;
- }
- else
- {
- Event.Len = Read;
- }
- }
- }
- //---------------------------------------------------------------------------
- void ProcessEvent(HANDLE ResponseEvent, HANDLE FileMapping)
- {
- TConsoleCommStruct* CommStruct = GetCommStruct(FileMapping);
- try
- {
- if (CommStruct->Version != TConsoleCommStruct::CurrentVersionConfirmed)
- {
- throw runtime_error("Incompatible console protocol version");
- }
- switch (CommStruct->Event)
- {
- case TConsoleCommStruct::PRINT:
- ProcessPrintEvent(CommStruct->PrintEvent);
- break;
- case TConsoleCommStruct::INPUT:
- ProcessInputEvent(CommStruct->InputEvent);
- break;
- case TConsoleCommStruct::CHOICE:
- ProcessChoiceEvent(CommStruct->ChoiceEvent);
- break;
- case TConsoleCommStruct::TITLE:
- ProcessTitleEvent(CommStruct->TitleEvent);
- break;
- case TConsoleCommStruct::INIT:
- ProcessInitEvent(CommStruct->InitEvent);
- break;
- case TConsoleCommStruct::TRANSFEROUT:
- ProcessTransferOutEvent(CommStruct->TransferEvent);
- break;
- case TConsoleCommStruct::TRANSFERIN:
- ProcessTransferInEvent(CommStruct->TransferEvent);
- break;
- default:
- throw runtime_error("Unknown event");
- }
- FreeCommStruct(CommStruct);
- SetEvent(ResponseEvent);
- }
- catch(...)
- {
- FreeCommStruct(CommStruct);
- throw;
- }
- }
- //---------------------------------------------------------------------------
- BOOL WINAPI HandlerRoutine(DWORD CtrlType)
- {
- if ((CtrlType == CTRL_C_EVENT) || (CtrlType == CTRL_BREAK_EVENT))
- {
- CancelInput();
- return true;
- }
- else
- {
- FinalizeChild(Child);
- return false;
- }
- }
- //---------------------------------------------------------------------------
- #pragma argsused
- int wmain(int /*argc*/, wchar_t* /*argv*/[])
- {
- unsigned long Result = RESULT_UNKNOWN_ERROR;
- try
- {
- randomize();
- OSVERSIONINFO VersionInfo;
- VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
- GetVersionEx(&VersionInfo);
- ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
- InputType = GetFileType(ConsoleInput);
- SetConsoleCtrlHandler(HandlerRoutine, true);
- unsigned int SavedConsoleCP = GetConsoleCP();
- unsigned int SavedConsoleOutputCP = GetConsoleOutputCP();
- ConsoleStandardOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- ConsoleOutput = ConsoleStandardOutput;
- ConsoleErrorOutput = GetStdHandle(STD_ERROR_HANDLE);
- SupportsUtf8ConsoleOutput =
- ((VersionInfo.dwMajorVersion == 6) && (VersionInfo.dwMinorVersion >= 1)) ||
- (VersionInfo.dwMajorVersion > 6);
- if ((InputType == FILE_TYPE_DISK) || (InputType == FILE_TYPE_PIPE) ||
- SupportsUtf8ConsoleOutput)
- {
- SetConsoleCP(CP_UTF8);
- }
- else
- {
- SetConsoleCP(CP_ACP);
- }
- wchar_t InstanceName[MAX_PATH];
- HANDLE RequestEvent, ResponseEvent, FileMapping, Job;
- InitializeConsole(InstanceName, RequestEvent, ResponseEvent,
- CancelEvent, FileMapping, Job);
- wchar_t SavedTitle[512];
- GetConsoleTitle(SavedTitle, LENOF(SavedTitle));
- try
- {
- #ifndef CONSOLE_TEST
- InitializeChild(GetCommandLine(), InstanceName, Child);
- #endif
- try
- {
- bool Continue = true;
- do
- {
- HANDLE Handles[2];
- Handles[0] = RequestEvent;
- Handles[1] = Child;
- unsigned int HandleCount;
- #ifndef CONSOLE_TEST
- HandleCount = 2;
- #else
- HandleCount = 1;
- #endif
- unsigned long WaitResult =
- WaitForMultipleObjects(HandleCount, Handles, false, INFINITE);
- switch (WaitResult)
- {
- case WAIT_OBJECT_0:
- ProcessEvent(ResponseEvent, FileMapping);
- break;
- case WAIT_OBJECT_0 + 1:
- GetExitCodeProcess(Child, &Result);
- CloseHandle(Child);
- Child = NULL;
- Continue = false;
- break;
- default:
- throw runtime_error("Error waiting for communication from child process.");
- }
- }
- while (Continue);
- // flush pending progress message
- Print(false, L"");
- }
- catch(const exception& e)
- {
- PrintException(e);
- Result = RESULT_PROCESSING_ERROR;
- }
- #ifndef CONSOLE_TEST
- FinalizeChild(Child);
- #endif
- SetConsoleTitle(SavedTitle);
- SetConsoleCP(SavedConsoleCP);
- SetConsoleOutputCP(SavedConsoleOutputCP);
- }
- catch(const exception& e)
- {
- PrintException(e);
- Result = RESULT_INIT_ERROR;
- }
- FinalizeConsole(InstanceName, RequestEvent, ResponseEvent,
- CancelEvent, FileMapping, Job);
- }
- catch(const exception& e)
- {
- PrintException(e);
- Result = RESULT_GLOBAL_ERROR;
- }
- return Result;
- }
- //---------------------------------------------------------------------------
|