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
8 #ifdef nsWindowsRestart_cpp
9 #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
11 #define nsWindowsRestart_cpp
14 #include "nsUTF8Utils.h"
18 // Needed for CreateEnvironmentBlock
20 #pragma comment(lib, "userenv.lib")
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
27 static int ArgStrLen(const wchar_t *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
45 // Escape the doublequote and all backslashes preceding the doublequote
60 * Copy string "s" to string "d", quoting the argument as appropriate and
61 * escaping doublequotes along with any backslashes that immediately precede
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
)
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
87 // Escape the doublequote and all backslashes preceding the doublequote
88 for (i
= 0; i
<= backslashes
; ++i
) {
105 if (addDoubleQuotes
) {
106 *d
= '"'; // final doublequote
114 * Creates a command line from a list of arguments. The returned
115 * string is allocated with "malloc" and should be "free"d.
120 MakeCommandLine(int argc
, wchar_t **argv
)
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
133 wchar_t *s
= (wchar_t*) malloc(len
* sizeof(wchar_t));
138 for (i
= 0; i
< argc
; ++i
) {
139 c
= ArgToString(c
, argv
[i
]);
152 * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
153 * can't link to updater.exe.
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
)];
164 ConvertUTF8toUTF16
convert(s
);
165 convert
.write(arg
, len
);
166 convert
.write_terminator();
171 FreeAllocStrings(int argc
, wchar_t **argv
)
175 delete [] argv
[argc
];
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
190 WinLaunchChild(const wchar_t *exePath
,
191 int argc
, wchar_t **argv
,
192 HANDLE userToken
= nullptr,
193 HANDLE
*hProcess
= nullptr);
196 WinLaunchChild(const wchar_t *exePath
,
197 int argc
, char **argv
,
201 wchar_t** argvConverted
= new wchar_t*[argc
];
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
);
213 BOOL ok
= WinLaunchChild(exePath
, argc
, argvConverted
, userToken
, hProcess
);
214 FreeAllocStrings(argc
, argvConverted
);
219 WinLaunchChild(const wchar_t *exePath
,
228 cl
= MakeCommandLine(argc
, argv
);
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
245 nullptr, // inherit my environment
246 nullptr, // use my current directory
250 // Create an environment block for the process we're about to start using
252 LPVOID environmentBlock
= nullptr;
253 if (!CreateEnvironmentBlock(&environmentBlock
, userToken
, TRUE
)) {
254 environmentBlock
= nullptr;
257 ok
= CreateProcessAsUserW(userToken
,
260 nullptr, // no special security attributes
261 nullptr, // no special thread attributes
262 FALSE
, // don't inherit filehandles
265 nullptr, // use my current directory
269 if (environmentBlock
) {
270 DestroyEnvironmentBlock(environmentBlock
);
276 *hProcess
= pi
.hProcess
; // the caller now owns the HANDLE
278 CloseHandle(pi
.hProcess
);
280 CloseHandle(pi
.hThread
);
282 LPVOID lpMsgBuf
= nullptr;
283 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
284 FORMAT_MESSAGE_FROM_SYSTEM
|
285 FORMAT_MESSAGE_IGNORE_INSERTS
,
288 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
292 wprintf(L
"Error restarting: %s\n", lpMsgBuf
? lpMsgBuf
: L
"(null)");