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 .
26 #if !defined WIN32_LEAN_AND_MEAN
27 # define WIN32_LEAN_AND_MEAN
31 #include <cppuhelper/findsofficepath.h>
32 #include <sal/types.h>
34 #define MY_SIZE(s) (sizeof (s) / sizeof *(s))
35 #define MY_LENGTH(s) (MY_SIZE(s) - 1)
37 static wchar_t* getPath(void);
38 static wchar_t* createCommandLine( wchar_t const * lpCmdLine
);
39 static FILE* getErrorFile( int create
);
40 static void writeError( const char* errstr
);
41 static void closeErrorFile(void);
44 * The main function implements a loader for applications which use UNO.
46 * <p>This code runs on the Windows platform only.</p>
48 * <p>The main function detects a UNO installation on the system and adds the
49 * program directory of the UNO installation to the PATH environment variable.
50 * After that, the application process is loaded and started, whereby the
51 * new process inherits the environment of the calling process, including
52 * the modified PATH environment variable. The application's executable name
53 * must be the same as the name of this executable, prefixed by '_'.</p>
55 * <p>A UNO installation can be specified by the user by setting the UNO_PATH
56 * environment variable to the program directory of the UNO installation.
57 * If no installation is specified by the user, the default installation on
58 * the system will be taken. The default installation is read from the
59 * default value of the key "Software\LibreOffice\UNO\InstallPath" from the
60 * root key HKEY_CURRENT_USER in the Windows Registry. If this key is missing,
61 * the key is read from the root key HKEY_LOCAL_MACHINE.</p>
63 int WINAPI
wWinMain( HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
64 LPWSTR lpCmdLine
, int nCmdShow
)
66 (void) hInstance
; /* unused */
67 (void) hPrevInstance
; /* unused */
68 (void) nCmdShow
; /* unused */
70 /* get the path of the UNO installation */
71 wchar_t* path
= getPath();
76 MY_LENGTH(L
"\"") + MAX_PATH
+
77 MY_LENGTH(L
"\\unoinfo.exe\" c++")];
78 /* hopefully does not overflow */
80 wcscpy(cmd
+ 1, path
);
81 if (wcschr(cmd
+ 1, L
'"') != NULL
) {
83 writeError("Error: bad characters in UNO installation path!\n");
87 size_t pathsize
= wcslen(cmd
);
90 &L
"\\unoinfo.exe\" c++"[
91 pathsize
== 1 || cmd
[pathsize
- 1] != L
'\\' ? 0 : 1]);
92 SECURITY_ATTRIBUTES sec
;
93 sec
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
94 sec
.lpSecurityDescriptor
= NULL
;
95 sec
.bInheritHandle
= TRUE
;
99 if (CreatePipe(&temp
, &stdoutWrite
, &sec
, 0) == 0 ||
101 GetCurrentProcess(), temp
, GetCurrentProcess(), &stdoutRead
, 0,
102 FALSE
, DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
) == 0)
105 writeError("Error: CreatePipe/DuplicateHandle failed!\n");
109 STARTUPINFOW startinfo
;
110 PROCESS_INFORMATION procinfo
;
111 memset(&startinfo
, 0, sizeof(startinfo
));
112 startinfo
.cb
= sizeof(startinfo
);
113 startinfo
.lpDesktop
= L
"";
114 startinfo
.dwFlags
= STARTF_USESTDHANDLES
;
115 startinfo
.hStdOutput
= stdoutWrite
;
116 BOOL ret
= CreateProcessW(
117 NULL
, cmd
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &startinfo
, &procinfo
);
119 // Release result of GetPath()
127 CloseHandle(stdoutWrite
);
128 CloseHandle(procinfo
.hThread
);
131 tmp
= realloc(buf
, n
);
135 "Error: out of memory reading unoinfo output!\n");
140 if (!ReadFile(stdoutRead
, buf
+ k
, n
- k
, &m
, NULL
))
142 DWORD err
= GetLastError();
143 if (err
== ERROR_HANDLE_EOF
|| err
== ERROR_BROKEN_PIPE
) {
146 writeError("Error: cannot read unoinfo output!\n");
155 if (n
>= MAXDWORD
/ 2) {
157 "Error: out of memory reading unoinfo output!\n");
165 writeError("Error: bad unoinfo output!\n");
169 CloseHandle(stdoutRead
);
170 if (!GetExitCodeProcess(procinfo
.hProcess
, &exitcode
) ||
173 writeError("Error: executing unoinfo failed!\n");
177 path
= (wchar_t*)realloc(buf
, k
+ sizeof(wchar_t));
182 "Error: out of memory zero-terminating unoinfo output!\n");
188 if (GetLastError() != ERROR_FILE_NOT_FOUND
) {
190 writeError("Error: calling unoinfo failed!\n");
194 CloseHandle(stdoutRead
);
195 CloseHandle(stdoutWrite
);
198 /* get the value of the PATH environment variable */
199 const wchar_t* ENVVARNAME
= L
"PATH";
200 const wchar_t* PATHSEPARATOR
= L
";";
201 wchar_t* value
= _wgetenv( ENVVARNAME
);
204 * add the UNO installation path to the PATH environment variable;
205 * note that this only affects the environment variable of the current
206 * process, the command processor's environment is not changed
208 size_t size
= wcslen( ENVVARNAME
) + wcslen( L
"=" ) + wcslen( path
) + 1;
210 size
+= wcslen( PATHSEPARATOR
) + wcslen( value
);
211 wchar_t* envstr
= (wchar_t*) malloc( size
*sizeof(wchar_t) );
213 wcscpy( envstr
, ENVVARNAME
);
214 wcscat( envstr
, L
"=" );
215 wcscat( envstr
, path
);
218 wcscat( envstr
, PATHSEPARATOR
);
219 wcscat( envstr
, value
);
227 writeError( "Warning: no UNO installation found!\n" );
230 /* create the command line for the application process */
231 wchar_t* cmdline
= createCommandLine( lpCmdLine
);
232 if ( cmdline
== NULL
)
234 writeError( "Error: cannot create command line!\n" );
239 /* create the application process */
240 STARTUPINFOW startup_info
;
241 PROCESS_INFORMATION process_info
;
242 memset( &startup_info
, 0, sizeof(startup_info
) );
243 startup_info
.cb
= sizeof(startup_info
);
244 BOOL bCreate
= CreateProcessW( NULL
, cmdline
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
,
245 &startup_info
, &process_info
);
249 writeError( "Error: cannot create process!\n" );
254 /* close the error file */
261 * Gets the path of a UNO installation.
263 * @return the installation path or NULL, if no installation was specified or
264 * found, or if an error occurred.
265 * Returned pointer must be released with free()
269 wchar_t* path
= cppuhelper_detail_findSofficePath();
272 writeError( "Warning: getting path from Windows Registry failed!\n" );
278 * Creates the command line for the application process including the absolute
279 * path of the executable.
281 * <p>The application's executable file name is the name of this executable
282 * prefixed by '_'.</p>
284 * @param appendix specifies the command line for the application excluding
285 * the executable name
287 * @return the command line for the application process or NULL, if an error
290 wchar_t* createCommandLine( wchar_t const * appendix
)
292 const wchar_t* CMDPREFIX
= L
"_";
293 const wchar_t* DQUOTE
= L
"\"";
294 const wchar_t* SPACE
= L
" ";
296 wchar_t* cmdline
= NULL
;
298 wchar_t cmdname
[ _MAX_PATH
];
299 wchar_t drive
[ _MAX_DRIVE
];
300 wchar_t dir
[ _MAX_PATH
];
301 wchar_t base
[ _MAX_FNAME
];
302 wchar_t newbase
[ _MAX_FNAME
];
303 wchar_t ext
[ _MAX_EXT
];
305 /* get the absolute path of the executable file */
306 if ( GetModuleFileNameW( NULL
, cmdname
, MY_SIZE( cmdname
) ) )
308 /* prefix the executable file name by '_' */
309 _wsplitpath( cmdname
, drive
, dir
, base
, ext
);
310 wcscpy( newbase
, CMDPREFIX
);
311 wcscat( newbase
, base
);
312 _wmakepath( cmdname
, drive
, dir
, newbase
, ext
);
314 /* create the command line */
315 cmdline
= (wchar_t*) malloc( (wcslen( DQUOTE
) + wcslen( cmdname
) +
316 wcslen ( DQUOTE
) + wcslen( SPACE
) + wcslen( appendix
) + 1) * sizeof(wchar_t) );
318 wcscpy( cmdline
, DQUOTE
);
319 wcscat( cmdline
, cmdname
);
320 wcscat( cmdline
, DQUOTE
);
321 wcscat( cmdline
, SPACE
);
322 wcscat( cmdline
, appendix
);
329 * Gets the pointer to the error file.
331 * <p>The error file will only be created, if create != 0.</p>
333 * <p>The error file has the name <executable file name>-error.log and is
334 * created in the same directory as the executable file. If this fails,
335 * the error file is created in the directory designated for temporary files.
338 * @param create specifies, if the error file should be created (create != 0)
340 * @return the pointer to the open error file or NULL, if no error file is
341 * open or can be created
343 FILE* getErrorFile( int create
)
345 const wchar_t* MODE
= L
"w";
346 const wchar_t* BASEPOSTFIX
= L
"-error";
347 const wchar_t* EXTENSION
= L
".log";
349 static FILE* ferr
= NULL
;
351 wchar_t fname
[ _MAX_PATH
];
352 wchar_t drive
[ _MAX_DRIVE
];
353 wchar_t dir
[ _MAX_PATH
];
354 wchar_t base
[ _MAX_FNAME
];
355 wchar_t newbase
[ _MAX_FNAME
];
356 wchar_t ext
[ _MAX_EXT
];
358 if ( ferr
== NULL
&& create
)
360 /* get the absolute path of the executable file */
361 if ( GetModuleFileNameW( NULL
, fname
, MY_SIZE( fname
) ) )
363 /* create error file in the directory of the executable file */
364 _wsplitpath( fname
, drive
, dir
, base
, ext
);
365 wcscpy( newbase
, base
);
366 wcscat( newbase
, BASEPOSTFIX
);
367 _wmakepath( fname
, drive
, dir
, newbase
, EXTENSION
);
368 ferr
= _wfopen( fname
, MODE
);
372 /* create error file in the temp directory */
373 GetTempPathW(MY_SIZE( fname
), fname
);
374 wcscat( fname
, newbase
);
375 wcscat( fname
, EXTENSION
);
376 ferr
= _wfopen( fname
, MODE
);
385 * Writes an error message to the error file.
387 * @param errstr specifies the error message
389 void writeError( const char* errstr
)
391 FILE* ferr
= getErrorFile( 1 );
394 fputs( errstr
, ferr
);
400 * Closes the error file.
402 void closeErrorFile()
404 FILE* ferr
= getErrorFile( 0 );
409 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */