| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 | //---------------------------------------------------------------------------#include <stdexcept>#include <stdio.h>#include <stdlib.h>#include <windows.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 Child = NULL;HANDLE CancelEvent = NULL;HANDLE InputTimerEvent = NULL;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.cppbool __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 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);  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);  CancelInput();}//---------------------------------------------------------------------------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){  Event.InputType = InputType;  Event.OutputType = OutputType;  // default anyway  Event.WantsProgress = false;}//---------------------------------------------------------------------------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;      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 argsusedint 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();    ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);    OutputType = GetFileType(ConsoleOutput);    bool 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);    }    if ((OutputType == FILE_TYPE_DISK) || (OutputType == FILE_TYPE_PIPE) ||        SupportsUtf8ConsoleOutput)    {      SetConsoleOutputCP(CP_UTF8);    }    else    {      SetConsoleOutputCP(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)      {        puts(e.what());        Result = RESULT_PROCESSING_ERROR;      }      #ifndef CONSOLE_TEST      FinalizeChild(Child);      #endif      SetConsoleTitle(SavedTitle);      SetConsoleCP(SavedConsoleCP);      SetConsoleOutputCP(SavedConsoleOutputCP);    }    catch(const exception& e)    {      puts(e.what());      Result = RESULT_INIT_ERROR;    }    FinalizeConsole(InstanceName, RequestEvent, ResponseEvent,      CancelEvent, FileMapping, Job);  }  catch(const exception& e)  {    puts(e.what());    Result = RESULT_GLOBAL_ERROR;  }  return Result;}//---------------------------------------------------------------------------
 |