1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: guistdio.inc,v $
10 * $Revision: 1.3.86.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 #define WIN32_LEAN_AND_MEAN
34 #pragma warning(push,1) // disable warnings within system headers
46 #include <systools/win32/uwinapi.h>
52 DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
56 HANDLE hReadPipe = readPipe;
60 //Indicates that we read an odd number of bytes. That is, we only read half of the last
62 bool bIncompleteWchar = false;
63 //fprintf, fwprintf will both send char data without the terminating zero.
64 //fwprintf converts the unicode string first.
65 //We expect here to receive unicode without the terminating zero.
66 //unopkg and the extension manager code MUST
67 //use dp_misc::writeConsole instead of using fprintf, etc.
69 DWORD dwToRead = sizeof(aBuffer);
70 BYTE * pBuffer = aBuffer;
71 while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) )
73 //If the previous ReadFile call read an odd number of bytes, then the last one was
74 //put at the front of the buffer. We increase the number of read bytes by one to reflect
78 //We must make sure that only complete wchar_t|s are written. WriteConsolse takes
79 //the number of wchar_t|s as argument. ReadFile, however, reads bytes.
80 bIncompleteWchar = dwRead % 2 ? true : false;
83 //To test this case, give aBuffer a small odd size, e.g. aBuffer[3]
84 //The last byte, which is the incomplete wchar_t (half of it), will not be written.
85 fSuccess = WriteConsoleW( console, aBuffer,
86 (dwRead - 1) / 2, &dwWritten, NULL );
88 //Move the last byte to the front of the buffer, so that it is the start of the
90 aBuffer[0] = aBuffer[dwRead - 1];
92 //Make sure that ReadFile does not overwrite the first byte the next time
93 dwToRead = sizeof(aBuffer) - 1;
94 pBuffer = aBuffer + 1;
98 { //We have read an even number of bytes. Therefore, we do not put the last incomplete
99 //wchar_t at the front of the buffer. We will use the complete buffer the next time
100 //when ReadFile is called.
101 dwToRead = sizeof(aBuffer);
103 fSuccess = WriteConsoleW( console,
104 aBuffer, dwRead / 2, &dwWritten, NULL );
114 DWORD WINAPI OutputThread( LPVOID pParam )
116 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE ));
120 DWORD WINAPI OutputThread( LPVOID pParam )
124 HANDLE hReadPipe = (HANDLE)pParam;
125 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
130 fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
136 //---------------------------------------------------------------------------
137 // Thread that reads from child process standard error pipe
138 //---------------------------------------------------------------------------
141 DWORD WINAPI ErrorThread( LPVOID pParam )
143 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE ));
147 DWORD WINAPI ErrorThread( LPVOID pParam )
151 HANDLE hReadPipe = (HANDLE)pParam;
153 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
158 fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
164 //---------------------------------------------------------------------------
165 // Thread that writes to child process standard input pipe
166 //---------------------------------------------------------------------------
169 DWORD WINAPI InputThread( LPVOID pParam )
172 HANDLE hWritePipe = (HANDLE)pParam;
174 //We need to read in the complete input until we encounter a new line before
175 //converting to Unicode. This is necessary because the input string can use
176 //characters of one, two, and more bytes. If the last character is not
177 //complete, then it will not be converted properly.
179 //Find out how a new line (0xd 0xa) looks like with the used code page.
180 //Characters may have one or multiple bytes and different byte ordering
181 //can be used (little and big endian);
182 int cNewLine = WideCharToMultiByte(
183 GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL);
184 char * mbBuff = new char[cNewLine];
186 GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL);
188 const size_t dwBufferSize = 256;
189 char* readBuf = (char*) malloc(dwBufferSize);
191 size_t curBufSize = dwBufferSize;
193 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
195 curBufSize - readAll, &dwRead, NULL ) )
198 int lastBufSize = curBufSize;
199 //Grow the buffer if necessary
200 if (readAll > curBufSize * 0.7)
203 readBuf = (char *) realloc(readBuf, curBufSize);
206 //If the buffer was filled completely then
207 //there could be more input coming. But if we read from the console
208 //and the console input fits exactly in the buffer, then the next
209 //ReadFile would block until the users presses return, etc.
210 //Therefor we check if last character is a new line.
211 //To test this, set dwBufferSize to 4 and enter "no". This should produce
212 //4 bytes with most code pages.
213 if ( readAll == lastBufSize
214 && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0)
216 //The buffer was completely filled and the last byte(s) are no
217 //new line, so there is more to come.
220 //Obtain the size of the buffer for the converted string.
221 int sizeWBuf = MultiByteToWideChar(
222 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0);
224 wchar_t * wideBuf = new wchar_t[sizeWBuf];
228 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
232 fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL );
241 DWORD WINAPI InputThread( LPVOID pParam )
245 HANDLE hWritePipe = (HANDLE)pParam;
247 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
252 fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
259 //---------------------------------------------------------------------------
260 // Thread that waits until child process reached input idle
261 //---------------------------------------------------------------------------
263 DWORD WINAPI WaitForUIThread( LPVOID pParam )
265 HANDLE hProcess = (HANDLE)pParam;
268 if ( !_tgetenv( TEXT("UNOPKG") ) )
269 WaitForInputIdle( hProcess, INFINITE );
276 //---------------------------------------------------------------------------
277 // Ctrl-Break handler that terminates the child process if Ctrl-C was pressed
278 //---------------------------------------------------------------------------
280 HANDLE hTargetProcess = INVALID_HANDLE_VALUE;
282 BOOL WINAPI CtrlBreakHandler(
283 DWORD // control signal type
286 TerminateProcess( hTargetProcess, 255 );
291 //---------------------------------------------------------------------------
293 //---------------------------------------------------------------------------
296 int main( int, char ** )
298 int _tmain( int, _TCHAR ** )
301 TCHAR szTargetFileName[MAX_PATH] = TEXT("");
302 STARTUPINFO aStartupInfo;
303 PROCESS_INFORMATION aProcessInfo;
305 ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
306 aStartupInfo.cb = sizeof(aStartupInfo);
307 aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
309 // Create an output pipe where the write end is inheritable
311 HANDLE hOutputRead, hOutputWrite;
313 if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) )
317 DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
318 CloseHandle( hOutputWrite );
319 hOutputWrite = hTemp;
321 aStartupInfo.hStdOutput = hOutputWrite;
324 // Create an error pipe where the write end is inheritable
326 HANDLE hErrorRead, hErrorWrite;
328 if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) )
332 DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
333 CloseHandle( hErrorWrite );
336 aStartupInfo.hStdError = hErrorWrite;
339 // Create an input pipe where the read end is inheritable
341 HANDLE hInputRead, hInputWrite;
343 if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) )
347 DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
348 CloseHandle( hInputRead );
351 aStartupInfo.hStdInput = hInputRead;
354 // Get image path with same name but with .exe extension
356 TCHAR szModuleFileName[MAX_PATH];
358 GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
359 _TCHAR *lpLastDot = _tcsrchr( szModuleFileName, '.' );
360 if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) )
362 size_t len = lpLastDot - szModuleFileName;
363 _tcsncpy( szTargetFileName, szModuleFileName, len );
364 _tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len );
367 // Create process with same command line, environment and stdio handles which
368 // are directed to the created pipes
370 BOOL fSuccess = CreateProcess(
384 // These pipe ends are inherited by the child process and no longer used
385 CloseHandle( hOutputWrite );
386 CloseHandle( hErrorWrite );
387 CloseHandle( hInputRead );
389 // Set the Ctrl-Break handler
390 hTargetProcess = aProcessInfo.hProcess;
391 SetConsoleCtrlHandler( CtrlBreakHandler, TRUE );
393 // Create threads that redirect remote pipe io to current process's console stdio
395 DWORD dwOutputThreadId, dwErrorThreadId, dwInputThreadId;
397 HANDLE hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId );
398 HANDLE hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId );
399 HANDLE hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId );
401 // Create thread that wait until child process entered input idle
403 DWORD dwWaitForUIThreadId;
404 HANDLE hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId );
416 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE );
418 bool bDetach = false;
422 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE );
424 switch ( dwWaitResult )
426 case WAIT_OBJECT_0: // The child process has terminated
427 case WAIT_OBJECT_0 + 1: // The child process entered input idle
430 case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed
431 case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed
432 bDetach = --nOpenPipes <= 0;
434 default: // Something went wrong
442 CloseHandle( hOutputThread );
443 CloseHandle( hErrorThread );
444 CloseHandle( hInputThread );
445 CloseHandle( hWaitForUIThread );
447 DWORD dwExitCode = 0;
448 GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );
449 CloseHandle( aProcessInfo.hProcess );
450 CloseHandle( aProcessInfo.hThread );