Update ooo320-m1
[ooovba.git] / desktop / win32 / source / guistdio / guistdio.inc
blob024b456efa99ab78f9fa958e2252a6523e374f2a
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  * 
5  * Copyright 2008 by Sun Microsystems, Inc.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * $RCSfile: guistdio.inc,v $
10  * $Revision: 1.3.86.3 $
11  *
12  * This file is part of OpenOffice.org.
13  *
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.
17  *
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).
23  *
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.
28  *
29  ************************************************************************/
31 #define UNICODE
32 #define WIN32_LEAN_AND_MEAN
33 #ifdef _MSC_VER
34 #pragma warning(push,1) // disable warnings within system headers
35 #endif
36 #include <windows.h>
37 #ifdef _MSC_VER
38 #pragma warning(pop)
39 #endif
41 #define _UNICODE
42 #include <tchar.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <systools/win32/uwinapi.h>
48 #include <stdio.h>
50 #ifdef UNOPKG
52 DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
54         BYTE aBuffer[1024];
55         DWORD dwRead = 0;
56         HANDLE hReadPipe = readPipe;
57         BOOL fSuccess;
58         DWORD dwWritten;
60         //Indicates that we read an odd number of bytes. That is, we only read half of the last
61         //wchar_t
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.
68         
69         DWORD dwToRead = sizeof(aBuffer);
70         BYTE * pBuffer = aBuffer;
71         while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) )
72         {
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
75                 //that one byte.
76                 if (bIncompleteWchar)
77                         dwRead++;
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;
81                 if (bIncompleteWchar)
82                 {
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 );
87                         
88                         //Move the last byte to the front of the buffer, so that it is the start of the 
89                         //next string
90                         aBuffer[0] = aBuffer[dwRead - 1];
91                         
92                         //Make sure that ReadFile does not overwrite the first byte the next time
93                         dwToRead = sizeof(aBuffer) - 1;
94                         pBuffer = aBuffer + 1;
95                         
96                 }
97                 else
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);
102                         pBuffer = aBuffer;
103                         fSuccess = WriteConsoleW( console, 
104                                 aBuffer, dwRead / 2, &dwWritten, NULL );
105                 }
106         }
108         return 0;
111 #endif
113 #ifdef UNOPKG
114 DWORD WINAPI OutputThread( LPVOID pParam )
116         return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE ));
119 #else
120 DWORD WINAPI OutputThread( LPVOID pParam )
122         BYTE    aBuffer[256];
123         DWORD   dwRead = 0;
124         HANDLE  hReadPipe = (HANDLE)pParam;
125         while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
126         {
127                 BOOL    fSuccess;
128                 DWORD   dwWritten;
130                 fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
131         }
133         return 0;
135 #endif
136 //---------------------------------------------------------------------------
137 // Thread that reads from child process standard error pipe
138 //---------------------------------------------------------------------------
140 #ifdef UNOPKG
141 DWORD WINAPI ErrorThread( LPVOID pParam )
143         return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE ));
146 #else
147 DWORD WINAPI ErrorThread( LPVOID pParam )
149         BYTE    aBuffer[256];
150         DWORD   dwRead = 0;
151         HANDLE  hReadPipe = (HANDLE)pParam;
153         while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
154         {
155                 BOOL    fSuccess;
156                 DWORD   dwWritten;
158                 fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
159         }
161         return 0;
163 #endif
164 //---------------------------------------------------------------------------
165 // Thread that writes to child process standard input pipe
166 //---------------------------------------------------------------------------
167 #ifdef UNOPKG
169 DWORD WINAPI InputThread( LPVOID pParam )
171         DWORD   dwRead = 0;
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];
185     WideCharToMultiByte(
186         GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL);
187     
188     const size_t dwBufferSize = 256;
189     char* readBuf = (char*) malloc(dwBufferSize);
190     int readAll = 0;
191     size_t curBufSize = dwBufferSize;
192     
193     while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
194                       readBuf + readAll,
195                       curBufSize - readAll, &dwRead, NULL ) )
196         {
197         readAll += dwRead;
198         int lastBufSize = curBufSize;
199         //Grow the buffer if necessary
200         if (readAll > curBufSize * 0.7)
201         {
202             curBufSize *= 2;
203             readBuf = (char *) realloc(readBuf, curBufSize);
204         }
205         
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)
215         {
216             //The buffer was completely filled and the last byte(s) are no
217             //new line, so there is more to come.
218             continue;
219         }
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];
226         //Do the conversion.
227         MultiByteToWideChar(
228             GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
229         
230         BOOL    fSuccess;
231                 DWORD   dwWritten;
232         fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL );
233         delete[] wideBuf;
234         readAll = 0;
235         }
236     delete[] mbBuff;
237     free(readBuf);
238         return 0;
240 #else
241 DWORD WINAPI InputThread( LPVOID pParam )
243         BYTE    aBuffer[256];
244         DWORD   dwRead = 0;
245         HANDLE  hWritePipe = (HANDLE)pParam;
247         while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
248         {
249                 BOOL    fSuccess;
250                 DWORD   dwWritten;
252                 fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
253         }
255         return 0;
257 #endif
259 //---------------------------------------------------------------------------
260 // Thread that waits until child process reached input idle
261 //---------------------------------------------------------------------------
263 DWORD WINAPI WaitForUIThread( LPVOID pParam )
265         HANDLE  hProcess = (HANDLE)pParam;
267 #ifndef UNOPKG
268         if ( !_tgetenv( TEXT("UNOPKG") ) )
269                 WaitForInputIdle( hProcess, INFINITE );
270 #endif
272         return 0;
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 );
287         return TRUE;
291 //---------------------------------------------------------------------------
293 //---------------------------------------------------------------------------
295 #ifdef __MINGW32__
296 int main( int, char ** )
297 #else
298 int _tmain( int, _TCHAR ** )
299 #endif
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;
312         
313         if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) )
314         {
315                 HANDLE  hTemp;
317                 DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
318                 CloseHandle( hOutputWrite );
319                 hOutputWrite = hTemp;
321                 aStartupInfo.hStdOutput = hOutputWrite;
322         }
324         // Create an error pipe where the write end is inheritable
326         HANDLE  hErrorRead, hErrorWrite;
327         
328         if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) )
329         {
330                 HANDLE  hTemp;
332                 DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
333                 CloseHandle( hErrorWrite );
334                 hErrorWrite = hTemp;
336                 aStartupInfo.hStdError = hErrorWrite;
337         }
339         // Create an input pipe where the read end is inheritable
341         HANDLE  hInputRead, hInputWrite;
343         if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) )
344         {
345                 HANDLE  hTemp;
347                 DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
348                 CloseHandle( hInputRead );
349                 hInputRead = hTemp;
351                 aStartupInfo.hStdInput = hInputRead;
352         }
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") ) )
361         {
362                 size_t len = lpLastDot - szModuleFileName;
363                 _tcsncpy( szTargetFileName, szModuleFileName, len );
364                 _tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len );
365         }
367         // Create process with same command line, environment and stdio handles which
368         // are directed to the created pipes
370         BOOL    fSuccess = CreateProcess(
371                 szTargetFileName,
372                 GetCommandLine(),
373                 NULL,
374                 NULL,
375                 TRUE,
376                 0,
377                 NULL,
378                 NULL,
379                 &aStartupInfo,
380                 &aProcessInfo );
382         if ( fSuccess )
383         {
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 );
406                 DWORD   dwWaitResult;
407                 HANDLE  hObjects[] = 
408                         {
409                                 hTargetProcess,
410                                 hWaitForUIThread,
411                                 hOutputThread,
412                                 hErrorThread
413                         };
415  #ifdef UNOPKG       
416         dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE );
417  #else        
418                 bool    bDetach = false;
419                 int             nOpenPipes = 2;
420                 do
421                 {
422                         dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE );
424                         switch ( dwWaitResult )
425                         {
426                         case WAIT_OBJECT_0:     // The child process has terminated
427                         case WAIT_OBJECT_0 + 1: // The child process entered input idle
428                                 bDetach = true;
429                                 break;
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;
433                                 break;
434                         default: // Something went wrong
435                                 bDetach = true;
436                                 break;
437                         }
438                 } while( !bDetach );
439             
440 #endif
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 );
452                 return dwExitCode;
453         }
455         return -1;