1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #define WIN32_LEAN_AND_MEAN
23 #pragma warning(push,1) // disable warnings within system headers
35 #include <systools/win32/uwinapi.h>
38 #include <sal/macros.h>
42 DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
46 HANDLE hReadPipe = readPipe;
49 //Indicates that we read an odd number of bytes. That is, we only read half of the last
51 bool bIncompleteWchar = false;
52 //fprintf, fwprintf will both send char data without the terminating zero.
53 //fwprintf converts the unicode string first.
54 //We expect here to receive unicode without the terminating zero.
55 //unopkg and the extension manager code MUST
56 //use dp_misc::writeConsole instead of using fprintf, etc.
58 DWORD dwToRead = sizeof(aBuffer);
59 BYTE * pBuffer = aBuffer;
60 while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) )
62 //If the previous ReadFile call read an odd number of bytes, then the last one was
63 //put at the front of the buffer. We increase the number of read bytes by one to reflect
67 //We must make sure that only complete wchar_t|s are written. WriteConsolse takes
68 //the number of wchar_t|s as argument. ReadFile, however, reads bytes.
69 bIncompleteWchar = (dwRead % 2) ? true : false;
72 //To test this case, give aBuffer a small odd size, e.g. aBuffer[3]
73 //The last byte, which is the incomplete wchar_t (half of it), will not be written.
74 (void) WriteConsoleW( console, aBuffer,
75 (dwRead - 1) / 2, &dwWritten, NULL );
77 //Move the last byte to the front of the buffer, so that it is the start of the
79 aBuffer[0] = aBuffer[dwRead - 1];
81 //Make sure that ReadFile does not overwrite the first byte the next time
82 dwToRead = sizeof(aBuffer) - 1;
83 pBuffer = aBuffer + 1;
87 { //We have read an even number of bytes. Therefore, we do not put the last incomplete
88 //wchar_t at the front of the buffer. We will use the complete buffer the next time
89 //when ReadFile is called.
90 dwToRead = sizeof(aBuffer);
92 (void) WriteConsoleW( console,
93 aBuffer, dwRead / 2, &dwWritten, NULL );
103 DWORD WINAPI OutputThread( LPVOID pParam )
105 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE ));
109 DWORD WINAPI OutputThread( LPVOID pParam )
113 HANDLE hReadPipe = (HANDLE)pParam;
114 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
118 (void) WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
125 // Thread that reads from child process standard error pipe
129 DWORD WINAPI ErrorThread( LPVOID pParam )
131 return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE ));
135 DWORD WINAPI ErrorThread( LPVOID pParam )
139 HANDLE hReadPipe = (HANDLE)pParam;
141 while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
145 (void) WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
152 // Thread that writes to child process standard input pipe
156 DWORD WINAPI InputThread( LPVOID pParam )
159 HANDLE hWritePipe = (HANDLE)pParam;
161 //We need to read in the complete input until we encounter a new line before
162 //converting to Unicode. This is necessary because the input string can use
163 //characters of one, two, and more bytes. If the last character is not
164 //complete, then it will not be converted properly.
166 //Find out how a new line (0xd 0xa) looks like with the used code page.
167 //Characters may have one or multiple bytes and different byte ordering
168 //can be used (little and big endian);
169 int cNewLine = WideCharToMultiByte(
170 GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL);
171 char * mbBuff = new char[cNewLine];
173 GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL);
175 const DWORD dwBufferSize = 256;
176 char* readBuf = (char*) malloc(dwBufferSize);
178 DWORD curBufSize = dwBufferSize;
180 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
182 curBufSize - readAll, &dwRead, NULL ) )
185 int lastBufSize = curBufSize;
186 //Grow the buffer if necessary
187 if (readAll > curBufSize * 0.7)
190 readBuf = (char *) realloc(readBuf, curBufSize);
193 //If the buffer was filled completely then
194 //there could be more input coming. But if we read from the console
195 //and the console input fits exactly in the buffer, then the next
196 //ReadFile would block until the users presses return, etc.
197 //Therefore we check if last character is a new line.
198 //To test this, set dwBufferSize to 4 and enter "no". This should produce
199 //4 bytes with most code pages.
200 if ( readAll == lastBufSize
201 && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0)
203 //The buffer was completely filled and the last byte(s) are no
204 //new line, so there is more to come.
207 //Obtain the size of the buffer for the converted string.
208 int sizeWBuf = MultiByteToWideChar(
209 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0);
211 wchar_t * wideBuf = new wchar_t[sizeWBuf];
215 GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
218 (void)WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL );
228 DWORD WINAPI InputThread( LPVOID pParam )
232 HANDLE hWritePipe = (HANDLE)pParam;
234 while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
237 (void) WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
245 // Thread that waits until child process reached input idle
248 DWORD WINAPI WaitForUIThread( LPVOID pParam )
251 HANDLE hProcess = (HANDLE)pParam;
253 if ( !_tgetenv( TEXT("UNOPKG") ) )
254 WaitForInputIdle( hProcess, INFINITE );
264 // Ctrl-Break handler that terminates the child process if Ctrl-C was pressed
267 HANDLE hTargetProcess = INVALID_HANDLE_VALUE;
269 BOOL WINAPI CtrlBreakHandler(
270 DWORD // control signal type
273 TerminateProcess( hTargetProcess, 255 );
283 int main( int, char ** )
285 int _tmain( int, _TCHAR ** )
288 TCHAR szTargetFileName[MAX_PATH] = TEXT("");
289 STARTUPINFO aStartupInfo;
290 PROCESS_INFORMATION aProcessInfo;
292 ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
293 aStartupInfo.cb = sizeof(aStartupInfo);
294 aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
296 // Create an output pipe where the write end is inheritable
298 HANDLE hOutputRead, hOutputWrite;
300 if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) )
304 DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
305 CloseHandle( hOutputWrite );
306 hOutputWrite = hTemp;
308 aStartupInfo.hStdOutput = hOutputWrite;
311 // Create an error pipe where the write end is inheritable
313 HANDLE hErrorRead, hErrorWrite;
315 if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) )
319 DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
320 CloseHandle( hErrorWrite );
323 aStartupInfo.hStdError = hErrorWrite;
326 // Create an input pipe where the read end is inheritable
328 HANDLE hInputRead, hInputWrite;
330 if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) )
334 DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
335 CloseHandle( hInputRead );
338 aStartupInfo.hStdInput = hInputRead;
341 // Get image path with same name but with .exe extension
343 TCHAR szModuleFileName[MAX_PATH];
345 GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
346 _TCHAR *lpLastDot = _tcsrchr( szModuleFileName, '.' );
347 if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) )
349 size_t len = lpLastDot - szModuleFileName;
350 _tcsncpy( szTargetFileName, szModuleFileName, len );
351 _tcsncpy( szTargetFileName + len, _T(".EXE"), SAL_N_ELEMENTS(szTargetFileName) - len );
354 // Create process with same command line, environment and stdio handles which
355 // are directed to the created pipes
357 BOOL fSuccess = CreateProcess(
371 // These pipe ends are inherited by the child process and no longer used
372 CloseHandle( hOutputWrite );
373 CloseHandle( hErrorWrite );
374 CloseHandle( hInputRead );
376 // Set the Ctrl-Break handler
377 hTargetProcess = aProcessInfo.hProcess;
378 SetConsoleCtrlHandler( CtrlBreakHandler, TRUE );
380 // Create threads that redirect remote pipe io to current process's console stdio
382 DWORD dwOutputThreadId, dwErrorThreadId, dwInputThreadId;
384 HANDLE hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId );
385 HANDLE hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId );
386 HANDLE hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId );
388 // Create thread that wait until child process entered input idle
390 DWORD dwWaitForUIThreadId;
391 HANDLE hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId );
402 WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, TRUE, INFINITE );
404 bool bDetach = false;
408 DWORD dwWaitResult = WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, FALSE, INFINITE );
410 switch ( dwWaitResult )
412 case WAIT_OBJECT_0: // The child process has terminated
413 case WAIT_OBJECT_0 + 1: // The child process entered input idle
416 case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed
417 case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed
418 bDetach = --nOpenPipes <= 0;
420 default: // Something went wrong
428 CloseHandle( hOutputThread );
429 CloseHandle( hErrorThread );
430 CloseHandle( hInputThread );
431 CloseHandle( hWaitForUIThread );
433 DWORD dwExitCode = 0;
434 GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );
435 CloseHandle( aProcessInfo.hProcess );
436 CloseHandle( aProcessInfo.hThread );
444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */