main
cpp 471 lines 14.3 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 // Exit macros
6 #define AppExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
7 #define AppExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
8 #define AppExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
9 #define AppExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
10 #define AppExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
11 #define AppExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_APPUTIL, x, e, s, __VA_ARGS__)
12 #define AppExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_APPUTIL, x, s, __VA_ARGS__)
13 #define AppExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__)
14 #define AppExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__)
15 #define AppExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_APPUTIL, p, x, e, s, __VA_ARGS__)
16 #define AppExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_APPUTIL, p, x, s, __VA_ARGS__)
17 #define AppExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_APPUTIL, e, x, s, __VA_ARGS__)
18 #define AppExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_APPUTIL, g, x, s, __VA_ARGS__)
19
20 typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD);
21 typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR);
22
23 static BOOL vfInitialized = FALSE;
24 static LPFN_SETDEFAULTDLLDIRECTORIES vpfnSetDefaultDllDirectories = NULL;
25 static LPFN_SETDLLDIRECTORYW vpfnSetDllDirectory = NULL;
26
27 /********************************************************************
28 EscapeCommandLineArgument - encodes wzArgument such that
29 ::CommandLineToArgv() will parse it back unaltered. If no escaping
30 was required, *psczEscaped is NULL.
31
32 ********************************************************************/
33 static HRESULT EscapeCommandLineArgument(
34 __in_z LPCWSTR wzArgument,
35 __out_z LPWSTR* psczEscaped
36 );
37
38 static void Initialize()
39 {
40 HRESULT hr = S_OK;
41 HMODULE hKernel32 = NULL;
42
43 if (vfInitialized)
44 {
45 ExitFunction();
46 }
47
48 hKernel32 = ::GetModuleHandleW(L"kernel32");
49 AppExitOnNullWithLastError(hKernel32, hr, "Failed to get module handle for kernel32.");
50
51 vpfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories");
52 vpfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW");
53
54 vfInitialized = TRUE;
55
56 LExit:
57 return;
58 }
59
60 DAPI_(HRESULT) LoadSystemLibrary(
61 __in_z LPCWSTR wzModuleName,
62 __out HMODULE* phModule
63 )
64 {
65 HRESULT hr = S_OK;
66
67 Initialize();
68
69 if (vpfnSetDefaultDllDirectories) // LOAD_LIBRARY_SEARCH_SYSTEM32 was added at same time as SetDefaultDllDirectories.
70 {
71 *phModule = ::LoadLibraryExW(wzModuleName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
72 AppExitOnNullWithLastError(*phModule, hr, "Failed to load library with LOAD_LIBRARY_SEARCH_SYSTEM32: %ls.", wzModuleName);
73 }
74 else
75 {
76 hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL);
77 }
78
79 LExit:
80 return hr;
81 }
82
83 DAPI_(HRESULT) LoadSystemLibraryWithPath(
84 __in_z LPCWSTR wzModuleName,
85 __out HMODULE* phModule,
86 __deref_out_z_opt LPWSTR* psczPath
87 )
88 {
89 HRESULT hr = S_OK;
90 LPWSTR sczDirectory = NULL;
91 LPWSTR sczPath = NULL;
92
93 hr = PathGetSystemDirectory(&sczDirectory);
94 AppExitOnFailure(hr, "Failed to get the Windows system directory.");
95
96 hr = StrAllocFormatted(&sczPath, L"%ls%ls", sczDirectory, wzModuleName);
97 AppExitOnFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
98
99 *phModule = ::LoadLibraryExW(sczPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
100 AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", sczPath);
101
102 if (psczPath)
103 {
104 *psczPath = sczPath;
105 sczPath = NULL;
106 }
107
108 LExit:
109 ReleaseStr(sczDirectory);
110
111 return hr;
112 }
113
114 DAPI_(HRESULT) LoadSystemApiSet(
115 __in_z LPCWSTR wzApiSet,
116 __out HMODULE* phModule
117 )
118 {
119 HRESULT hr = S_OK;
120
121 Initialize();
122
123 if (!vpfnSetDefaultDllDirectories)
124 {
125 // For many API sets, the .dll does not actually exist on disk so there's no point on even trying if SetDefaultDllDirectories is not available.
126 // On OS's where API sets are implemented, the loader requires just the API set name with .dll.
127 // It is not safe to pass such strings to LoadLibraryEx without LOAD_LIBRARY_SEARCH_SYSTEM32, which isn't available on old OS's.
128 AppExitWithRootFailure(hr, E_MODNOTFOUND, "OS doesn't support API sets.");
129 }
130 else
131 {
132 hr = LoadSystemLibrary(wzApiSet, phModule);
133 }
134
135 LExit:
136 return hr;
137 }
138
139 DAPI_(void) AppInitialize(
140 __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[],
141 __in DWORD cSafelyLoadSystemDlls
142 )
143 {
144 HRESULT hr = S_OK;
145 HMODULE hIgnored = NULL;
146 BOOL fSetDefaultDllDirectories = FALSE;
147
148 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
149
150 Initialize();
151
152 // Best effort call to initialize default DLL directories to system only.
153 if (vpfnSetDefaultDllDirectories)
154 {
155 if (vpfnSetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
156 {
157 fSetDefaultDllDirectories = TRUE;
158 }
159 else
160 {
161 hr = HRESULT_FROM_WIN32(::GetLastError());
162 TraceError(hr, "Failed to call SetDefaultDllDirectories.");
163 }
164 }
165
166 // Only need to safely load if the default DLL directories was not
167 // able to be set.
168 if (!fSetDefaultDllDirectories)
169 {
170 // Remove current working directory from search order.
171 if (!vpfnSetDllDirectory || !vpfnSetDllDirectory(L""))
172 {
173 hr = vpfnSetDllDirectory ? HRESULT_FROM_WIN32(::GetLastError()) : HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
174 TraceError(hr, "Failed to call SetDllDirectory.");
175 }
176
177 for (DWORD i = 0; i < cSafelyLoadSystemDlls; ++i)
178 {
179 hr = LoadSystemLibrary(rgsczSafelyLoadSystemDlls[i], &hIgnored);
180 if (FAILED(hr))
181 {
182 TraceError(hr, "Failed to safety load: %ls", rgsczSafelyLoadSystemDlls[i]);
183 }
184 }
185 }
186 }
187
188 DAPI_(void) AppInitializeUnsafe()
189 {
190 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
191 }
192
193 DAPI_(HRESULT) AppAppendCommandLineArgument(
194 __deref_inout_z LPWSTR* psczCommandLine,
195 __in_z LPCWSTR wzArgument
196 )
197 {
198 HRESULT hr = S_OK;
199 LPWSTR sczQuotedArg = NULL;
200
201 hr = EscapeCommandLineArgument(wzArgument, &sczQuotedArg);
202 AppExitOnFailure(hr, "Failed to escape command line argument.");
203
204 // If there is already data in the command line,
205 // append a space before appending the argument.
206 if (*psczCommandLine && **psczCommandLine)
207 {
208 hr = StrAllocConcatSecure(psczCommandLine, L" ", 0);
209 AppExitOnFailure(hr, "Failed to append space to command line with existing data.");
210 }
211
212 hr = StrAllocConcatSecure(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0);
213 AppExitOnFailure(hr, "Failed to copy command line argument.");
214
215 LExit:
216 ReleaseStr(sczQuotedArg);
217
218 return hr;
219 }
220
221 DAPIV_(HRESULT) AppAppendCommandLineArgumentFormatted(
222 __deref_inout_z LPWSTR* psczCommandLine,
223 __in __format_string LPCWSTR wzFormat,
224 ...
225 )
226 {
227 HRESULT hr = S_OK;
228 va_list args;
229
230 va_start(args, wzFormat);
231 hr = AppAppendCommandLineArgumentFormattedArgs(psczCommandLine, wzFormat, args);
232 va_end(args);
233
234 return hr;
235 }
236
237 DAPI_(HRESULT) AppAppendCommandLineArgumentFormattedArgs(
238 __deref_inout_z LPWSTR* psczCommandLine,
239 __in __format_string LPCWSTR wzFormat,
240 __in va_list args
241 )
242 {
243 HRESULT hr = S_OK;
244 LPWSTR sczQuotedArg = NULL;
245
246 hr = AppEscapeCommandLineArgumentFormattedArgs(&sczQuotedArg, wzFormat, args);
247 AppExitOnFailure(hr, "Failed to escape command line argument.");
248
249 // If there is already data in the command line,
250 // append a space before appending the argument.
251 if (*psczCommandLine && **psczCommandLine)
252 {
253 hr = StrAllocConcatSecure(psczCommandLine, L" ", 0);
254 AppExitOnFailure(hr, "Failed to append space to command line with existing data.");
255 }
256
257 hr = StrAllocConcatSecure(psczCommandLine, sczQuotedArg, 0);
258 AppExitOnFailure(hr, "Failed to copy command line argument.");
259
260 LExit:
261 ReleaseStr(sczQuotedArg);
262
263 return hr;
264 }
265
266 DAPIV_(HRESULT) AppEscapeCommandLineArgumentFormatted(
267 __deref_inout_z LPWSTR* psczEscapedArgument,
268 __in __format_string LPCWSTR wzFormat,
269 ...
270 )
271 {
272 HRESULT hr = S_OK;
273 va_list args;
274
275 va_start(args, wzFormat);
276 hr = AppEscapeCommandLineArgumentFormattedArgs(psczEscapedArgument, wzFormat, args);
277 va_end(args);
278
279 return hr;
280 }
281
282 DAPI_(HRESULT) AppEscapeCommandLineArgumentFormattedArgs(
283 __deref_inout_z LPWSTR* psczEscapedArgument,
284 __in __format_string LPCWSTR wzFormat,
285 __in va_list args
286 )
287 {
288 HRESULT hr = S_OK;
289 LPWSTR sczFormattedArg = NULL;
290 LPWSTR sczQuotedArg = NULL;
291
292 hr = StrAllocFormattedArgsSecure(&sczFormattedArg, wzFormat, args);
293 AppExitOnFailure(hr, "Failed to format command line argument.");
294
295 hr = EscapeCommandLineArgument(sczFormattedArg, &sczQuotedArg);
296 AppExitOnFailure(hr, "Failed to escape command line argument.");
297
298 if (sczQuotedArg)
299 {
300 *psczEscapedArgument = sczQuotedArg;
301 sczQuotedArg = NULL;
302 }
303 else
304 {
305 *psczEscapedArgument = sczFormattedArg;
306 sczFormattedArg = NULL;
307 }
308
309 LExit:
310 ReleaseStr(sczFormattedArg);
311 ReleaseStr(sczQuotedArg);
312
313 return hr;
314 }
315
316 DAPI_(HRESULT) AppWaitForSingleObject(
317 __in HANDLE hHandle,
318 __in DWORD dwMilliseconds
319 )
320 {
321 HRESULT hr = S_OK;
322 DWORD dwResult = 0;
323
324 dwResult = ::WaitForSingleObject(hHandle, dwMilliseconds);
325 if (WAIT_TIMEOUT == dwResult)
326 {
327 ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult));
328 }
329 else if (WAIT_ABANDONED == dwResult)
330 {
331 AppExitOnWin32Error(dwResult, hr, "Abandoned wait for single object.");
332 }
333 else if (WAIT_OBJECT_0 != dwResult)
334 {
335 AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForSingleObject.");
336 AppExitWithLastError(hr, "Failed to wait for single object.");
337 }
338
339 LExit:
340 return hr;
341 }
342
343 DAPI_(HRESULT) AppWaitForMultipleObjects(
344 __in DWORD dwCount,
345 __in const HANDLE* rghHandles,
346 __in BOOL fWaitAll,
347 __in DWORD dwMilliseconds,
348 __out_opt DWORD* pdwSignaledIndex
349 )
350 {
351 HRESULT hr = S_OK;
352 DWORD dwResult = 0;
353 DWORD dwSignaledIndex = dwCount;
354
355 dwResult = ::WaitForMultipleObjects(dwCount, rghHandles, fWaitAll, dwMilliseconds);
356 if (WAIT_TIMEOUT == dwResult)
357 {
358 ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult));
359 }
360 else if (WAIT_ABANDONED_0 <= dwResult && (WAIT_ABANDONED_0 + dwCount) > dwResult)
361 {
362 dwSignaledIndex = dwResult - WAIT_ABANDONED_0;
363 AppExitOnWin32Error(dwResult, hr, "Abandoned wait for multiple objects, index: %u.", dwSignaledIndex);
364 }
365 else if (WAIT_OBJECT_0 <= dwResult && (WAIT_OBJECT_0 + dwCount) > dwResult)
366 {
367 dwSignaledIndex = dwResult - WAIT_OBJECT_0;
368 }
369 else
370 {
371 AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForMultipleObjects.");
372 AppExitWithLastError(hr, "Failed to wait for multiple objects.");
373 }
374
375 LExit:
376 if (pdwSignaledIndex)
377 {
378 *pdwSignaledIndex = dwSignaledIndex;
379 }
380
381 return hr;
382 }
383
384 static HRESULT EscapeCommandLineArgument(
385 __in_z LPCWSTR wzArgument,
386 __out_z LPWSTR* psczEscaped
387 )
388 {
389 HRESULT hr = S_OK;
390 BOOL fRequiresQuoting = FALSE;
391 SIZE_T cMaxEscapedSize = 0;
392
393 *psczEscaped = NULL;
394
395 // Loop through the argument determining if it needs to be quoted and what the maximum
396 // size would be if there are escape characters required.
397 for (LPCWSTR pwz = wzArgument; *pwz; ++pwz)
398 {
399 // Arguments with whitespace need quoting.
400 if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz)
401 {
402 fRequiresQuoting = TRUE;
403 }
404 else if (L'"' == *pwz) // quotes need quoting and sometimes escaping.
405 {
406 fRequiresQuoting = TRUE;
407 ++cMaxEscapedSize;
408 }
409 else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room.
410 {
411 ++cMaxEscapedSize;
412 }
413
414 ++cMaxEscapedSize;
415 }
416
417 // If we found anything in the argument that requires our argument to be quoted
418 if (fRequiresQuoting)
419 {
420 hr = StrAlloc(psczEscaped, cMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator.
421 AppExitOnFailure(hr, "Failed to allocate argument to be quoted.");
422
423 LPCWSTR pwz = wzArgument;
424 LPWSTR pwzQuoted = *psczEscaped;
425
426 *pwzQuoted = L'"';
427 ++pwzQuoted;
428 while (*pwz)
429 {
430 DWORD dwBackslashes = 0;
431 while (L'\\' == *pwz)
432 {
433 ++dwBackslashes;
434 ++pwz;
435 }
436
437 // Escape all backslashes at the end of the string.
438 if (!*pwz)
439 {
440 dwBackslashes *= 2;
441 }
442 else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself.
443 {
444 dwBackslashes = dwBackslashes * 2 + 1;
445 }
446 // the backslashes don't have to be escaped.
447
448 // Add the appropriate number of backslashes
449 for (DWORD i = 0; i < dwBackslashes; ++i)
450 {
451 *pwzQuoted = L'\\';
452 ++pwzQuoted;
453 }
454
455 // If there is a character, add it after all the escaped backslashes
456 if (*pwz)
457 {
458 *pwzQuoted = *pwz;
459 ++pwz;
460 ++pwzQuoted;
461 }
462 }
463
464 *pwzQuoted = L'"';
465 ++pwzQuoted;
466 *pwzQuoted = L'\0'; // ensure the arg is null terminated.
467 }
468
469 LExit:
470 return hr;
471 }