/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: guistdio.inc,v $ * $Revision: 1.3.86.3 $ * * 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 * * 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 #ifdef _MSC_VER #pragma warning(pop) #endif #define _UNICODE #include #include #include #include #include //--------------------------------------------------------------------------- // Thread that reads from child process standard output pipe //--------------------------------------------------------------------------- #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 ) { const DWORD dwBufferSize = 256; wchar_t aBuffer[dwBufferSize]; DWORD dwRead = 0; HANDLE hWritePipe = (HANDLE)pParam; while (ReadConsoleW( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, dwBufferSize, &dwRead, NULL ) ) { BOOL fSuccess; DWORD dwWritten; fSuccess = WriteFile( hWritePipe, aBuffer, dwRead * 2, &dwWritten, NULL ); } 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; }