bump product version to 4.1.6.2
[LibreOffice.git] / odk / source / unoapploader / win / unoapploader.c
blob830a38df6079a81a1c20c5236641f2212c677e66
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <process.h>
25 #if defined _MSC_VER
26 #pragma warning(push, 1)
27 #endif
28 #include <windows.h>
29 #if defined _MSC_VER
30 #pragma warning(pop)
31 #endif
33 #include "cppuhelper/findsofficepath.h"
34 #include "sal/types.h"
36 #define MY_LENGTH(s) (sizeof (s) / sizeof *(s) - 1)
38 char const* getPath();
39 char* createCommandLine( char* lpCmdLine );
40 FILE* getErrorFile( int create );
41 void writeError( const char* errstr );
42 void closeErrorFile();
45 * The main function implements a loader for applications which use UNO.
47 * <p>This code runs on the Windows platform only.</p>
49 * <p>The main function detects a UNO installation on the system and adds the
50 * program directory of the UNO installation to the PATH environment variable.
51 * After that, the application process is loaded and started, whereby the
52 * new process inherits the environment of the calling process, including
53 * the modified PATH environment variable. The application's executable name
54 * must be the same as the name of this executable, prefixed by '_'.</p>
56 * <p>A UNO installation can be specified by the user by setting the UNO_PATH
57 * environment variable to the program directory of the UNO installation.
58 * If no installation is specified by the user, the default installation on
59 * the system will be taken. The default installation is read from the
60 * default value of the key "Software\LibreOffice\UNO\InstallPath" from the
61 * root key HKEY_CURRENT_USER in the Windows Registry. If this key is missing,
62 * the key is read from the root key HKEY_LOCAL_MACHINE.</p>
64 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
65 LPSTR lpCmdLine, int nCmdShow )
67 const char* ENVVARNAME = "PATH";
68 const char* PATHSEPARATOR = ";";
70 char const* path = NULL;
71 char path2[MAX_PATH];
72 char* value = NULL;
73 char* envstr = NULL;
74 char* cmdline = NULL;
75 int size;
76 STARTUPINFO startup_info;
77 PROCESS_INFORMATION process_info;
78 BOOL bCreate;
80 (void) hInstance; /* unused */
81 (void) hPrevInstance; /* unused */
82 (void) nCmdShow; /* unused */
84 /* get the path of the UNO installation */
85 path = getPath();
87 if ( path != NULL )
89 wchar_t cmd[
90 MY_LENGTH(L"\"") + MAX_PATH +
91 MY_LENGTH(L"\\unoinfo.exe\" c++")];
92 /* hopefully does not overflow */
93 int pathsize;
94 SECURITY_ATTRIBUTES sec;
95 HANDLE temp;
96 HANDLE stdoutRead;
97 HANDLE stdoutWrite;
98 STARTUPINFOW startinfo;
99 PROCESS_INFORMATION procinfo;
100 int ret;
101 cmd[0] = L'"';
102 pathsize = MultiByteToWideChar(CP_ACP, 0, path, -1, cmd + 1, MAX_PATH);
103 if (pathsize == 0) {
104 writeError("Error: MultiByteToWideChar failed!\n");
105 closeErrorFile();
106 return 1;
108 if (wcschr(cmd + 1, L'"') != NULL) {
109 writeError("Error: bad characters in UNO installation path!\n");
110 closeErrorFile();
111 return 1;
113 wcscpy(
114 cmd + pathsize,
115 (L"\\unoinfo.exe\" c++" +
116 (pathsize == 1 || cmd[pathsize - 1] != L'\\' ? 0 : 1)));
117 sec.nLength = sizeof (SECURITY_ATTRIBUTES);
118 sec.lpSecurityDescriptor = NULL;
119 sec.bInheritHandle = TRUE;
120 if (CreatePipe(&temp, &stdoutWrite, &sec, 0) == 0 ||
121 DuplicateHandle(
122 GetCurrentProcess(), temp, GetCurrentProcess(), &stdoutRead, 0,
123 FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == 0)
125 writeError("Error: CreatePipe/DuplicateHandle failed!\n");
126 closeErrorFile();
127 return 1;
129 memset(&startinfo, 0, sizeof (STARTUPINFOW));
130 startinfo.cb = sizeof (STARTUPINFOW);
131 startinfo.lpDesktop = L"";
132 startinfo.dwFlags = STARTF_USESTDHANDLES;
133 startinfo.hStdOutput = stdoutWrite;
134 ret = CreateProcessW(
135 NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &procinfo);
136 if (ret != 0) {
137 char * buf = NULL;
138 size_t n = 1000;
139 size_t k = 0;
140 DWORD exitcode;
141 int path2size;
142 CloseHandle(stdoutWrite);
143 CloseHandle(procinfo.hThread);
144 for (;;) {
145 DWORD m;
146 buf = realloc(buf, n);
147 if (buf == NULL) {
148 writeError(
149 "Error: out of memory reading unoinfo output!\n");
150 closeErrorFile();
151 return 1;
153 if (!ReadFile(stdoutRead, buf + k, n - k, &m, NULL))
155 DWORD err = GetLastError();
156 if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) {
157 break;
159 writeError("Error: cannot read unoinfo output!\n");
160 closeErrorFile();
161 return 1;
163 if (m == 0) {
164 break;
166 k += m;
167 if (k >= n) {
168 if (n >= SAL_MAX_SIZE / 2) {
169 writeError(
170 "Error: out of memory reading unoinfo output!\n");
171 closeErrorFile();
172 return 1;
174 n *= 2;
177 if ((k & 1) == 1) {
178 writeError("Error: bad unoinfo output!\n");
179 closeErrorFile();
180 return 1;
182 CloseHandle(stdoutRead);
183 if (!GetExitCodeProcess(procinfo.hProcess, &exitcode) ||
184 exitcode != 0)
186 writeError("Error: executing unoinfo failed!\n");
187 closeErrorFile();
188 return 1;
190 if (k == 0) {
191 path2size = 0;
192 } else {
193 path2size = WideCharToMultiByte(
194 CP_ACP, 0, (wchar_t *) buf, k / 2, path2, MAX_PATH - 1,
195 NULL, NULL);
196 if (path2size == 0) {
197 writeError("Error: converting unoinfo output failed!\n");
198 closeErrorFile();
199 return 1;
202 path2[path2size] = '\0';
203 path = path2;
204 } else {
205 if (GetLastError() != ERROR_FILE_NOT_FOUND) {
206 writeError("Error: calling unoinfo failed!\n");
207 closeErrorFile();
208 return 1;
210 CloseHandle(stdoutRead);
211 CloseHandle(stdoutWrite);
214 /* get the value of the PATH environment variable */
215 value = getenv( ENVVARNAME );
218 * add the UNO installation path to the PATH environment variable;
219 * note that this only affects the environment variable of the current
220 * process, the command processor's environment is not changed
222 size = strlen( ENVVARNAME ) + strlen( "=" ) + strlen( path ) + 1;
223 if ( value != NULL )
224 size += strlen( PATHSEPARATOR ) + strlen( value );
225 envstr = (char*) malloc( size );
226 strcpy( envstr, ENVVARNAME );
227 strcat( envstr, "=" );
228 strcat( envstr, path );
229 if ( value != NULL )
231 strcat( envstr, PATHSEPARATOR );
232 strcat( envstr, value );
234 _putenv( envstr );
235 free( envstr );
237 else
239 writeError( "Warning: no UNO installation found!\n" );
242 /* create the command line for the application process */
243 cmdline = createCommandLine( lpCmdLine );
244 if ( cmdline == NULL )
246 writeError( "Error: cannot create command line!\n" );
247 closeErrorFile();
248 return 1;
251 /* create the application process */
252 memset( &startup_info, 0, sizeof( STARTUPINFO ) );
253 startup_info.cb = sizeof( STARTUPINFO );
254 bCreate = CreateProcess( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL,
255 &startup_info, &process_info );
256 free( cmdline );
257 if ( !bCreate )
259 writeError( "Error: cannot create process!\n" );
260 closeErrorFile();
261 return 1;
264 /* close the error file */
265 closeErrorFile();
267 return 0;
271 * Gets the path of a UNO installation.
273 * @return the installation path or NULL, if no installation was specified or
274 * found, or if an error occurred
276 char const* getPath()
278 char const* path = cppuhelper_detail_findSofficePath();
280 if ( path == NULL )
281 writeError( "Warning: getting path from Windows Registry failed!\n" );
283 return path;
287 * Creates the command line for the application process including the absolute
288 * path of the executable.
290 * <p>The application's executable file name is the name of this executable
291 * prefixed by '_'.</p>
293 * @param appendix specifies the command line for the application excluding
294 * the executable name
296 * @return the command line for the application process or NULL, if an error
297 * occurred
299 char* createCommandLine( char* appendix )
301 const char* CMDPREFIX = "_";
302 const char* DQUOTE = "\"";
303 const char* SPACE = " ";
305 char* cmdline = NULL;
307 char cmdname[ _MAX_PATH ];
308 char drive[ _MAX_DRIVE ];
309 char dir[ _MAX_PATH ];
310 char base[ _MAX_FNAME ];
311 char newbase[ _MAX_FNAME ];
312 char ext[ _MAX_EXT ];
314 /* get the absolute path of the executable file */
315 if ( GetModuleFileName( NULL, cmdname, sizeof( cmdname ) ) )
317 /* prefix the executable file name by '_' */
318 _splitpath( cmdname, drive, dir, base, ext );
319 strcpy( newbase, CMDPREFIX );
320 strcat( newbase, base );
321 _makepath( cmdname, drive, dir, newbase, ext );
323 /* create the command line */
324 cmdline = (char*) malloc( strlen( DQUOTE ) + strlen( cmdname ) +
325 strlen ( DQUOTE ) + strlen( SPACE ) + strlen( appendix ) + 1 );
326 strcpy( cmdline, DQUOTE );
327 strcat( cmdline, cmdname );
328 strcat( cmdline, DQUOTE );
329 strcat( cmdline, SPACE );
330 strcat( cmdline, appendix );
333 return cmdline;
337 * Gets the pointer to the error file.
339 * <p>The error file will only be created, if create != 0.</p>
341 * <p>The error file has the name <executable file name>-error.log and is
342 * created in the same directory as the executable file. If this fails,
343 * the error file is created in the directory designated for temporary files.
344 * </p>
346 * @param create specifies, if the error file should be created (create != 0)
348 * @return the pointer to the open error file or NULL, if no error file is
349 * open or can be created
351 FILE* getErrorFile( int create )
353 const char* MODE = "w";
354 const char* BASEPOSTFIX = "-error";
355 const char* EXTENSION = ".log";
357 static FILE* ferr = NULL;
359 char fname[ _MAX_PATH ];
360 char drive[ _MAX_DRIVE ];
361 char dir[ _MAX_PATH ];
362 char base[ _MAX_FNAME ];
363 char newbase[ _MAX_FNAME ];
364 char ext[ _MAX_EXT ];
366 if ( ferr == NULL && create )
368 /* get the absolute path of the executable file */
369 if ( GetModuleFileName( NULL, fname, sizeof( fname ) ) )
371 /* create error file in the directory of the executable file */
372 _splitpath( fname, drive, dir, base, ext );
373 strcpy( newbase, base );
374 strcat( newbase, BASEPOSTFIX );
375 _makepath( fname, drive, dir, newbase, EXTENSION );
376 ferr = fopen( fname, MODE );
378 if ( ferr == NULL )
380 /* create error file in the temp directory */
381 GetTempPath( sizeof( fname ), fname );
382 strcat( fname, newbase );
383 strcat( fname, EXTENSION );
384 ferr = fopen( fname, MODE );
389 return ferr;
393 * Writes an error message to the error file.
395 * @param errstr specifies the error message
397 void writeError( const char* errstr )
399 FILE* ferr = getErrorFile( 1 );
400 if ( ferr != NULL )
402 fprintf( ferr, errstr );
403 fflush( ferr );
408 * Closes the error file.
410 void closeErrorFile()
412 FILE* ferr = getErrorFile( 0 );
413 if ( ferr != NULL )
414 fclose( ferr );
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */