Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Copyright (c) 2014, alice (@a1lic)
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of the Alice Software Studio nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL ALICE BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #define OUTPUT_BUFFER_BYTES 65536U
- #include <Windows.h>
- #include <process.h>
- #include <tchar.h>
- struct UTF8OUTPUTDESCRIPTOR
- {
- HANDLE ChildProcessInput;
- bool IsStdError;
- bool AbortThread;
- };
- typedef struct UTF8OUTPUTDESCRIPTOR UTF8OUTPUTDESCRIPTOR;
- struct UTF8INPUTDESCRIPTOR
- {
- HANDLE ChildProcessOutput;
- HANDLE Input;
- bool AbortThread;
- };
- typedef struct UTF8INPUTDESCRIPTOR UTF8INPUTDESCRIPTOR;
- extern "C" const _TCHAR * GetNextToken(const _TCHAR * const Str)
- {
- const _TCHAR * _Str;
- _TCHAR PrevChar;
- boolean IsInToken;
- boolean Spacing;
- for(PrevChar = _T('\0'), IsInToken = false, Spacing = false, _Str = Str; *_Str; _Str++)
- {
- switch(*_Str)
- {
- case _T('"'):
- IsInToken = !IsInToken;
- break;
- case _T(' '):
- if(!IsInToken)
- {
- Spacing = true;
- }
- break;
- default:
- if(Spacing)
- {
- if(PrevChar == _T('"'))
- {
- _Str--;
- }
- return _Str;
- }
- break;
- }
- PrevChar = *_Str;
- }
- return nullptr;
- }
- extern "C" unsigned int __stdcall ConsoleReaderThread(void * const Argument)
- {
- return 0;
- }
- extern "C" unsigned int __stdcall Utf8InputThread(void * const Argument)
- {
- OVERLAPPED Overlapped;
- WCHAR * Buffer;
- DWORD Read;
- CHAR * Utf8Input;
- int Length;
- ((UTF8INPUTDESCRIPTOR*)Argument)->AbortThread = FALSE;
- Buffer = reinterpret_cast<WCHAR *>(::VirtualAlloc(nullptr, 65536, MEM_COMMIT, PAGE_READWRITE));
- for(;;)
- {
- if(((UTF8INPUTDESCRIPTOR*)Argument)->AbortThread)
- {
- break;
- }
- ::memset(&Overlapped, 0, sizeof(OVERLAPPED));
- ::memset(Buffer, 0, 65536);
- if(::ReadConsoleW(((UTF8INPUTDESCRIPTOR*)Argument)->Input, Buffer, 32768, &Read, nullptr))
- {
- Length = ::WideCharToMultiByte(CP_UTF8, 0, Buffer, (int)::wcslen(Buffer), nullptr, 0, nullptr, nullptr);
- if(Length > 0)
- {
- Utf8Input = new CHAR[Length];
- ::WideCharToMultiByte(CP_UTF8, 0, Buffer, (int)::wcslen(Buffer), Utf8Input, Length, nullptr, nullptr);
- ::WriteFile(((UTF8INPUTDESCRIPTOR*)Argument)->ChildProcessOutput, Utf8Input, Length, &Read, nullptr);
- delete[] Utf8Input;
- }
- }
- }
- ::VirtualFree(Buffer, 0, MEM_RELEASE);
- ::free(Argument);
- return 0;
- }
- extern "C" void WriteConsoleAsUtf8(const char * const Buffer)
- {
- int Length;
- WCHAR * UnicodeString;
- DWORD _w;
- Length = ::MultiByteToWideChar(CP_UTF8, 0, Buffer, (int)::strlen(Buffer), nullptr, 0);
- if(Length > 0)
- {
- UnicodeString = new WCHAR[Length];
- ::MultiByteToWideChar(CP_UTF8, 0, Buffer, (int)::strlen(Buffer), UnicodeString, Length);
- ::WriteConsoleW(::GetStdHandle(STD_OUTPUT_HANDLE), UnicodeString, Length, &_w, nullptr);
- delete[] UnicodeString;
- }
- }
- extern "C" VOID CALLBACK Utf8OutputComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
- {
- ::SetEvent(lpOverlapped->hEvent);
- }
- extern "C" bool uni_utf8toucs4(unsigned long * const ucs4, const unsigned char * const utf8, unsigned char * const bytes_read)
- {
- const unsigned char * utf = utf8;
- unsigned char read = 0;
- unsigned long RC = 0;
- if((utf[0]) && ((utf[0] & 0xC0) != 0x80))
- {
- if(utf[0] < 0xC0)
- {
- RC = utf[0];
- read = 1;
- }
- else if((utf[1] & 0xC0) == 0x80)
- {
- if(utf[0] < 0xE0)
- {
- RC = (((utf[0] - 0xC0) << 6) + (utf[1] - 0x80));
- read = 2;
- }
- else if((utf[2] & 0xC0) == 0x80)
- {
- if(utf[0] < 0xF0)
- {
- RC = (((utf[0] - 0xE0) << 12) + ((utf[1] - 0x80) << 6) + (utf[2] - 0x80));
- read = 3;
- }
- else if((utf[3] & 0xC0) == 0x80)
- {
- if(utf[0] < 0xF8)
- {
- RC = (((utf[0] - 0xF0) << 18) + ((utf[1] - 0x80) << 12) + ((utf[2] - 0x80) << 6) + (utf[3] - 0x80));
- read = 4;
- }
- else if((utf[4] & 0xC0) == 0x80)
- {
- if(utf[0] < 0xFC)
- {
- RC = (((utf[0] - 0xF8) << 24) + ((utf[1] - 0x80) << 18) + ((utf[2] - 0x80) << 12) + ((utf[3] - 0x80) << 6) + (utf[4] - 0x80));
- read = 4;
- }
- else if((utf[5] & 0xC0) == 0x80)
- {
- if(utf[0] < 0xFE)
- {
- RC = (((utf[0] - 0xFC) << 30) + ((utf[1] - 0x80) << 24) + ((utf[2] - 0x80) << 18) + ((utf[3] - 0x80) << 12) + ((utf[4] - 0x80) << 6) + (utf[5] - 0x80));
- read = 5;
- }
- }
- }
- }
- }
- }
- }
- if(read)
- {
- if(bytes_read)
- {
- *bytes_read = read;
- }
- if(ucs4)
- {
- *ucs4 = RC;
- }
- return true;
- }
- return false;
- }
- extern "C" bool GetValidRangeUtf8String(const CHAR * const Buffer, size_t BufferSize, ptrdiff_t StartAt, ptrdiff_t * const ValidAt, size_t * const ValidLength)
- {
- ptrdiff_t ValidStart;
- ptrdiff_t Offset;
- ptrdiff_t EndOffset;
- size_t Length;
- unsigned char SequenceLength;
- unsigned char CharBuffer[8];
- EndOffset = (BufferSize) - StartAt;
- for(ValidStart = StartAt; ValidStart <= EndOffset; ValidStart++)
- {
- ::memset(CharBuffer, 0, 8);
- ::memcpy(CharBuffer, &Buffer[ValidStart], min(6, EndOffset - ValidStart));
- if(::uni_utf8toucs4(nullptr, CharBuffer, &SequenceLength))
- {
- break;
- }
- }
- if(ValidStart > EndOffset)
- {
- *ValidAt = 0;
- *ValidLength = 0;
- return false;
- }
- Length = 0;
- for(Offset = ValidStart; Offset <= EndOffset; Offset += SequenceLength)
- {
- ::memset(CharBuffer, 0, 8);
- ::memcpy(CharBuffer, &Buffer[Offset], min(6, EndOffset - Offset));
- if(!::uni_utf8toucs4(nullptr, CharBuffer, &SequenceLength))
- {
- break;
- }
- Length += SequenceLength;
- }
- *ValidAt = ValidStart;
- *ValidLength = Length;
- return true;
- }
- extern "C" size_t CopyValidUtf8StringFromBuffer(const CHAR * const Buffer, size_t BufferSize, ptrdiff_t StartAt, CHAR * const DstBuffer, size_t DstSize)
- {
- ptrdiff_t ValidAt;
- size_t ValidLength;
- ::memset(DstBuffer, 0, DstSize);
- if(::GetValidRangeUtf8String(Buffer, BufferSize, StartAt, &ValidAt, &ValidLength))
- {
- ::memcpy(DstBuffer, &Buffer[ValidAt], ValidLength);
- }
- else
- {
- ValidLength = 0;
- }
- return ValidLength;
- }
- extern "C" unsigned int __stdcall Utf8OutputThread(void * const Argument)
- {
- OVERLAPPED Overlapped;
- HANDLE Event;
- CHAR * Buffer;
- CHAR * ValidUtf8Buffer;
- DWORD WaitStatus;
- DWORD BytesInBuffer;
- DWORD PrevBytesInBuffer;
- ptrdiff_t BufferCursor;
- size_t BufferCopied;
- size_t Remaining;
- CHAR Remains[8];
- ((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread = FALSE;
- // 子プロセスからの標準出力を読み込むバッファー
- Buffer = reinterpret_cast<CHAR *>(::VirtualAlloc(nullptr, OUTPUT_BUFFER_BYTES, MEM_COMMIT, PAGE_READWRITE));
- if(Buffer == nullptr)
- {
- ::free(Argument);
- return 1;
- }
- // バッファーから有効なUTF-8とみなされたバイト列を格納するバッファー
- // このバッファーの内容は画面に出力が行われるごとに無効化される
- ValidUtf8Buffer = reinterpret_cast<CHAR *>(::VirtualAlloc(nullptr, OUTPUT_BUFFER_BYTES, MEM_COMMIT, PAGE_READWRITE));
- if(ValidUtf8Buffer == nullptr)
- {
- ::VirtualFree(Buffer, 0, MEM_RELEASE);
- ::free(Argument);
- return 1;
- }
- Event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
- ::memset(Remains, 0, sizeof(Remains));
- for(;;)
- {
- if(((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread)
- {
- break;
- }
- ::memset(&Overlapped, 0, sizeof(OVERLAPPED));
- ::memset(Buffer, 0, OUTPUT_BUFFER_BYTES);
- Overlapped.hEvent = Event; // これ要らないかも
- // 前回の読み込みで余ったバイトシーケンスをバッファーに書き込む
- Remaining = ::strlen(Remains);
- if(Remaining > 0)
- {
- ::memcpy(Buffer, Remains, sizeof(Remains));
- ::memset(Remains, 0, sizeof(Remains));
- }
- BufferCursor = Remaining;
- // 非同期読み込み開始
- if(!::ReadFileEx(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput, &Buffer[BufferCursor], OUTPUT_BUFFER_BYTES - (DWORD)BufferCursor, &Overlapped, Utf8OutputComplete))
- {
- // 非同期処理に失敗した場合はやり直し
- ::OutputDebugString(TEXT("ReadFileEx failed.\n"));
- ::Sleep(5000);
- continue;
- }
- for(;;)
- {
- if(((UTF8OUTPUTDESCRIPTOR*)Argument)->AbortThread)
- {
- // スレッドの終了要求が来たらI/Oを終了してループを脱出する
- ::CancelIo(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput);
- break;
- }
- if(::GetOverlappedResult(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput, &Overlapped, &BytesInBuffer, FALSE))
- {
- if((BytesInBuffer > 0) && (BytesInBuffer == PrevBytesInBuffer))
- {
- // これ以上ストリームにデータが無い場合はI/Oを終わらせる
- ::CancelIo(((struct UTF8OUTPUTDESCRIPTOR*)Argument)->ChildProcessInput);
- break;
- }
- PrevBytesInBuffer = BytesInBuffer;
- // 定期的にバッファを確認し、有効なUTF-8のバイトシーケンスを取り出す
- BufferCopied = ::CopyValidUtf8StringFromBuffer(Buffer, OUTPUT_BUFFER_BYTES, BufferCursor, ValidUtf8Buffer, OUTPUT_BUFFER_BYTES);
- if(BufferCopied > 0)
- {
- // 有効なバイトシーケンスが取り出せた場合は出力
- ::WriteConsoleAsUtf8(reinterpret_cast<char *>(ValidUtf8Buffer));
- BufferCursor += BufferCopied;
- }
- }
- WaitStatus = ::WaitForSingleObject(Event, 0);
- if(WaitStatus != WAIT_TIMEOUT)
- {
- // I/O完了ルーチンが呼ばれていた場合は現在のReadFileExを終了して新たにReadFileExを開始する
- ::memcpy(Remains, &Buffer[BufferCursor], Overlapped.InternalHigh - BufferCursor);
- break;
- }
- // 待機
- ::Sleep(50);
- }
- }
- ::CloseHandle(Event);
- ::VirtualFree(ValidUtf8Buffer, 0, MEM_RELEASE);
- ::VirtualFree(Buffer, 0, MEM_RELEASE);
- ::free(Argument);
- return 0;
- }
- extern "C" int _tmain(int const argc, const _TCHAR * const argv[])
- {
- SECURITY_ATTRIBUTES Attribute;
- STARTUPINFO Startup;
- PROCESS_INFORMATION Process;
- UTF8OUTPUTDESCRIPTOR *Utf8OutDesc, *Utf8ErrDesc;
- UTF8INPUTDESCRIPTOR *Utf8InDesc;
- HANDLE Utf8OutReader, Utf8ErrReader, Utf8InWriter;
- HANDLE Utf8Out, Utf8Err, Utf8In;
- HANDLE Utf8OutThread, Utf8ErrThread, Utf8InThread;
- const _TCHAR * NewArg;
- _TCHAR * ArgDup;
- BOOL BoolResult;
- unsigned int ThreadId;
- if(argc <= 1)
- {
- return 1;
- }
- NewArg = ::GetNextToken(::GetCommandLine());
- ::_tprintf_s(_T("Next token : %s\n"), NewArg);
- ArgDup = ::_tcsdup(NewArg);
- // パイプのハンドルを継承できるように属性を設定する
- ::memset(&Attribute, 0, sizeof(SECURITY_ATTRIBUTES));
- Attribute.nLength = sizeof(SECURITY_ATTRIBUTES);
- Attribute.bInheritHandle = TRUE;
- // 子プロセスの標準入出力に使用するパイプを作成する
- ::CreatePipe(&Utf8OutReader, &Utf8Out, &Attribute, 0);
- ::CreatePipe(&Utf8ErrReader, &Utf8Err, &Attribute, 0);
- ::CreatePipe(&Utf8In, &Utf8InWriter, &Attribute, 0);
- // 子プロセスの標準入出力を設定する
- ::memset(&Startup, 0, sizeof(STARTUPINFO));
- Startup.cb = sizeof(STARTUPINFO);
- Startup.dwFlags = STARTF_USESTDHANDLES;
- Startup.hStdOutput = Utf8Out;
- Startup.hStdError = Utf8Err;
- Startup.hStdInput = Utf8In;
- // 子プロセスを起動する
- BoolResult = ::CreateProcess(nullptr, ArgDup, nullptr, nullptr, TRUE, 0 /*CREATE_NO_WINDOW*/, nullptr, nullptr, &Startup, &Process);
- // この時点で子プロセスに渡したハンドルは必要ないので破棄する
- ::CloseHandle(Utf8In);
- ::CloseHandle(Utf8Out);
- ::CloseHandle(Utf8Err);
- if(BoolResult)
- {
- // スレッドのハンドルは不要なので閉じる
- ::CloseHandle(Process.hThread);
- Utf8OutDesc = reinterpret_cast<UTF8OUTPUTDESCRIPTOR*>(::malloc(sizeof(UTF8OUTPUTDESCRIPTOR)));
- Utf8OutDesc->ChildProcessInput = Utf8OutReader;
- Utf8OutDesc->IsStdError = false;
- Utf8ErrDesc = reinterpret_cast<UTF8OUTPUTDESCRIPTOR*>(::malloc(sizeof(UTF8OUTPUTDESCRIPTOR)));
- Utf8ErrDesc->ChildProcessInput = Utf8ErrReader;
- Utf8ErrDesc->IsStdError = true;
- Utf8InDesc = reinterpret_cast<UTF8INPUTDESCRIPTOR*>(::malloc(sizeof(UTF8INPUTDESCRIPTOR)));
- Utf8InDesc->ChildProcessOutput = Utf8InWriter;
- Utf8InDesc->Input = ::GetStdHandle(STD_INPUT_HANDLE);
- Utf8InDesc->AbortThread = true;
- // スレッドをハンドル毎に生成する
- Utf8OutThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8OutputThread, Utf8OutDesc, 0, &ThreadId));
- ::CloseHandle(Utf8OutThread);
- Utf8ErrThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8OutputThread, Utf8ErrDesc, 0, &ThreadId));
- ::CloseHandle(Utf8ErrThread);
- Utf8InThread = reinterpret_cast<HANDLE>(::_beginthreadex(nullptr, 0, Utf8InputThread, Utf8InDesc, 0, &ThreadId));
- ::CloseHandle(Utf8InThread);
- // プロセスが終わるまで待機
- ::WaitForSingleObject(Process.hProcess, INFINITE);
- // プロセスのハンドルを破棄する
- ::CloseHandle(Process.hProcess);
- // スレッドに終了を通知する
- Utf8OutDesc->AbortThread = true;
- Utf8ErrDesc->AbortThread = true;
- Utf8InDesc->AbortThread = true;
- }
- // 通信用のパイプを破棄する
- ::CloseHandle(Utf8InWriter);
- ::CloseHandle(Utf8OutReader);
- ::CloseHandle(Utf8ErrReader);
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement