Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / desktop / win32 / source / guistdio / guistdio.inc
blobeb1b9cda1639285323f5189b65a1a9200b49bc3f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
20 #define WIN32_LEAN_AND_MEAN
21 #include <windows.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <systools/win32/uwinapi.h>
27 #include <stdio.h>
28 #include <sal/macros.h>
30 #ifdef UNOPKG
32 DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
34     BYTE aBuffer[1024];
35     DWORD dwRead = 0;
36     HANDLE hReadPipe = readPipe;
37     DWORD dwWritten;
39     //Indicates that we read an odd number of bytes. That is, we only read half of the last
40     //wchar_t
41     bool bIncompleteWchar = false;
42     //fprintf, fwprintf will both send char data without the terminating zero.
43     //fwprintf converts the unicode string first.
44     //We expect here to receive unicode without the terminating zero.
45     //unopkg and the extension manager code MUST
46     //use dp_misc::writeConsole instead of using fprintf, etc.
48     DWORD dwToRead = sizeof(aBuffer);
49     BYTE * pBuffer = aBuffer;
50     while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, nullptr ) )
51     {
52         //If the previous ReadFile call read an odd number of bytes, then the last one was
53         //put at the front of the buffer. We increase the number of read bytes by one to reflect
54         //that one byte.
55         if (bIncompleteWchar)
56             dwRead++;
57         //We must make sure that only complete wchar_t|s are written. WriteConsolse takes
58         //the number of wchar_t|s as argument. ReadFile, however, reads bytes.
59         bIncompleteWchar = (dwRead % 2) != 0;
60         if (bIncompleteWchar)
61         {
62             //To test this case, give aBuffer a small odd size, e.g. aBuffer[3]
63             //The last byte, which is the incomplete wchar_t (half of it), will not be written.
64             (void) WriteConsoleW( console, aBuffer,
65                 (dwRead - 1) / 2, &dwWritten, nullptr );
67             //Move the last byte to the front of the buffer, so that it is the start of the
68             //next string
69             aBuffer[0] = aBuffer[dwRead - 1];
71             //Make sure that ReadFile does not overwrite the first byte the next time
72             dwToRead = sizeof(aBuffer) - 1;
73             pBuffer = aBuffer + 1;
75         }
76         else
77         {   //We have read an even number of bytes. Therefore, we do not put the last incomplete
78             //wchar_t at the front of the buffer. We will use the complete buffer the next time
79             //when ReadFile is called.
80             dwToRead = sizeof(aBuffer);
81             pBuffer = aBuffer;
82             (void) WriteConsoleW( console,
83                 aBuffer, dwRead / 2, &dwWritten, nullptr );
84         }
85     }
87     return 0;
90 #endif
92 #ifdef UNOPKG
93 DWORD WINAPI OutputThread( LPVOID pParam )
95     return passOutputToConsole(static_cast<HANDLE>(pParam), GetStdHandle( STD_OUTPUT_HANDLE ));
98 #else
99 DWORD WINAPI OutputThread( LPVOID pParam )
101     BYTE    aBuffer[256];
102     DWORD   dwRead = 0;
103     HANDLE  hReadPipe = (HANDLE)pParam;
104     while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
105     {
106         DWORD   dwWritten;
108         (void) WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
109     }
111     return 0;
113 #endif
115 // Thread that reads from child process standard error pipe
118 #ifdef UNOPKG
119 DWORD WINAPI ErrorThread( LPVOID pParam )
121     return passOutputToConsole(static_cast<HANDLE>(pParam), GetStdHandle( STD_ERROR_HANDLE ));
124 #else
125 DWORD WINAPI ErrorThread( LPVOID pParam )
127     BYTE    aBuffer[256];
128     DWORD   dwRead = 0;
129     HANDLE  hReadPipe = (HANDLE)pParam;
131     while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
132     {
133         DWORD   dwWritten;
135         (void) WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
136     }
138     return 0;
140 #endif
142 // Thread that writes to child process standard input pipe
144 #ifdef UNOPKG
146 DWORD WINAPI InputThread( LPVOID pParam )
148     DWORD   dwRead = 0;
149     HANDLE  hWritePipe = static_cast<HANDLE>(pParam);
151     //We need to read in the complete input until we encounter a new line before
152     //converting to Unicode. This is necessary because the input string can use
153     //characters of one, two, and more bytes. If the last character is not
154     //complete, then it will not be converted properly.
156     //Find out how a new line (0xd 0xa) looks like with the used code page.
157     //Characters may have one or multiple bytes and different byte ordering
158     //can be used (little and big endian);
159     int cNewLine = WideCharToMultiByte(
160         GetConsoleCP(), 0, L"\r\n", 2, nullptr, 0, nullptr, nullptr);
161     char * mbBuff = new char[cNewLine];
162     WideCharToMultiByte(
163         GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, nullptr, nullptr);
165     const DWORD dwBufferSize = 256;
166     char* readBuf = static_cast<char*>(malloc(dwBufferSize));
167     int readAll = 0;
168     DWORD curBufSize = dwBufferSize;
170     while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
171                       readBuf + readAll,
172                       curBufSize - readAll, &dwRead, nullptr ) )
173     {
174         readAll += dwRead;
175         int lastBufSize = curBufSize;
176         //Grow the buffer if necessary
177         if (readAll > curBufSize * 0.7)
178         {
179             curBufSize *= 2;
180             readBuf = static_cast<char *>(realloc(readBuf, curBufSize));
181         }
183         //If the buffer was filled completely then
184         //there could be more input coming. But if we read from the console
185         //and the console input fits exactly in the buffer, then the next
186         //ReadFile would block until the users presses return, etc.
187         //Therefore we check if last character is a new line.
188         //To test this, set dwBufferSize to 4 and enter "no". This should produce
189         //4 bytes with most code pages.
190         if ( readAll == lastBufSize
191              && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0)
192         {
193             //The buffer was completely filled and the last byte(s) are no
194             //new line, so there is more to come.
195             continue;
196         }
197         //Obtain the size of the buffer for the converted string.
198         int sizeWBuf = MultiByteToWideChar(
199             GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, nullptr, 0);
201         wchar_t * wideBuf = new wchar_t[sizeWBuf];
203         //Do the conversion.
204         MultiByteToWideChar(
205             GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
207         DWORD   dwWritten;
208         (void)WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, nullptr );
210         delete[] wideBuf;
211         readAll = 0;
212     }
213     delete[] mbBuff;
214     free(readBuf);
215     return 0;
217 #else
218 DWORD WINAPI InputThread( LPVOID pParam )
220     BYTE    aBuffer[256];
221     DWORD   dwRead = 0;
222     HANDLE  hWritePipe = (HANDLE)pParam;
224     while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
225     {
226         DWORD dwWritten;
227         (void) WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
228     }
230     return 0;
232 #endif
235 // Thread that waits until child process reached input idle
238 DWORD WINAPI WaitForUIThread( LPVOID pParam )
240 #ifndef UNOPKG
241     HANDLE  hProcess = (HANDLE)pParam;
243     if ( !wgetenv( L"UNOPKG" ) )
244         WaitForInputIdle( hProcess, INFINITE );
245 #else
246     (void) pParam;
247 #endif
249     return 0;
254 // Ctrl-Break handler that terminates the child process if Ctrl-C was pressed
257 HANDLE  hTargetProcess = INVALID_HANDLE_VALUE;
259 BOOL WINAPI CtrlBreakHandler(
260   DWORD   //  control signal type
263     TerminateProcess( hTargetProcess, 255 );
264     return TRUE;
267 int wmain( int, wchar_t** )
269     WCHAR               szTargetFileName[MAX_PATH] = L"";
270     STARTUPINFOW        aStartupInfo;
271     PROCESS_INFORMATION aProcessInfo;
273     ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
274     aStartupInfo.cb = sizeof(aStartupInfo);
275     aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
277     // Create an output pipe where the write end is inheritable
279     HANDLE  hOutputRead, hOutputWrite;
281     if ( CreatePipe( &hOutputRead, &hOutputWrite, nullptr, 0 ) )
282     {
283         HANDLE  hTemp;
285         DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
286         CloseHandle( hOutputWrite );
287         hOutputWrite = hTemp;
289         aStartupInfo.hStdOutput = hOutputWrite;
290     }
292     // Create an error pipe where the write end is inheritable
294     HANDLE  hErrorRead, hErrorWrite;
296     if ( CreatePipe( &hErrorRead, &hErrorWrite, nullptr, 0 ) )
297     {
298         HANDLE  hTemp;
300         DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
301         CloseHandle( hErrorWrite );
302         hErrorWrite = hTemp;
304         aStartupInfo.hStdError = hErrorWrite;
305     }
307     // Create an input pipe where the read end is inheritable
309     HANDLE  hInputRead, hInputWrite;
311     if ( CreatePipe( &hInputRead, &hInputWrite, nullptr, 0 ) )
312     {
313         HANDLE  hTemp;
315         DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
316         CloseHandle( hInputRead );
317         hInputRead = hTemp;
319         aStartupInfo.hStdInput = hInputRead;
320     }
322     // Get image path with same name but with .exe extension
324     WCHAR szModuleFileName[MAX_PATH];
326     GetModuleFileNameW( nullptr, szModuleFileName, MAX_PATH );
327     WCHAR  *lpLastDot = wcsrchr( szModuleFileName, '.' );
328     if ( lpLastDot && 0 == wcsicmp( lpLastDot, L".COM" ) )
329     {
330         size_t len = lpLastDot - szModuleFileName;
331         wcsncpy( szTargetFileName, szModuleFileName, len );
332         wcsncpy( szTargetFileName + len, L".EXE", SAL_N_ELEMENTS(szTargetFileName) - len );
333     }
335     // Create process with same command line, environment and stdio handles which
336     // are directed to the created pipes
338     BOOL fSuccess = CreateProcessW(
339         szTargetFileName,
340         GetCommandLineW(),
341         nullptr,
342         nullptr,
343         TRUE,
344         0,
345         nullptr,
346         nullptr,
347         &aStartupInfo,
348         &aProcessInfo );
350     if ( fSuccess )
351     {
352         // These pipe ends are inherited by the child process and no longer used
353         CloseHandle( hOutputWrite );
354         CloseHandle( hErrorWrite );
355         CloseHandle( hInputRead );
357         // Set the Ctrl-Break handler
358         hTargetProcess = aProcessInfo.hProcess;
359         SetConsoleCtrlHandler( CtrlBreakHandler, TRUE );
361         // Create threads that redirect remote pipe io to current process's console stdio
363         DWORD   dwOutputThreadId, dwErrorThreadId, dwInputThreadId;
365         HANDLE  hOutputThread = CreateThread( nullptr, 0, OutputThread, static_cast<LPVOID>(hOutputRead), 0, &dwOutputThreadId );
366         HANDLE  hErrorThread = CreateThread( nullptr, 0, OutputThread, static_cast<LPVOID>(hErrorRead), 0, &dwErrorThreadId );
367         HANDLE  hInputThread = CreateThread( nullptr, 0, InputThread, static_cast<LPVOID>(hInputWrite), 0, &dwInputThreadId );
369         // Create thread that wait until child process entered input idle
371         DWORD   dwWaitForUIThreadId;
372         HANDLE  hWaitForUIThread = CreateThread( nullptr, 0, WaitForUIThread, static_cast<LPVOID>(aProcessInfo.hProcess), 0, &dwWaitForUIThreadId );
374         HANDLE  hObjects[] =
375             {
376                 hTargetProcess,
377                 hWaitForUIThread,
378                 hOutputThread,
379                 hErrorThread
380             };
382  #ifdef UNOPKG
383         WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, TRUE, INFINITE );
384  #else
385         bool    bDetach = false;
386         int     nOpenPipes = 2;
387         do
388         {
389             DWORD dwWaitResult = WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, FALSE, INFINITE );
391             switch ( dwWaitResult )
392             {
393             case WAIT_OBJECT_0: // The child process has terminated
394             case WAIT_OBJECT_0 + 1: // The child process entered input idle
395                 bDetach = true;
396                 break;
397             case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed
398             case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed
399                 bDetach = --nOpenPipes <= 0;
400                 break;
401             default: // Something went wrong
402                 bDetach = true;
403                 break;
404             }
405         } while( !bDetach );
407 #endif
409         CloseHandle( hOutputThread );
410         CloseHandle( hErrorThread );
411         CloseHandle( hInputThread );
412         CloseHandle( hWaitForUIThread );
414         DWORD   dwExitCode = 0;
415         GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );
416         CloseHandle( aProcessInfo.hProcess );
417         CloseHandle( aProcessInfo.hThread );
419         return dwExitCode;
420     }
422     return -1;
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */