diff options
Diffstat (limited to 'desktop/win32/source/guistdio/guistdio.inc')
-rw-r--r-- | desktop/win32/source/guistdio/guistdio.inc | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/desktop/win32/source/guistdio/guistdio.inc b/desktop/win32/source/guistdio/guistdio.inc new file mode 100644 index 000000000000..7f2bbe9002d8 --- /dev/null +++ b/desktop/win32/source/guistdio/guistdio.inc @@ -0,0 +1,453 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#define UNICODE +#define WIN32_LEAN_AND_MEAN +#ifdef _MSC_VER +#pragma warning(push,1) // disable warnings within system headers +#endif +#include <windows.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#define _UNICODE +#include <tchar.h> + +#include <string.h> +#include <stdlib.h> +#include <systools/win32/uwinapi.h> + +#include <stdio.h> + +#ifdef UNOPKG + +DWORD passOutputToConsole(HANDLE readPipe, HANDLE console) +{ + BYTE aBuffer[1024]; + DWORD dwRead = 0; + HANDLE hReadPipe = readPipe; + BOOL fSuccess; + DWORD dwWritten; + + //Indicates that we read an odd number of bytes. That is, we only read half of the last + //wchar_t + bool bIncompleteWchar = false; + //fprintf, fwprintf will both send char data without the terminating zero. + //fwprintf converts the unicode string first. + //We expect here to receive unicode without the terminating zero. + //unopkg and the extension manager code MUST + //use dp_misc::writeConsole instead of using fprintf, etc. + + DWORD dwToRead = sizeof(aBuffer); + BYTE * pBuffer = aBuffer; + while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) ) + { + //If the previous ReadFile call read an odd number of bytes, then the last one was + //put at the front of the buffer. We increase the number of read bytes by one to reflect + //that one byte. + if (bIncompleteWchar) + dwRead++; + //We must make sure that only complete wchar_t|s are written. WriteConsolse takes + //the number of wchar_t|s as argument. ReadFile, however, reads bytes. + bIncompleteWchar = dwRead % 2 ? true : false; + if (bIncompleteWchar) + { + //To test this case, give aBuffer a small odd size, e.g. aBuffer[3] + //The last byte, which is the incomplete wchar_t (half of it), will not be written. + fSuccess = WriteConsoleW( console, aBuffer, + (dwRead - 1) / 2, &dwWritten, NULL ); + + //Move the last byte to the front of the buffer, so that it is the start of the + //next string + aBuffer[0] = aBuffer[dwRead - 1]; + + //Make sure that ReadFile does not overwrite the first byte the next time + dwToRead = sizeof(aBuffer) - 1; + pBuffer = aBuffer + 1; + + } + else + { //We have read an even number of bytes. Therefore, we do not put the last incomplete + //wchar_t at the front of the buffer. We will use the complete buffer the next time + //when ReadFile is called. + dwToRead = sizeof(aBuffer); + pBuffer = aBuffer; + fSuccess = WriteConsoleW( console, + aBuffer, dwRead / 2, &dwWritten, NULL ); + } + } + + return 0; +} + +#endif + +#ifdef UNOPKG +DWORD WINAPI OutputThread( LPVOID pParam ) +{ + return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE )); +} + +#else +DWORD WINAPI OutputThread( LPVOID pParam ) +{ + BYTE aBuffer[256]; + DWORD dwRead = 0; + HANDLE hReadPipe = (HANDLE)pParam; + while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) + { + BOOL fSuccess; + DWORD dwWritten; + + fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); + } + + return 0; +} +#endif +//--------------------------------------------------------------------------- +// Thread that reads from child process standard error pipe +//--------------------------------------------------------------------------- + +#ifdef UNOPKG +DWORD WINAPI ErrorThread( LPVOID pParam ) +{ + return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE )); +} + +#else +DWORD WINAPI ErrorThread( LPVOID pParam ) +{ + BYTE aBuffer[256]; + DWORD dwRead = 0; + HANDLE hReadPipe = (HANDLE)pParam; + + while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) + { + BOOL fSuccess; + DWORD dwWritten; + + fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); + } + + return 0; +} +#endif +//--------------------------------------------------------------------------- +// Thread that writes to child process standard input pipe +//--------------------------------------------------------------------------- +#ifdef UNOPKG + +DWORD WINAPI InputThread( LPVOID pParam ) +{ + DWORD dwRead = 0; + HANDLE hWritePipe = (HANDLE)pParam; + + //We need to read in the complete input until we encounter a new line before + //converting to Unicode. This is necessary because the input string can use + //characters of one, two, and more bytes. If the last character is not + //complete, then it will not be converted properly. + + //Find out how a new line (0xd 0xa) looks like with the used code page. + //Characters may have one or multiple bytes and different byte ordering + //can be used (little and big endian); + int cNewLine = WideCharToMultiByte( + GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL); + char * mbBuff = new char[cNewLine]; + WideCharToMultiByte( + GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL); + + const size_t dwBufferSize = 256; + char* readBuf = (char*) malloc(dwBufferSize); + int readAll = 0; + size_t curBufSize = dwBufferSize; + + while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), + readBuf + readAll, + curBufSize - readAll, &dwRead, NULL ) ) + { + readAll += dwRead; + int lastBufSize = curBufSize; + //Grow the buffer if necessary + if (readAll > curBufSize * 0.7) + { + curBufSize *= 2; + readBuf = (char *) realloc(readBuf, curBufSize); + } + + //If the buffer was filled completely then + //there could be more input coming. But if we read from the console + //and the console input fits exactly in the buffer, then the next + //ReadFile would block until the users presses return, etc. + //Therefor we check if last character is a new line. + //To test this, set dwBufferSize to 4 and enter "no". This should produce + //4 bytes with most code pages. + if ( readAll == lastBufSize + && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0) + { + //The buffer was completely filled and the last byte(s) are no + //new line, so there is more to come. + continue; + } + //Obtain the size of the buffer for the converted string. + int sizeWBuf = MultiByteToWideChar( + GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0); + + wchar_t * wideBuf = new wchar_t[sizeWBuf]; + + //Do the conversion. + MultiByteToWideChar( + GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf); + + BOOL fSuccess; + DWORD dwWritten; + fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL ); + delete[] wideBuf; + readAll = 0; + } + delete[] mbBuff; + free(readBuf); + return 0; +} +#else +DWORD WINAPI InputThread( LPVOID pParam ) +{ + BYTE aBuffer[256]; + DWORD dwRead = 0; + HANDLE hWritePipe = (HANDLE)pParam; + + while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) + { + BOOL fSuccess; + DWORD dwWritten; + + fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL ); + } + + return 0; +} +#endif + +//--------------------------------------------------------------------------- +// Thread that waits until child process reached input idle +//--------------------------------------------------------------------------- + +DWORD WINAPI WaitForUIThread( LPVOID pParam ) +{ + HANDLE hProcess = (HANDLE)pParam; + +#ifndef UNOPKG + if ( !_tgetenv( TEXT("UNOPKG") ) ) + WaitForInputIdle( hProcess, INFINITE ); +#endif + + return 0; +} + + +//--------------------------------------------------------------------------- +// Ctrl-Break handler that terminates the child process if Ctrl-C was pressed +//--------------------------------------------------------------------------- + +HANDLE hTargetProcess = INVALID_HANDLE_VALUE; + +BOOL WINAPI CtrlBreakHandler( + DWORD // control signal type +) +{ + TerminateProcess( hTargetProcess, 255 ); + return TRUE; +} + + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +#ifdef __MINGW32__ +int main( int, char ** ) +#else +int _tmain( int, _TCHAR ** ) +#endif +{ + TCHAR szTargetFileName[MAX_PATH] = TEXT(""); + STARTUPINFO aStartupInfo; + PROCESS_INFORMATION aProcessInfo; + + ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) ); + aStartupInfo.cb = sizeof(aStartupInfo); + aStartupInfo.dwFlags = STARTF_USESTDHANDLES; + + // Create an output pipe where the write end is inheritable + + HANDLE hOutputRead, hOutputWrite; + + if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) ) + { + HANDLE hTemp; + + DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); + CloseHandle( hOutputWrite ); + hOutputWrite = hTemp; + + aStartupInfo.hStdOutput = hOutputWrite; + } + + // Create an error pipe where the write end is inheritable + + HANDLE hErrorRead, hErrorWrite; + + if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) ) + { + HANDLE hTemp; + + DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); + CloseHandle( hErrorWrite ); + hErrorWrite = hTemp; + + aStartupInfo.hStdError = hErrorWrite; + } + + // Create an input pipe where the read end is inheritable + + HANDLE hInputRead, hInputWrite; + + if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) ) + { + HANDLE hTemp; + + DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); + CloseHandle( hInputRead ); + hInputRead = hTemp; + + aStartupInfo.hStdInput = hInputRead; + } + + // Get image path with same name but with .exe extension + + TCHAR szModuleFileName[MAX_PATH]; + + GetModuleFileName( NULL, szModuleFileName, MAX_PATH ); + _TCHAR *lpLastDot = _tcsrchr( szModuleFileName, '.' ); + if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) ) + { + size_t len = lpLastDot - szModuleFileName; + _tcsncpy( szTargetFileName, szModuleFileName, len ); + _tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len ); + } + + // Create process with same command line, environment and stdio handles which + // are directed to the created pipes + + BOOL fSuccess = CreateProcess( + szTargetFileName, + GetCommandLine(), + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &aStartupInfo, + &aProcessInfo ); + + if ( fSuccess ) + { + // These pipe ends are inherited by the child process and no longer used + CloseHandle( hOutputWrite ); + CloseHandle( hErrorWrite ); + CloseHandle( hInputRead ); + + // Set the Ctrl-Break handler + hTargetProcess = aProcessInfo.hProcess; + SetConsoleCtrlHandler( CtrlBreakHandler, TRUE ); + + // Create threads that redirect remote pipe io to current process's console stdio + + DWORD dwOutputThreadId, dwErrorThreadId, dwInputThreadId; + + HANDLE hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId ); + HANDLE hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId ); + HANDLE hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId ); + + // Create thread that wait until child process entered input idle + + DWORD dwWaitForUIThreadId; + HANDLE hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId ); + + DWORD dwWaitResult; + HANDLE hObjects[] = + { + hTargetProcess, + hWaitForUIThread, + hOutputThread, + hErrorThread + }; + + #ifdef UNOPKG + dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE ); + #else + bool bDetach = false; + int nOpenPipes = 2; + do + { + dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE ); + + switch ( dwWaitResult ) + { + case WAIT_OBJECT_0: // The child process has terminated + case WAIT_OBJECT_0 + 1: // The child process entered input idle + bDetach = true; + break; + case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed + case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed + bDetach = --nOpenPipes <= 0; + break; + default: // Something went wrong + bDetach = true; + break; + } + } while( !bDetach ); + +#endif + + CloseHandle( hOutputThread ); + CloseHandle( hErrorThread ); + CloseHandle( hInputThread ); + CloseHandle( hWaitForUIThread ); + + DWORD dwExitCode = 0; + GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode ); + CloseHandle( aProcessInfo.hProcess ); + CloseHandle( aProcessInfo.hThread ); + + return dwExitCode; + } + + return -1; +} |