Merge m-c to b-i.
[gecko.git] / toolkit / xre / nsWindowsRestart.cpp
blobaf92cd876402f41b67d9859f3bc5e16fe7c8695e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // This file is not build directly. Instead, it is included in multiple
6 // shared objects.
8 #ifdef nsWindowsRestart_cpp
9 #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
10 #else
11 #define nsWindowsRestart_cpp
12 #endif
14 #include "nsUTF8Utils.h"
16 #include <shellapi.h>
18 // Needed for CreateEnvironmentBlock
19 #include <userenv.h>
20 #pragma comment(lib, "userenv.lib")
22 /**
23 * Get the length that the string will take and takes into account the
24 * additional length if the string needs to be quoted and if characters need to
25 * be escaped.
27 static int ArgStrLen(const wchar_t *s)
29 int backslashes = 0;
30 int i = wcslen(s);
31 BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
32 // Only add doublequotes if the string contains a space or a tab
33 BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
35 if (addDoubleQuotes) {
36 i += 2; // initial and final duoblequote
39 if (hasDoubleQuote) {
40 while (*s) {
41 if (*s == '\\') {
42 ++backslashes;
43 } else {
44 if (*s == '"') {
45 // Escape the doublequote and all backslashes preceding the doublequote
46 i += backslashes + 1;
49 backslashes = 0;
52 ++s;
56 return i;
59 /**
60 * Copy string "s" to string "d", quoting the argument as appropriate and
61 * escaping doublequotes along with any backslashes that immediately precede
62 * doublequotes.
63 * The CRT parses this to retrieve the original argc/argv that we meant,
64 * see STDARGV.C in the MSVC CRT sources.
66 * @return the end of the string
68 static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
70 int backslashes = 0;
71 BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
72 // Only add doublequotes if the string contains a space or a tab
73 BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
75 if (addDoubleQuotes) {
76 *d = '"'; // initial doublequote
77 ++d;
80 if (hasDoubleQuote) {
81 int i;
82 while (*s) {
83 if (*s == '\\') {
84 ++backslashes;
85 } else {
86 if (*s == '"') {
87 // Escape the doublequote and all backslashes preceding the doublequote
88 for (i = 0; i <= backslashes; ++i) {
89 *d = '\\';
90 ++d;
94 backslashes = 0;
97 *d = *s;
98 ++d; ++s;
100 } else {
101 wcscpy(d, s);
102 d += wcslen(s);
105 if (addDoubleQuotes) {
106 *d = '"'; // final doublequote
107 ++d;
110 return d;
114 * Creates a command line from a list of arguments. The returned
115 * string is allocated with "malloc" and should be "free"d.
117 * argv is UTF8
119 wchar_t*
120 MakeCommandLine(int argc, wchar_t **argv)
122 int i;
123 int len = 0;
125 // The + 1 of the last argument handles the allocation for null termination
126 for (i = 0; i < argc; ++i)
127 len += ArgStrLen(argv[i]) + 1;
129 // Protect against callers that pass 0 arguments
130 if (len == 0)
131 len = 1;
133 wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t));
134 if (!s)
135 return nullptr;
137 wchar_t *c = s;
138 for (i = 0; i < argc; ++i) {
139 c = ArgToString(c, argv[i]);
140 if (i + 1 != argc) {
141 *c = ' ';
142 ++c;
146 *c = '\0';
148 return s;
152 * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
153 * can't link to updater.exe.
155 static PRUnichar*
156 AllocConvertUTF8toUTF16(const char *arg)
158 // UTF16 can't be longer in units than UTF8
159 int len = strlen(arg);
160 PRUnichar *s = new PRUnichar[(len + 1) * sizeof(PRUnichar)];
161 if (!s)
162 return nullptr;
164 ConvertUTF8toUTF16 convert(s);
165 convert.write(arg, len);
166 convert.write_terminator();
167 return s;
170 static void
171 FreeAllocStrings(int argc, wchar_t **argv)
173 while (argc) {
174 --argc;
175 delete [] argv[argc];
178 delete [] argv;
184 * Launch a child process with the specified arguments.
185 * @note argv[0] is ignored
186 * @note The form of this function that takes char **argv expects UTF-8
189 BOOL
190 WinLaunchChild(const wchar_t *exePath,
191 int argc, wchar_t **argv,
192 HANDLE userToken = nullptr,
193 HANDLE *hProcess = nullptr);
195 BOOL
196 WinLaunchChild(const wchar_t *exePath,
197 int argc, char **argv,
198 HANDLE userToken,
199 HANDLE *hProcess)
201 wchar_t** argvConverted = new wchar_t*[argc];
202 if (!argvConverted)
203 return FALSE;
205 for (int i = 0; i < argc; ++i) {
206 argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
207 if (!argvConverted[i]) {
208 FreeAllocStrings(i, argvConverted);
209 return FALSE;
213 BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
214 FreeAllocStrings(argc, argvConverted);
215 return ok;
218 BOOL
219 WinLaunchChild(const wchar_t *exePath,
220 int argc,
221 wchar_t **argv,
222 HANDLE userToken,
223 HANDLE *hProcess)
225 wchar_t *cl;
226 BOOL ok;
228 cl = MakeCommandLine(argc, argv);
229 if (!cl) {
230 return FALSE;
233 STARTUPINFOW si = {0};
234 si.cb = sizeof(STARTUPINFOW);
235 si.lpDesktop = L"winsta0\\Default";
236 PROCESS_INFORMATION pi = {0};
238 if (userToken == nullptr) {
239 ok = CreateProcessW(exePath,
241 nullptr, // no special security attributes
242 nullptr, // no special thread attributes
243 FALSE, // don't inherit filehandles
244 0, // creation flags
245 nullptr, // inherit my environment
246 nullptr, // use my current directory
247 &si,
248 &pi);
249 } else {
250 // Create an environment block for the process we're about to start using
251 // the user's token.
252 LPVOID environmentBlock = nullptr;
253 if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
254 environmentBlock = nullptr;
257 ok = CreateProcessAsUserW(userToken,
258 exePath,
260 nullptr, // no special security attributes
261 nullptr, // no special thread attributes
262 FALSE, // don't inherit filehandles
263 0, // creation flags
264 environmentBlock,
265 nullptr, // use my current directory
266 &si,
267 &pi);
269 if (environmentBlock) {
270 DestroyEnvironmentBlock(environmentBlock);
274 if (ok) {
275 if (hProcess) {
276 *hProcess = pi.hProcess; // the caller now owns the HANDLE
277 } else {
278 CloseHandle(pi.hProcess);
280 CloseHandle(pi.hThread);
281 } else {
282 LPVOID lpMsgBuf = nullptr;
283 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
284 FORMAT_MESSAGE_FROM_SYSTEM |
285 FORMAT_MESSAGE_IGNORE_INSERTS,
286 nullptr,
287 GetLastError(),
288 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
289 (LPTSTR) &lpMsgBuf,
291 nullptr);
292 wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
293 if (lpMsgBuf)
294 LocalFree(lpMsgBuf);
297 free(cl);
299 return ok;