SHARE
TWEET

IO Proxy C++ version

EnderAlice Sep 2nd, 2014 293 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. Copyright (c) 2014, alice (@a1lic)
  3. All rights reserved.
  4.  
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are met:
  7. * Redistributions of source code must retain the above copyright notice,
  8. this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright notice,
  10. this list of conditions and the following disclaimer in the documentation
  11. and/or other materials provided with the distribution.
  12. * Neither the name of the Alice Software Studio nor the names of its contributors
  13. may be used to endorse or promote products derived from this software
  14. without specific prior written permission.
  15.  
  16. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. DISCLAIMED. IN NO EVENT SHALL ALICE BE LIABLE FOR ANY
  20. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27.  
  28. #define OUTPUT_BUFFER_BYTES 65536U
  29.  
  30. #include <Windows.h>
  31. #include <process.h>
  32. #include <tchar.h>
  33.  
  34. struct UTF8OUTPUTDESCRIPTOR
  35. {
  36.         HANDLE ChildProcessInput;
  37.         bool IsStdError;
  38.         bool AbortThread;
  39. };
  40. typedef struct UTF8OUTPUTDESCRIPTOR UTF8OUTPUTDESCRIPTOR;
  41.  
  42. struct UTF8INPUTDESCRIPTOR
  43. {
  44.         HANDLE ChildProcessOutput;
  45.         HANDLE Input;
  46.         bool AbortThread;
  47. };
  48. typedef struct UTF8INPUTDESCRIPTOR UTF8INPUTDESCRIPTOR;
  49.  
  50. extern "C" const _TCHAR * GetNextToken(const _TCHAR * const Str)
  51. {
  52.         const _TCHAR * _Str;
  53.         _TCHAR PrevChar;
  54.         boolean IsInToken;
  55.         boolean Spacing;
  56.  
  57.         for(PrevChar = _T('\0'), IsInToken = false, Spacing = false, _Str = Str; *_Str; _Str++)
  58.         {
  59.                 switch(*_Str)
  60.                 {
  61.                 case _T('"'):
  62.                         IsInToken = !IsInToken;
  63.                         break;
  64.                 case _T(' '):
  65.                         if(!IsInToken)
  66.                         {
  67.                                 Spacing = true;
  68.                         }
  69.                         break;
  70.                 default:
  71.                         if(Spacing)
  72.                         {
  73.                                 if(PrevChar == _T('"'))
  74.                                 {
  75.                                         _Str--;
  76.                                 }
  77.                                 return _Str;
  78.                         }
  79.                         break;
  80.                 }
  81.                 PrevChar = *_Str;
  82.         }
  83.  
  84.         return nullptr;
  85. }
  86.  
  87. extern "C" unsigned int __stdcall ConsoleReaderThread(void * const Argument)
  88. {
  89.  
  90.         return 0;
  91. }
  92.  
  93. extern "C" unsigned int __stdcall Utf8InputThread(void * const Argument)
  94. {
  95.         OVERLAPPED Overlapped;
  96.         WCHAR * Buffer;
  97.         DWORD Read;
  98.         CHAR * Utf8Input;
  99.         int Length;
  100.  
  101.         ((UTF8INPUTDESCRIPTOR*)Argument)->AbortThread = FALSE;
  102.  
  103.         Buffer = reinterpret_cast<WCHAR *>(::VirtualAlloc(nullptr, 65536, MEM_COMMIT, PAGE_READWRITE));
  104.         for(;;)
  105.         {
  106.                 if(((UTF8INPUTDESCRIPTOR*)Argument)->AbortThread)
  107.                 {
  108.                         break;
  109.                 }
  110.  
  111.                 ::memset(&Overlapped, 0, sizeof(OVERLAPPED));
  112.                 ::memset(Buffer, 0, 65536);
  113.  
  114.                 if(::ReadConsoleW(((UTF8INPUTDESCRIPTOR*)Argument)->Input, Buffer, 32768, &Read, nullptr))
  115.                 {
  116.                         Length = ::WideCharToMultiByte(CP_UTF8, 0, Buffer, (int)::wcslen(Buffer), nullptr, 0, nullptr, nullptr);
  117.                         if(Length > 0)
  118.                         {
  119.                                 Utf8Input = new CHAR[Length];
  120.                                 ::WideCharToMultiByte(CP_UTF8, 0, Buffer, (int)::wcslen(Buffer), Utf8Input, Length, nullptr, nullptr);
  121.  
  122.                                 ::WriteFile(((UTF8INPUTDESCRIPTOR*)Argument)->ChildProcessOutput, Utf8Input, Length, &Read, nullptr);
  123.                                 delete[] Utf8Input;
  124.                         }
  125.                 }
  126.         }
  127.  
  128.         ::VirtualFree(Buffer, 0, MEM_RELEASE);
  129.         ::free(Argument);
  130.         return 0;
  131. }
  132.  
  133. extern "C" void WriteConsoleAsUtf8(const char * const Buffer)
  134. {
  135.         int Length;
  136.         WCHAR * UnicodeString;
  137.         DWORD _w;
  138.  
  139.         Length = ::MultiByteToWideChar(CP_UTF8, 0, Buffer, (int)::strlen(Buffer), nullptr, 0);
  140.         if(Length > 0)
  141.         {
  142.                 UnicodeString = new WCHAR[Length];
  143.                 ::MultiByteToWideChar(CP_UTF8, 0, Buffer, (int)::strlen(Buffer), UnicodeString, Length);
  144.  
  145.                 ::WriteConsoleW(::GetStdHandle(STD_OUTPUT_HANDLE), UnicodeString, Length, &_w, nullptr);
  146.  
  147.                 delete[] UnicodeString;
  148.         }
  149. }
  150.  
  151. extern "C" VOID CALLBACK Utf8OutputComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
  152. {
  153.         ::SetEvent(lpOverlapped->hEvent);
  154. }
  155.  
  156. extern "C" bool uni_utf8toucs4(unsigned long * const ucs4, const unsigned char * const utf8, unsigned char * const bytes_read)
  157. {
  158.         const unsigned char * utf = utf8;
  159.         unsigned char read = 0;
  160.         unsigned long RC = 0;
  161.  
  162.         if((utf[0]) && ((utf[0] & 0xC0) != 0x80))
  163.         {
  164.                 if(utf[0] < 0xC0)
  165.                 {
  166.                         RC = utf[0];
  167.                         read = 1;
  168.                 }
  169.                 else if((utf[1] & 0xC0) == 0x80)
  170.                 {
  171.                         if(utf[0] < 0xE0)
  172.                         {
  173.                                 RC = (((utf[0] - 0xC0) << 6) + (utf[1] - 0x80));
  174.                                 read = 2;
  175.                         }
  176.                         else if((utf[2] & 0xC0) == 0x80)
  177.                         {
  178.                                 if(utf[0] < 0xF0)
  179.                                 {
  180.                                         RC = (((utf[0] - 0xE0) << 12) + ((utf[1] - 0x80) << 6) + (utf[2] - 0x80));
  181.                                         read = 3;
  182.                                 }
  183.                                 else if((utf[3] & 0xC0) == 0x80)
  184.                                 {
  185.                                         if(utf[0] < 0xF8)
  186.                                         {
  187.                                                 RC = (((utf[0] - 0xF0) << 18) + ((utf[1] - 0x80) << 12) + ((utf[2] - 0x80) << 6) + (utf[3] - 0x80));
  188.                                                 read = 4;
  189.                                         }
  190.                                         else if((utf[4] & 0xC0) == 0x80)
  191.                                         {
  192.                                                 if(utf[0] < 0xFC)
  193.                                                 {
  194.                                                         RC = (((utf[0] - 0xF8) << 24) + ((utf[1] - 0x80) << 18) + ((utf[2] - 0x80) << 12) + ((utf[3] - 0x80) << 6) + (utf[4] - 0x80));
  195.                                                         read = 4;
  196.                                                 }
  197.                                                 else if((utf[5] & 0xC0) == 0x80)
  198.                                                 {
  199.                                                         if(utf[0] < 0xFE)
  200.                                                         {
  201.                                                                 RC = (((utf[0] - 0xFC) << 30) + ((utf[1] - 0x80) << 24) + ((utf[2] - 0x80) << 18) + ((utf[3] - 0x80) << 12) + ((utf[4] - 0x80) << 6) + (utf[5] - 0x80));
  202.                                                                 read = 5;
  203.                                                         }
  204.                                                 }
  205.                                         }
  206.                                 }
  207.                         }
  208.                 }
  209.         }
  210.  
  211.         if(read)
  212.         {
  213.                 if(bytes_read)
  214.                 {
  215.                         *bytes_read = read;
  216.                 }
  217.                 if(ucs4)
  218.                 {
  219.                         *ucs4 = RC;
  220.                 }
  221.                 return true;
  222.         }
  223.  
  224.         return false;
  225. }
  226.  
  227. extern "C" bool GetValidRangeUtf8String(const CHAR * const Buffer, size_t BufferSize, ptrdiff_t StartAt, ptrdiff_t * const ValidAt, size_t * const ValidLength)
  228. {
  229.         ptrdiff_t ValidStart;
  230.         ptrdiff_t Offset;
  231.         ptrdiff_t EndOffset;
  232.         size_t Length;
  233.         unsigned char SequenceLength;
  234.         unsigned char CharBuffer[8];
  235.  
  236.         EndOffset = (BufferSize) - StartAt;
  237.         for(ValidStart = StartAt; ValidStart <= EndOffset; ValidStart++)
  238.         {
  239.                 ::memset(CharBuffer, 0, 8);
  240.                 ::memcpy(CharBuffer, &Buffer[ValidStart], min(6, EndOffset - ValidStart));
  241.                 if(::uni_utf8toucs4(nullptr, CharBuffer, &SequenceLength))
  242.                 {
  243.                         break;
  244.                 }
  245.         }
  246.         if(ValidStart > EndOffset)
  247.         {
  248.                 *ValidAt = 0;
  249.                 *ValidLength = 0;
  250.                 return false;
  251.         }
  252.  
  253.         Length = 0;
  254.         for(Offset = ValidStart; Offset <= EndOffset; Offset += SequenceLength)
  255.         {
  256.                 ::memset(CharBuffer, 0, 8);
  257.                 ::memcpy(CharBuffer, &Buffer[Offset], min(6, EndOffset - Offset));
  258.                 if(!::uni_utf8toucs4(nullptr, CharBuffer, &SequenceLength))
  259.                 {
  260.                         break;
  261.                 }
  262.                 Length += SequenceLength;
  263.         }
  264.  
  265.         *ValidAt = ValidStart;
  266.         *ValidLength = Length;
  267.         return true;
  268. }
  269.  
  270. extern "C" size_t CopyValidUtf8StringFromBuffer(const CHAR * const Buffer, size_t BufferSize, ptrdiff_t StartAt, CHAR * const DstBuffer, size_t DstSize)
  271. {
  272.         ptrdiff_t ValidAt;
  273.         size_t ValidLength;
  274.  
  275.         ::memset(DstBuffer, 0, DstSize);
  276.  
  277.         if(::GetValidRangeUtf8String(Buffer, BufferSize, StartAt, &ValidAt, &ValidLength))
  278.         {
  279.                 ::memcpy(DstBuffer, &Buffer[ValidAt], ValidLength);
  280.         }
  281.         else
  282.         {
  283.                 ValidLength = 0;
  284.         }
  285.  
  286.         return ValidLength;
  287. }
  288.  
  289. extern "C" unsigned int __stdcall Utf8OutputThread(void * const Argument)
  290. {
  291.         OVERLAPPED Overlapped;
  292.         HANDLE Event;
  293.         CHAR * Buffer;
  294.         CHAR * ValidUtf8Buffer;
  295.         DWORD WaitStatus;
  296.         DWORD BytesInBuffer;
  297.         DWORD PrevBytesInBuffer;
  298.         ptrdiff_t BufferCursor;
  299.         size_t BufferCopied;
  300.         size_t Remaining;
  301.         CHAR Remains[8];
  302.  
  303.         ((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread = FALSE;
  304.  
  305.         // 子プロセスからの標準出力を読み込むバッファー
  306.         Buffer = reinterpret_cast<CHAR *>(::VirtualAlloc(nullptr, OUTPUT_BUFFER_BYTES, MEM_COMMIT, PAGE_READWRITE));
  307.         if(Buffer == nullptr)
  308.         {
  309.                 ::free(Argument);
  310.                 return 1;
  311.         }
  312.  
  313.         // バッファーから有効なUTF-8とみなされたバイト列を格納するバッファー
  314.         // このバッファーの内容は画面に出力が行われるごとに無効化される
  315.         ValidUtf8Buffer = reinterpret_cast<CHAR *>(::VirtualAlloc(nullptr, OUTPUT_BUFFER_BYTES, MEM_COMMIT, PAGE_READWRITE));
  316.         if(ValidUtf8Buffer == nullptr)
  317.         {
  318.                 ::VirtualFree(Buffer, 0, MEM_RELEASE);
  319.                 ::free(Argument);
  320.                 return 1;
  321.         }
  322.  
  323.         Event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
  324.         ::memset(Remains, 0, sizeof(Remains));
  325.         for(;;)
  326.         {
  327.                 if(((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread)
  328.                 {
  329.                         break;
  330.                 }
  331.  
  332.                 ::memset(&Overlapped, 0, sizeof(OVERLAPPED));
  333.                 ::memset(Buffer, 0, OUTPUT_BUFFER_BYTES);
  334.                 Overlapped.hEvent = Event; // これ要らないかも
  335.  
  336.                 // 前回の読み込みで余ったバイトシーケンスをバッファーに書き込む
  337.                 Remaining = ::strlen(Remains);
  338.                 if(Remaining > 0)
  339.                 {
  340.                         ::memcpy(Buffer, Remains, sizeof(Remains));
  341.                         ::memset(Remains, 0, sizeof(Remains));
  342.                 }
  343.                 BufferCursor = Remaining;
  344.  
  345.                 // 非同期読み込み開始
  346.                 if(!::ReadFileEx(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput, &Buffer[BufferCursor], OUTPUT_BUFFER_BYTES - (DWORD)BufferCursor, &Overlapped, Utf8OutputComplete))
  347.                 {
  348.                         // 非同期処理に失敗した場合はやり直し
  349.                         ::OutputDebugString(TEXT("ReadFileEx failed.\n"));
  350.                         ::Sleep(5000);
  351.                         continue;
  352.                 }
  353.  
  354.                 for(;;)
  355.                 {
  356.                         if(((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread)
  357.                         {
  358.                                 // スレッドの終了要求が来たらI/Oを終了してループを脱出する
  359.                                 ::CancelIo(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput);
  360.                                 break;
  361.                         }
  362.  
  363.                         if(::GetOverlappedResult(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput, &Overlapped, &BytesInBuffer, FALSE))
  364.                         {
  365.                                 if((BytesInBuffer > 0) && (BytesInBuffer == PrevBytesInBuffer))
  366.                                 {
  367.                                         // これ以上ストリームにデータが無い場合はI/Oを終わらせる
  368.                                         ::CancelIo(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput);
  369.                                         break;
  370.                                 }
  371.  
  372.                                 PrevBytesInBuffer = BytesInBuffer;
  373.  
  374.                                 // 定期的にバッファを確認し、有効なUTF-8のバイトシーケンスを取り出す
  375.                                 BufferCopied = ::CopyValidUtf8StringFromBuffer(Buffer, OUTPUT_BUFFER_BYTES, BufferCursor, ValidUtf8Buffer, OUTPUT_BUFFER_BYTES);
  376.                                 if(BufferCopied > 0)
  377.                                 {
  378.                                         // 有効なバイトシーケンスが取り出せた場合は出力
  379.                                         ::WriteConsoleAsUtf8(reinterpret_cast<char *>(ValidUtf8Buffer));
  380.                                         BufferCursor += BufferCopied;
  381.                                 }
  382.                         }
  383.  
  384.                         WaitStatus = ::WaitForSingleObject(Event, 0);
  385.                         if(WaitStatus != WAIT_TIMEOUT)
  386.                         {
  387.                                 // I/O完了ルーチンが呼ばれていた場合は現在のReadFileExを終了して新たにReadFileExを開始する
  388.                                 ::memcpy(Remains, &Buffer[BufferCursor], Overlapped.InternalHigh - BufferCursor);
  389.                                 break;
  390.                         }
  391.  
  392.                         // 待機
  393.                         ::Sleep(50);
  394.                 }
  395.         }
  396.  
  397.         ::CloseHandle(Event);
  398.         ::VirtualFree(ValidUtf8Buffer, 0, MEM_RELEASE);
  399.         ::VirtualFree(Buffer, 0, MEM_RELEASE);
  400.         ::free(Argument);
  401.         return 0;
  402. }
  403.  
  404. extern "C" int _tmain(int const argc, const _TCHAR * const argv[])
  405. {
  406.         SECURITY_ATTRIBUTES Attribute;
  407.         STARTUPINFO Startup;
  408.         PROCESS_INFORMATION Process;
  409.         UTF8OUTPUTDESCRIPTOR *Utf8OutDesc, *Utf8ErrDesc;
  410.         UTF8INPUTDESCRIPTOR *Utf8InDesc;
  411.         HANDLE Utf8OutReader, Utf8ErrReader, Utf8InWriter;
  412.         HANDLE Utf8Out, Utf8Err, Utf8In;
  413.         HANDLE Utf8OutThread, Utf8ErrThread, Utf8InThread;
  414.         const _TCHAR * NewArg;
  415.         _TCHAR * ArgDup;
  416.         BOOL BoolResult;
  417.         unsigned int ThreadId;
  418.  
  419.         if(argc <= 1)
  420.         {
  421.                 return 1;
  422.         }
  423.  
  424.         NewArg = ::GetNextToken(::GetCommandLine());
  425.         ::_tprintf_s(_T("Next token : %s\n"), NewArg);
  426.  
  427.         ArgDup = ::_tcsdup(NewArg);
  428.  
  429.         // パイプのハンドルを継承できるように属性を設定する
  430.         ::memset(&Attribute, 0, sizeof(SECURITY_ATTRIBUTES));
  431.         Attribute.nLength = sizeof(SECURITY_ATTRIBUTES);
  432.         Attribute.bInheritHandle = TRUE;
  433.  
  434.         // 子プロセスの標準入出力に使用するパイプを作成する
  435.         ::CreatePipe(&Utf8OutReader, &Utf8Out, &Attribute, 0);
  436.         ::CreatePipe(&Utf8ErrReader, &Utf8Err, &Attribute, 0);
  437.         ::CreatePipe(&Utf8In, &Utf8InWriter, &Attribute, 0);
  438.  
  439.         // 子プロセスの標準入出力を設定する
  440.         ::memset(&Startup, 0, sizeof(STARTUPINFO));
  441.         Startup.cb = sizeof(STARTUPINFO);
  442.         Startup.dwFlags = STARTF_USESTDHANDLES;
  443.         Startup.hStdOutput = Utf8Out;
  444.         Startup.hStdError = Utf8Err;
  445.         Startup.hStdInput = Utf8In;
  446.  
  447.         // 子プロセスを起動する
  448.         BoolResult = ::CreateProcess(nullptr, ArgDup, nullptr, nullptr, TRUE, 0 /*CREATE_NO_WINDOW*/, nullptr, nullptr, &Startup, &Process);
  449.         // この時点で子プロセスに渡したハンドルは必要ないので破棄する
  450.         ::CloseHandle(Utf8In);
  451.         ::CloseHandle(Utf8Out);
  452.         ::CloseHandle(Utf8Err);
  453.         if(BoolResult)
  454.         {
  455.                 // スレッドのハンドルは不要なので閉じる
  456.                 ::CloseHandle(Process.hThread);
  457.  
  458.                 Utf8OutDesc = reinterpret_cast<UTF8OUTPUTDESCRIPTOR*>(::malloc(sizeof(UTF8OUTPUTDESCRIPTOR)));
  459.                 Utf8OutDesc->ChildProcessInput = Utf8OutReader;
  460.                 Utf8OutDesc->IsStdError = false;
  461.                 Utf8ErrDesc = reinterpret_cast<UTF8OUTPUTDESCRIPTOR*>(::malloc(sizeof(UTF8OUTPUTDESCRIPTOR)));
  462.                 Utf8ErrDesc->ChildProcessInput = Utf8ErrReader;
  463.                 Utf8ErrDesc->IsStdError = true;
  464.                 Utf8InDesc = reinterpret_cast<UTF8INPUTDESCRIPTOR*>(::malloc(sizeof(UTF8INPUTDESCRIPTOR)));
  465.                 Utf8InDesc->ChildProcessOutput = Utf8InWriter;
  466.                 Utf8InDesc->Input = ::GetStdHandle(STD_INPUT_HANDLE);
  467.                 Utf8InDesc->AbortThread = true;
  468.  
  469.                 // スレッドをハンドル毎に生成する
  470.                 Utf8OutThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8OutputThread, Utf8OutDesc, 0, &ThreadId));
  471.                 ::CloseHandle(Utf8OutThread);
  472.                 Utf8ErrThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8OutputThread, Utf8ErrDesc, 0, &ThreadId));
  473.                 ::CloseHandle(Utf8ErrThread);
  474.                 Utf8InThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8InputThread, Utf8InDesc, 0, &ThreadId));
  475.                 ::CloseHandle(Utf8InThread);
  476.  
  477.                 // プロセスが終わるまで待機
  478.                 ::WaitForSingleObject(Process.hProcess, INFINITE);
  479.                 // プロセスのハンドルを破棄する
  480.                 ::CloseHandle(Process.hProcess);
  481.  
  482.                 // スレッドに終了を通知する
  483.                 Utf8OutDesc->AbortThread = true;
  484.                 Utf8ErrDesc->AbortThread = true;
  485.                 Utf8InDesc->AbortThread = true;
  486.         }
  487.  
  488.         // 通信用のパイプを破棄する
  489.         ::CloseHandle(Utf8InWriter);
  490.         ::CloseHandle(Utf8OutReader);
  491.         ::CloseHandle(Utf8ErrReader);
  492.  
  493.         return 0;
  494. }
RAW Paste Data
Top