1/************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28#define UNICODE 29#define WIN32_LEAN_AND_MEAN 30#ifdef _MSC_VER 31#pragma warning(push,1) // disable warnings within system headers 32#endif 33#include <windows.h> 34#ifdef _MSC_VER 35#pragma warning(pop) 36#endif 37 38#define _UNICODE 39#include <tchar.h> 40 41#include <string.h> 42#include <stdlib.h> 43#include <systools/win32/uwinapi.h> 44 45#include <stdio.h> 46 47#ifdef UNOPKG 48 49DWORD passOutputToConsole(HANDLE readPipe, HANDLE console) 50{ 51 BYTE aBuffer[1024]; 52 DWORD dwRead = 0; 53 HANDLE hReadPipe = readPipe; 54 BOOL fSuccess; 55 DWORD dwWritten; 56 57 //Indicates that we read an odd number of bytes. That is, we only read half of the last 58 //wchar_t 59 bool bIncompleteWchar = false; 60 //fprintf, fwprintf will both send char data without the terminating zero. 61 //fwprintf converts the unicode string first. 62 //We expect here to receive unicode without the terminating zero. 63 //unopkg and the extension manager code MUST 64 //use dp_misc::writeConsole instead of using fprintf, etc. 65 66 DWORD dwToRead = sizeof(aBuffer); 67 BYTE * pBuffer = aBuffer; 68 while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) ) 69 { 70 //If the previous ReadFile call read an odd number of bytes, then the last one was 71 //put at the front of the buffer. We increase the number of read bytes by one to reflect 72 //that one byte. 73 if (bIncompleteWchar) 74 dwRead++; 75 //We must make sure that only complete wchar_t|s are written. WriteConsolse takes 76 //the number of wchar_t|s as argument. ReadFile, however, reads bytes. 77 bIncompleteWchar = dwRead % 2 ? true : false; 78 if (bIncompleteWchar) 79 { 80 //To test this case, give aBuffer a small odd size, e.g. aBuffer[3] 81 //The last byte, which is the incomplete wchar_t (half of it), will not be written. 82 fSuccess = WriteConsoleW( console, aBuffer, 83 (dwRead - 1) / 2, &dwWritten, NULL ); 84 85 //Move the last byte to the front of the buffer, so that it is the start of the 86 //next string 87 aBuffer[0] = aBuffer[dwRead - 1]; 88 89 //Make sure that ReadFile does not overwrite the first byte the next time 90 dwToRead = sizeof(aBuffer) - 1; 91 pBuffer = aBuffer + 1; 92 93 } 94 else 95 { //We have read an even number of bytes. Therefore, we do not put the last incomplete 96 //wchar_t at the front of the buffer. We will use the complete buffer the next time 97 //when ReadFile is called. 98 dwToRead = sizeof(aBuffer); 99 pBuffer = aBuffer; 100 fSuccess = WriteConsoleW( console, 101 aBuffer, dwRead / 2, &dwWritten, NULL ); 102 } 103 } 104 105 return 0; 106} 107 108#endif 109 110#ifdef UNOPKG 111DWORD WINAPI OutputThread( LPVOID pParam ) 112{ 113 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE )); 114} 115 116#else 117DWORD WINAPI OutputThread( LPVOID pParam ) 118{ 119 BYTE aBuffer[256]; 120 DWORD dwRead = 0; 121 HANDLE hReadPipe = (HANDLE)pParam; 122 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 123 { 124 BOOL fSuccess; 125 DWORD dwWritten; 126 127 fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); 128 } 129 130 return 0; 131} 132#endif 133//--------------------------------------------------------------------------- 134// Thread that reads from child process standard error pipe 135//--------------------------------------------------------------------------- 136 137#ifdef UNOPKG 138DWORD WINAPI ErrorThread( LPVOID pParam ) 139{ 140 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE )); 141} 142 143#else 144DWORD WINAPI ErrorThread( LPVOID pParam ) 145{ 146 BYTE aBuffer[256]; 147 DWORD dwRead = 0; 148 HANDLE hReadPipe = (HANDLE)pParam; 149 150 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 151 { 152 BOOL fSuccess; 153 DWORD dwWritten; 154 155 fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL ); 156 } 157 158 return 0; 159} 160#endif 161//--------------------------------------------------------------------------- 162// Thread that writes to child process standard input pipe 163//--------------------------------------------------------------------------- 164#ifdef UNOPKG 165 166DWORD WINAPI InputThread( LPVOID pParam ) 167{ 168 DWORD dwRead = 0; 169 HANDLE hWritePipe = (HANDLE)pParam; 170 171 //We need to read in the complete input until we encounter a new line before 172 //converting to Unicode. This is necessary because the input string can use 173 //characters of one, two, and more bytes. If the last character is not 174 //complete, then it will not be converted properly. 175 176 //Find out how a new line (0xd 0xa) looks like with the used code page. 177 //Characters may have one or multiple bytes and different byte ordering 178 //can be used (little and big endian); 179 int cNewLine = WideCharToMultiByte( 180 GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL); 181 char * mbBuff = new char[cNewLine]; 182 WideCharToMultiByte( 183 GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL); 184 185 const size_t dwBufferSize = 256; 186 char* readBuf = (char*) malloc(dwBufferSize); 187 int readAll = 0; 188 size_t curBufSize = dwBufferSize; 189 190 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), 191 readBuf + readAll, 192 curBufSize - readAll, &dwRead, NULL ) ) 193 { 194 readAll += dwRead; 195 int lastBufSize = curBufSize; 196 //Grow the buffer if necessary 197 if (readAll > curBufSize * 0.7) 198 { 199 curBufSize *= 2; 200 readBuf = (char *) realloc(readBuf, curBufSize); 201 } 202 203 //If the buffer was filled completely then 204 //there could be more input coming. But if we read from the console 205 //and the console input fits exactly in the buffer, then the next 206 //ReadFile would block until the users presses return, etc. 207 //Therefor we check if last character is a new line. 208 //To test this, set dwBufferSize to 4 and enter "no". This should produce 209 //4 bytes with most code pages. 210 if ( readAll == lastBufSize 211 && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0) 212 { 213 //The buffer was completely filled and the last byte(s) are no 214 //new line, so there is more to come. 215 continue; 216 } 217 //Obtain the size of the buffer for the converted string. 218 int sizeWBuf = MultiByteToWideChar( 219 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0); 220 221 wchar_t * wideBuf = new wchar_t[sizeWBuf]; 222 223 //Do the conversion. 224 MultiByteToWideChar( 225 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf); 226 227 BOOL fSuccess; 228 DWORD dwWritten; 229 fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL ); 230 delete[] wideBuf; 231 readAll = 0; 232 } 233 delete[] mbBuff; 234 free(readBuf); 235 return 0; 236} 237#else 238DWORD WINAPI InputThread( LPVOID pParam ) 239{ 240 BYTE aBuffer[256]; 241 DWORD dwRead = 0; 242 HANDLE hWritePipe = (HANDLE)pParam; 243 244 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) ) 245 { 246 BOOL fSuccess; 247 DWORD dwWritten; 248 249 fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL ); 250 } 251 252 return 0; 253} 254#endif 255 256//--------------------------------------------------------------------------- 257// Thread that waits until child process reached input idle 258//--------------------------------------------------------------------------- 259 260DWORD WINAPI WaitForUIThread( LPVOID pParam ) 261{ 262 HANDLE hProcess = (HANDLE)pParam; 263 264#ifndef UNOPKG 265 if ( !_tgetenv( TEXT("UNOPKG") ) ) 266 WaitForInputIdle( hProcess, INFINITE ); 267#endif 268 269 return 0; 270} 271 272 273//--------------------------------------------------------------------------- 274// Ctrl-Break handler that terminates the child process if Ctrl-C was pressed 275//--------------------------------------------------------------------------- 276 277HANDLE hTargetProcess = INVALID_HANDLE_VALUE; 278 279BOOL WINAPI CtrlBreakHandler( 280 DWORD // control signal type 281) 282{ 283 TerminateProcess( hTargetProcess, 255 ); 284 return TRUE; 285} 286 287 288//--------------------------------------------------------------------------- 289 290//--------------------------------------------------------------------------- 291 292#ifdef __MINGW32__ 293int main( int, char ** ) 294#else 295int _tmain( int, _TCHAR ** ) 296#endif 297{ 298 TCHAR szTargetFileName[MAX_PATH] = TEXT(""); 299 STARTUPINFO aStartupInfo; 300 PROCESS_INFORMATION aProcessInfo; 301 302 ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) ); 303 aStartupInfo.cb = sizeof(aStartupInfo); 304 aStartupInfo.dwFlags = STARTF_USESTDHANDLES; 305 306 // Create an output pipe where the write end is inheritable 307 308 HANDLE hOutputRead, hOutputWrite; 309 310 if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) ) 311 { 312 HANDLE hTemp; 313 314 DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 315 CloseHandle( hOutputWrite ); 316 hOutputWrite = hTemp; 317 318 aStartupInfo.hStdOutput = hOutputWrite; 319 } 320 321 // Create an error pipe where the write end is inheritable 322 323 HANDLE hErrorRead, hErrorWrite; 324 325 if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) ) 326 { 327 HANDLE hTemp; 328 329 DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 330 CloseHandle( hErrorWrite ); 331 hErrorWrite = hTemp; 332 333 aStartupInfo.hStdError = hErrorWrite; 334 } 335 336 // Create an input pipe where the read end is inheritable 337 338 HANDLE hInputRead, hInputWrite; 339 340 if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) ) 341 { 342 HANDLE hTemp; 343 344 DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS ); 345 CloseHandle( hInputRead ); 346 hInputRead = hTemp; 347 348 aStartupInfo.hStdInput = hInputRead; 349 } 350 351 // Get image path with same name but with .exe extension 352 353 TCHAR szModuleFileName[MAX_PATH]; 354 355 GetModuleFileName( NULL, szModuleFileName, MAX_PATH ); 356 _TCHAR *lpLastDot = _tcsrchr( szModuleFileName, '.' ); 357 if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) ) 358 { 359 size_t len = lpLastDot - szModuleFileName; 360 _tcsncpy( szTargetFileName, szModuleFileName, len ); 361 _tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len ); 362 } 363 364 // Create process with same command line, environment and stdio handles which 365 // are directed to the created pipes 366 367 BOOL fSuccess = CreateProcess( 368 szTargetFileName, 369 GetCommandLine(), 370 NULL, 371 NULL, 372 TRUE, 373 0, 374 NULL, 375 NULL, 376 &aStartupInfo, 377 &aProcessInfo ); 378 379 if ( fSuccess ) 380 { 381 // These pipe ends are inherited by the child process and no longer used 382 CloseHandle( hOutputWrite ); 383 CloseHandle( hErrorWrite ); 384 CloseHandle( hInputRead ); 385 386 // Set the Ctrl-Break handler 387 hTargetProcess = aProcessInfo.hProcess; 388 SetConsoleCtrlHandler( CtrlBreakHandler, TRUE ); 389 390 // Create threads that redirect remote pipe io to current process's console stdio 391 392 DWORD dwOutputThreadId, dwErrorThreadId, dwInputThreadId; 393 394 HANDLE hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId ); 395 HANDLE hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId ); 396 HANDLE hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId ); 397 398 // Create thread that wait until child process entered input idle 399 400 DWORD dwWaitForUIThreadId; 401 HANDLE hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId ); 402 403 DWORD dwWaitResult; 404 HANDLE hObjects[] = 405 { 406 hTargetProcess, 407 hWaitForUIThread, 408 hOutputThread, 409 hErrorThread 410 }; 411 412 #ifdef UNOPKG 413 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE ); 414 #else 415 bool bDetach = false; 416 int nOpenPipes = 2; 417 do 418 { 419 dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE ); 420 421 switch ( dwWaitResult ) 422 { 423 case WAIT_OBJECT_0: // The child process has terminated 424 case WAIT_OBJECT_0 + 1: // The child process entered input idle 425 bDetach = true; 426 break; 427 case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed 428 case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed 429 bDetach = --nOpenPipes <= 0; 430 break; 431 default: // Something went wrong 432 bDetach = true; 433 break; 434 } 435 } while( !bDetach ); 436 437#endif 438 439 CloseHandle( hOutputThread ); 440 CloseHandle( hErrorThread ); 441 CloseHandle( hInputThread ); 442 CloseHandle( hWaitForUIThread ); 443 444 DWORD dwExitCode = 0; 445 GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode ); 446 CloseHandle( aProcessInfo.hProcess ); 447 CloseHandle( aProcessInfo.hThread ); 448 449 return dwExitCode; 450 } 451 452 return -1; 453} 454