diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/pathutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 1083 |
1 files changed, 1083 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp new file mode 100644 index 00000000..7c3cfe06 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp | |||
@@ -0,0 +1,1083 @@ | |||
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 | |||
6 | // Exit macros | ||
7 | #define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
8 | #define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
9 | #define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
10 | #define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
11 | #define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
12 | #define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | ||
13 | #define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) | ||
14 | #define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) | ||
15 | #define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) | ||
16 | #define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) | ||
17 | #define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__) | ||
18 | #define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) | ||
19 | |||
20 | #define PATH_GOOD_ENOUGH 64 | ||
21 | |||
22 | |||
23 | DAPI_(HRESULT) PathCommandLineAppend( | ||
24 | __deref_inout_z LPWSTR* psczCommandLine, | ||
25 | __in_z LPCWSTR wzArgument | ||
26 | ) | ||
27 | { | ||
28 | HRESULT hr = S_OK; | ||
29 | LPWSTR sczQuotedArg = NULL; | ||
30 | BOOL fRequiresQuoting = FALSE; | ||
31 | DWORD dwMaxEscapedSize = 0; | ||
32 | |||
33 | // Loop through the argument determining if it needs to be quoted and what the maximum | ||
34 | // size would be if there are escape characters required. | ||
35 | for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) | ||
36 | { | ||
37 | // Arguments with whitespace need quoting. | ||
38 | if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) | ||
39 | { | ||
40 | fRequiresQuoting = TRUE; | ||
41 | } | ||
42 | else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. | ||
43 | { | ||
44 | fRequiresQuoting = TRUE; | ||
45 | ++dwMaxEscapedSize; | ||
46 | } | ||
47 | else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. | ||
48 | { | ||
49 | ++dwMaxEscapedSize; | ||
50 | } | ||
51 | |||
52 | ++dwMaxEscapedSize; | ||
53 | } | ||
54 | |||
55 | // If we found anything in the argument that requires our argument to be quoted | ||
56 | if (fRequiresQuoting) | ||
57 | { | ||
58 | hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. | ||
59 | PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); | ||
60 | |||
61 | LPCWSTR pwz = wzArgument; | ||
62 | LPWSTR pwzQuoted = sczQuotedArg; | ||
63 | |||
64 | *pwzQuoted = L'"'; | ||
65 | ++pwzQuoted; | ||
66 | while (*pwz) | ||
67 | { | ||
68 | DWORD dwBackslashes = 0; | ||
69 | while (L'\\' == *pwz) | ||
70 | { | ||
71 | ++dwBackslashes; | ||
72 | ++pwz; | ||
73 | } | ||
74 | |||
75 | // Escape all backslashes at the end of the string. | ||
76 | if (!*pwz) | ||
77 | { | ||
78 | dwBackslashes *= 2; | ||
79 | } | ||
80 | else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. | ||
81 | { | ||
82 | dwBackslashes = dwBackslashes * 2 + 1; | ||
83 | } | ||
84 | // the backslashes don't have to be escaped. | ||
85 | |||
86 | // Add the appropriate number of backslashes | ||
87 | for (DWORD i = 0; i < dwBackslashes; ++i) | ||
88 | { | ||
89 | *pwzQuoted = L'\\'; | ||
90 | ++pwzQuoted; | ||
91 | } | ||
92 | |||
93 | // If there is a character, add it after all the escaped backslashes | ||
94 | if (*pwz) | ||
95 | { | ||
96 | *pwzQuoted = *pwz; | ||
97 | ++pwz; | ||
98 | ++pwzQuoted; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | *pwzQuoted = L'"'; | ||
103 | ++pwzQuoted; | ||
104 | *pwzQuoted = L'\0'; // ensure the arg is null terminated. | ||
105 | } | ||
106 | |||
107 | // If there is already data in the command line, append a space before appending the | ||
108 | // argument. | ||
109 | if (*psczCommandLine && **psczCommandLine) | ||
110 | { | ||
111 | hr = StrAllocConcat(psczCommandLine, L" ", 0); | ||
112 | PathExitOnFailure(hr, "Failed to append space to command line with existing data."); | ||
113 | } | ||
114 | |||
115 | hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); | ||
116 | PathExitOnFailure(hr, "Failed to copy command line argument."); | ||
117 | |||
118 | LExit: | ||
119 | ReleaseStr(sczQuotedArg); | ||
120 | |||
121 | return hr; | ||
122 | } | ||
123 | |||
124 | |||
125 | DAPI_(LPWSTR) PathFile( | ||
126 | __in_z LPCWSTR wzPath | ||
127 | ) | ||
128 | { | ||
129 | if (!wzPath) | ||
130 | { | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | LPWSTR wzFile = const_cast<LPWSTR>(wzPath); | ||
135 | for (LPWSTR wz = wzFile; *wz; ++wz) | ||
136 | { | ||
137 | // valid delineators | ||
138 | // \ => Windows path | ||
139 | // / => unix and URL path | ||
140 | // : => relative path from mapped root | ||
141 | if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) | ||
142 | { | ||
143 | wzFile = wz + 1; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | return wzFile; | ||
148 | } | ||
149 | |||
150 | |||
151 | DAPI_(LPCWSTR) PathExtension( | ||
152 | __in_z LPCWSTR wzPath | ||
153 | ) | ||
154 | { | ||
155 | if (!wzPath) | ||
156 | { | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | // Find the last dot in the last thing that could be a file. | ||
161 | LPCWSTR wzExtension = NULL; | ||
162 | for (LPCWSTR wz = wzPath; *wz; ++wz) | ||
163 | { | ||
164 | if (L'\\' == *wz || L'/' == *wz || L':' == *wz) | ||
165 | { | ||
166 | wzExtension = NULL; | ||
167 | } | ||
168 | else if (L'.' == *wz) | ||
169 | { | ||
170 | wzExtension = wz; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return wzExtension; | ||
175 | } | ||
176 | |||
177 | |||
178 | DAPI_(HRESULT) PathGetDirectory( | ||
179 | __in_z LPCWSTR wzPath, | ||
180 | __out_z LPWSTR *psczDirectory | ||
181 | ) | ||
182 | { | ||
183 | HRESULT hr = S_OK; | ||
184 | size_t cchDirectory = SIZE_T_MAX; | ||
185 | |||
186 | for (LPCWSTR wz = wzPath; *wz; ++wz) | ||
187 | { | ||
188 | // valid delineators: | ||
189 | // \ => Windows path | ||
190 | // / => unix and URL path | ||
191 | // : => relative path from mapped root | ||
192 | if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) | ||
193 | { | ||
194 | cchDirectory = static_cast<size_t>(wz - wzPath) + 1; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | if (SIZE_T_MAX == cchDirectory) | ||
199 | { | ||
200 | // we were given just a file name, so there's no directory available | ||
201 | return S_FALSE; | ||
202 | } | ||
203 | |||
204 | if (wzPath[0] == L'\"') | ||
205 | { | ||
206 | ++wzPath; | ||
207 | --cchDirectory; | ||
208 | } | ||
209 | |||
210 | hr = StrAllocString(psczDirectory, wzPath, cchDirectory); | ||
211 | PathExitOnFailure(hr, "Failed to copy directory."); | ||
212 | |||
213 | LExit: | ||
214 | return hr; | ||
215 | } | ||
216 | |||
217 | |||
218 | DAPI_(HRESULT) PathGetParentPath( | ||
219 | __in_z LPCWSTR wzPath, | ||
220 | __out_z LPWSTR *psczParent | ||
221 | ) | ||
222 | { | ||
223 | HRESULT hr = S_OK; | ||
224 | LPCWSTR wzParent = NULL; | ||
225 | |||
226 | for (LPCWSTR wz = wzPath; *wz; ++wz) | ||
227 | { | ||
228 | if (wz[1] && (L'\\' == *wz || L'/' == *wz)) | ||
229 | { | ||
230 | wzParent = wz; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | if (wzParent) | ||
235 | { | ||
236 | size_t cchPath = static_cast<size_t>(wzParent - wzPath) + 1; | ||
237 | |||
238 | hr = StrAllocString(psczParent, wzPath, cchPath); | ||
239 | PathExitOnFailure(hr, "Failed to copy directory."); | ||
240 | } | ||
241 | else | ||
242 | { | ||
243 | ReleaseNullStr(psczParent); | ||
244 | } | ||
245 | |||
246 | LExit: | ||
247 | return hr; | ||
248 | } | ||
249 | |||
250 | |||
251 | DAPI_(HRESULT) PathExpand( | ||
252 | __out LPWSTR *psczFullPath, | ||
253 | __in_z LPCWSTR wzRelativePath, | ||
254 | __in DWORD dwResolveFlags | ||
255 | ) | ||
256 | { | ||
257 | Assert(wzRelativePath && *wzRelativePath); | ||
258 | |||
259 | HRESULT hr = S_OK; | ||
260 | DWORD cch = 0; | ||
261 | LPWSTR sczExpandedPath = NULL; | ||
262 | DWORD cchExpandedPath = 0; | ||
263 | SIZE_T cbSize = 0; | ||
264 | |||
265 | LPWSTR sczFullPath = NULL; | ||
266 | |||
267 | // | ||
268 | // First, expand any environment variables. | ||
269 | // | ||
270 | if (dwResolveFlags & PATH_EXPAND_ENVIRONMENT) | ||
271 | { | ||
272 | cchExpandedPath = PATH_GOOD_ENOUGH; | ||
273 | |||
274 | hr = StrAlloc(&sczExpandedPath, cchExpandedPath); | ||
275 | PathExitOnFailure(hr, "Failed to allocate space for expanded path."); | ||
276 | |||
277 | cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); | ||
278 | if (0 == cch) | ||
279 | { | ||
280 | PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); | ||
281 | } | ||
282 | else if (cchExpandedPath < cch) | ||
283 | { | ||
284 | cchExpandedPath = cch; | ||
285 | hr = StrAlloc(&sczExpandedPath, cchExpandedPath); | ||
286 | PathExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); | ||
287 | |||
288 | cch = ::ExpandEnvironmentStringsW(wzRelativePath, sczExpandedPath, cchExpandedPath); | ||
289 | if (0 == cch) | ||
290 | { | ||
291 | PathExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); | ||
292 | } | ||
293 | else if (cchExpandedPath < cch) | ||
294 | { | ||
295 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
296 | PathExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | if (MAX_PATH < cch) | ||
301 | { | ||
302 | hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet | ||
303 | if (E_INVALIDARG == hr) | ||
304 | { | ||
305 | hr = S_OK; | ||
306 | } | ||
307 | PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); | ||
308 | |||
309 | hr = StrMaxLength(sczExpandedPath, &cbSize); | ||
310 | PathExitOnFailure(hr, "Failed to get max length of expanded path."); | ||
311 | |||
312 | cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | // | ||
317 | // Second, get the full path. | ||
318 | // | ||
319 | if (dwResolveFlags & PATH_EXPAND_FULLPATH) | ||
320 | { | ||
321 | LPWSTR wzFileName = NULL; | ||
322 | LPCWSTR wzPath = sczExpandedPath ? sczExpandedPath : wzRelativePath; | ||
323 | DWORD cchFullPath = max(PATH_GOOD_ENOUGH, cchExpandedPath); | ||
324 | |||
325 | hr = StrAlloc(&sczFullPath, cchFullPath); | ||
326 | PathExitOnFailure(hr, "Failed to allocate space for full path."); | ||
327 | |||
328 | cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); | ||
329 | if (0 == cch) | ||
330 | { | ||
331 | PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); | ||
332 | } | ||
333 | else if (cchFullPath < cch) | ||
334 | { | ||
335 | cchFullPath = cch < MAX_PATH ? cch : cch + 7; // ensure space for "\\?\UNC" prefix if needed | ||
336 | hr = StrAlloc(&sczFullPath, cchFullPath); | ||
337 | PathExitOnFailure(hr, "Failed to re-allocate more space for full path."); | ||
338 | |||
339 | cch = ::GetFullPathNameW(wzPath, cchFullPath, sczFullPath, &wzFileName); | ||
340 | if (0 == cch) | ||
341 | { | ||
342 | PathExitWithLastError(hr, "Failed to get full path for string: %ls", wzPath); | ||
343 | } | ||
344 | else if (cchFullPath < cch) | ||
345 | { | ||
346 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
347 | PathExitOnRootFailure(hr, "Failed to allocate buffer for full path."); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | if (MAX_PATH < cch) | ||
352 | { | ||
353 | hr = PathPrefix(&sczFullPath); | ||
354 | PathExitOnFailure(hr, "Failed to prefix long path after expanding."); | ||
355 | } | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | sczFullPath = sczExpandedPath; | ||
360 | sczExpandedPath = NULL; | ||
361 | } | ||
362 | |||
363 | hr = StrAllocString(psczFullPath, sczFullPath? sczFullPath : wzRelativePath, 0); | ||
364 | PathExitOnFailure(hr, "Failed to copy relative path into full path."); | ||
365 | |||
366 | LExit: | ||
367 | ReleaseStr(sczFullPath); | ||
368 | ReleaseStr(sczExpandedPath); | ||
369 | |||
370 | return hr; | ||
371 | } | ||
372 | |||
373 | |||
374 | DAPI_(HRESULT) PathPrefix( | ||
375 | __inout LPWSTR *psczFullPath | ||
376 | ) | ||
377 | { | ||
378 | Assert(psczFullPath && *psczFullPath); | ||
379 | |||
380 | HRESULT hr = S_OK; | ||
381 | LPWSTR wzFullPath = *psczFullPath; | ||
382 | SIZE_T cbFullPath = 0; | ||
383 | |||
384 | if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || | ||
385 | (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && | ||
386 | L':' == wzFullPath[1] && | ||
387 | L'\\' == wzFullPath[2]) // normal path | ||
388 | { | ||
389 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); | ||
390 | PathExitOnFailure(hr, "Failed to add prefix to file path."); | ||
391 | } | ||
392 | else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC | ||
393 | { | ||
394 | // ensure that we're not already prefixed | ||
395 | if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) | ||
396 | { | ||
397 | hr = StrSize(*psczFullPath, &cbFullPath); | ||
398 | PathExitOnFailure(hr, "Failed to get size of full path."); | ||
399 | |||
400 | memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); | ||
401 | |||
402 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); | ||
403 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); | ||
404 | } | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | hr = E_INVALIDARG; | ||
409 | PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); | ||
410 | } | ||
411 | |||
412 | LExit: | ||
413 | return hr; | ||
414 | } | ||
415 | |||
416 | |||
417 | DAPI_(HRESULT) PathFixedBackslashTerminate( | ||
418 | __inout_ecount_z(cchPath) LPWSTR wzPath, | ||
419 | __in SIZE_T cchPath | ||
420 | ) | ||
421 | { | ||
422 | HRESULT hr = S_OK; | ||
423 | size_t cchLength = 0; | ||
424 | |||
425 | hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); | ||
426 | PathExitOnFailure(hr, "Failed to get length of path."); | ||
427 | |||
428 | if (cchLength >= cchPath) | ||
429 | { | ||
430 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
431 | } | ||
432 | else if (L'\\' != wzPath[cchLength - 1]) | ||
433 | { | ||
434 | wzPath[cchLength] = L'\\'; | ||
435 | wzPath[cchLength + 1] = L'\0'; | ||
436 | } | ||
437 | |||
438 | LExit: | ||
439 | return hr; | ||
440 | } | ||
441 | |||
442 | |||
443 | DAPI_(HRESULT) PathBackslashTerminate( | ||
444 | __inout LPWSTR* psczPath | ||
445 | ) | ||
446 | { | ||
447 | Assert(psczPath && *psczPath); | ||
448 | |||
449 | HRESULT hr = S_OK; | ||
450 | SIZE_T cchPath = 0; | ||
451 | size_t cchLength = 0; | ||
452 | |||
453 | hr = StrMaxLength(*psczPath, &cchPath); | ||
454 | PathExitOnFailure(hr, "Failed to get size of path string."); | ||
455 | |||
456 | hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); | ||
457 | PathExitOnFailure(hr, "Failed to get length of path."); | ||
458 | |||
459 | if (L'\\' != (*psczPath)[cchLength - 1]) | ||
460 | { | ||
461 | hr = StrAllocConcat(psczPath, L"\\", 1); | ||
462 | PathExitOnFailure(hr, "Failed to concat backslash onto string."); | ||
463 | } | ||
464 | |||
465 | LExit: | ||
466 | return hr; | ||
467 | } | ||
468 | |||
469 | |||
470 | DAPI_(HRESULT) PathForCurrentProcess( | ||
471 | __inout LPWSTR *psczFullPath, | ||
472 | __in_opt HMODULE hModule | ||
473 | ) | ||
474 | { | ||
475 | HRESULT hr = S_OK; | ||
476 | DWORD cch = MAX_PATH; | ||
477 | |||
478 | do | ||
479 | { | ||
480 | hr = StrAlloc(psczFullPath, cch); | ||
481 | PathExitOnFailure(hr, "Failed to allocate string for module path."); | ||
482 | |||
483 | DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); | ||
484 | if (0 == cchRequired) | ||
485 | { | ||
486 | PathExitWithLastError(hr, "Failed to get path for executing process."); | ||
487 | } | ||
488 | else if (cchRequired == cch) | ||
489 | { | ||
490 | cch = cchRequired + 1; | ||
491 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
492 | } | ||
493 | else | ||
494 | { | ||
495 | hr = S_OK; | ||
496 | } | ||
497 | } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); | ||
498 | |||
499 | LExit: | ||
500 | return hr; | ||
501 | } | ||
502 | |||
503 | |||
504 | DAPI_(HRESULT) PathRelativeToModule( | ||
505 | __inout LPWSTR *psczFullPath, | ||
506 | __in_opt LPCWSTR wzFileName, | ||
507 | __in_opt HMODULE hModule | ||
508 | ) | ||
509 | { | ||
510 | HRESULT hr = PathForCurrentProcess(psczFullPath, hModule); | ||
511 | PathExitOnFailure(hr, "Failed to get current module path."); | ||
512 | |||
513 | hr = PathGetDirectory(*psczFullPath, psczFullPath); | ||
514 | PathExitOnFailure(hr, "Failed to get current module directory."); | ||
515 | |||
516 | if (wzFileName) | ||
517 | { | ||
518 | hr = PathConcat(*psczFullPath, wzFileName, psczFullPath); | ||
519 | PathExitOnFailure(hr, "Failed to append filename."); | ||
520 | } | ||
521 | |||
522 | LExit: | ||
523 | return hr; | ||
524 | } | ||
525 | |||
526 | |||
527 | DAPI_(HRESULT) PathCreateTempFile( | ||
528 | __in_opt LPCWSTR wzDirectory, | ||
529 | __in_opt __format_string LPCWSTR wzFileNameTemplate, | ||
530 | __in DWORD dwUniqueCount, | ||
531 | __in DWORD dwFileAttributes, | ||
532 | __out_opt LPWSTR* psczTempFile, | ||
533 | __out_opt HANDLE* phTempFile | ||
534 | ) | ||
535 | { | ||
536 | AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); | ||
537 | |||
538 | HRESULT hr = S_OK; | ||
539 | |||
540 | LPWSTR sczTempPath = NULL; | ||
541 | DWORD cchTempPath = MAX_PATH; | ||
542 | |||
543 | HANDLE hTempFile = INVALID_HANDLE_VALUE; | ||
544 | LPWSTR scz = NULL; | ||
545 | LPWSTR sczTempFile = NULL; | ||
546 | |||
547 | if (wzDirectory && *wzDirectory) | ||
548 | { | ||
549 | hr = StrAllocString(&sczTempPath, wzDirectory, 0); | ||
550 | PathExitOnFailure(hr, "Failed to copy temp path."); | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | hr = StrAlloc(&sczTempPath, cchTempPath); | ||
555 | PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); | ||
556 | |||
557 | if (!::GetTempPathW(cchTempPath, sczTempPath)) | ||
558 | { | ||
559 | PathExitWithLastError(hr, "Failed to get temp path."); | ||
560 | } | ||
561 | } | ||
562 | |||
563 | if (wzFileNameTemplate && *wzFileNameTemplate) | ||
564 | { | ||
565 | for (DWORD i = 1; i <= dwUniqueCount && INVALID_HANDLE_VALUE == hTempFile; ++i) | ||
566 | { | ||
567 | hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); | ||
568 | PathExitOnFailure(hr, "Failed to allocate memory for file template."); | ||
569 | |||
570 | hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); | ||
571 | PathExitOnFailure(hr, "Failed to allocate temp file name."); | ||
572 | |||
573 | hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); | ||
574 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
575 | { | ||
576 | // if the file already exists, just try again | ||
577 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
578 | if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) | ||
579 | { | ||
580 | hr = S_OK; | ||
581 | } | ||
582 | PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile); | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | |||
587 | // If we were not able to or we did not try to create a temp file, ask | ||
588 | // the system to provide us a temp file using its built-in mechanism. | ||
589 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
590 | { | ||
591 | hr = StrAlloc(&sczTempFile, MAX_PATH); | ||
592 | PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); | ||
593 | |||
594 | if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile)) | ||
595 | { | ||
596 | PathExitWithLastError(hr, "Failed to create new temp file name."); | ||
597 | } | ||
598 | |||
599 | hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); | ||
600 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
601 | { | ||
602 | PathExitWithLastError(hr, "Failed to open new temp file: %ls", sczTempFile); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | // If the caller wanted the temp file name or handle, return them here. | ||
607 | if (psczTempFile) | ||
608 | { | ||
609 | hr = StrAllocString(psczTempFile, sczTempFile, 0); | ||
610 | PathExitOnFailure(hr, "Failed to copy temp file string."); | ||
611 | } | ||
612 | |||
613 | if (phTempFile) | ||
614 | { | ||
615 | *phTempFile = hTempFile; | ||
616 | hTempFile = INVALID_HANDLE_VALUE; | ||
617 | } | ||
618 | |||
619 | LExit: | ||
620 | if (INVALID_HANDLE_VALUE != hTempFile) | ||
621 | { | ||
622 | ::CloseHandle(hTempFile); | ||
623 | } | ||
624 | |||
625 | ReleaseStr(scz); | ||
626 | ReleaseStr(sczTempFile); | ||
627 | ReleaseStr(sczTempPath); | ||
628 | |||
629 | return hr; | ||
630 | } | ||
631 | |||
632 | |||
633 | DAPI_(HRESULT) PathCreateTimeBasedTempFile( | ||
634 | __in_z_opt LPCWSTR wzDirectory, | ||
635 | __in_z LPCWSTR wzPrefix, | ||
636 | __in_z_opt LPCWSTR wzPostfix, | ||
637 | __in_z LPCWSTR wzExtension, | ||
638 | __deref_opt_out_z LPWSTR* psczTempFile, | ||
639 | __out_opt HANDLE* phTempFile | ||
640 | ) | ||
641 | { | ||
642 | HRESULT hr = S_OK; | ||
643 | BOOL fRetry = FALSE; | ||
644 | WCHAR wzTempPath[MAX_PATH] = { }; | ||
645 | LPWSTR sczPrefix = NULL; | ||
646 | LPWSTR sczPrefixFolder = NULL; | ||
647 | SYSTEMTIME time = { }; | ||
648 | |||
649 | LPWSTR sczTempPath = NULL; | ||
650 | HANDLE hTempFile = INVALID_HANDLE_VALUE; | ||
651 | DWORD dwAttempts = 0; | ||
652 | |||
653 | if (wzDirectory && *wzDirectory) | ||
654 | { | ||
655 | hr = PathConcat(wzDirectory, wzPrefix, &sczPrefix); | ||
656 | PathExitOnFailure(hr, "Failed to combine directory and log prefix."); | ||
657 | } | ||
658 | else | ||
659 | { | ||
660 | if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) | ||
661 | { | ||
662 | PathExitWithLastError(hr, "Failed to get temp folder."); | ||
663 | } | ||
664 | |||
665 | hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); | ||
666 | PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); | ||
667 | } | ||
668 | |||
669 | hr = PathGetDirectory(sczPrefix, &sczPrefixFolder); | ||
670 | if (S_OK == hr) | ||
671 | { | ||
672 | hr = DirEnsureExists(sczPrefixFolder, NULL); | ||
673 | PathExitOnFailure(hr, "Failed to ensure temp file path exists: %ls", sczPrefixFolder); | ||
674 | } | ||
675 | |||
676 | if (!wzPostfix) | ||
677 | { | ||
678 | wzPostfix = L""; | ||
679 | } | ||
680 | |||
681 | do | ||
682 | { | ||
683 | fRetry = FALSE; | ||
684 | ++dwAttempts; | ||
685 | |||
686 | ::GetLocalTime(&time); | ||
687 | |||
688 | // Log format: pre YYYY MM dd hh mm ss post ext | ||
689 | hr = StrAllocFormatted(&sczTempPath, L"%ls_%04u%02u%02u%02u%02u%02u%ls%ls%ls", sczPrefix, time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, wzPostfix, L'.' == *wzExtension ? L"" : L".", wzExtension); | ||
690 | PathExitOnFailure(hr, "failed to allocate memory for the temp path"); | ||
691 | |||
692 | hTempFile = ::CreateFileW(sczTempPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); | ||
693 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
694 | { | ||
695 | // If the file already exists, just try again. | ||
696 | DWORD er = ::GetLastError(); | ||
697 | if (ERROR_FILE_EXISTS == er || ERROR_ACCESS_DENIED == er) | ||
698 | { | ||
699 | ::Sleep(100); | ||
700 | |||
701 | if (10 > dwAttempts) | ||
702 | { | ||
703 | er = ERROR_SUCCESS; | ||
704 | fRetry = TRUE; | ||
705 | } | ||
706 | } | ||
707 | |||
708 | hr = HRESULT_FROM_WIN32(er); | ||
709 | PathExitOnFailureDebugTrace(hr, "Failed to create temp file: %ls", sczTempPath); | ||
710 | } | ||
711 | } while (fRetry); | ||
712 | |||
713 | if (psczTempFile) | ||
714 | { | ||
715 | hr = StrAllocString(psczTempFile, sczTempPath, 0); | ||
716 | PathExitOnFailure(hr, "Failed to copy temp path to return."); | ||
717 | } | ||
718 | |||
719 | if (phTempFile) | ||
720 | { | ||
721 | *phTempFile = hTempFile; | ||
722 | hTempFile = INVALID_HANDLE_VALUE; | ||
723 | } | ||
724 | |||
725 | LExit: | ||
726 | ReleaseFile(hTempFile); | ||
727 | ReleaseStr(sczTempPath); | ||
728 | ReleaseStr(sczPrefixFolder); | ||
729 | ReleaseStr(sczPrefix); | ||
730 | |||
731 | return hr; | ||
732 | } | ||
733 | |||
734 | |||
735 | DAPI_(HRESULT) PathCreateTempDirectory( | ||
736 | __in_opt LPCWSTR wzDirectory, | ||
737 | __in __format_string LPCWSTR wzDirectoryNameTemplate, | ||
738 | __in DWORD dwUniqueCount, | ||
739 | __out LPWSTR* psczTempDirectory | ||
740 | ) | ||
741 | { | ||
742 | AssertSz(wzDirectoryNameTemplate && *wzDirectoryNameTemplate, "DirectoryNameTemplate must be specified."); | ||
743 | AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); | ||
744 | |||
745 | HRESULT hr = S_OK; | ||
746 | |||
747 | LPWSTR sczTempPath = NULL; | ||
748 | DWORD cchTempPath = MAX_PATH; | ||
749 | |||
750 | LPWSTR scz = NULL; | ||
751 | |||
752 | if (wzDirectory && *wzDirectory) | ||
753 | { | ||
754 | hr = StrAllocString(&sczTempPath, wzDirectory, 0); | ||
755 | PathExitOnFailure(hr, "Failed to copy temp path."); | ||
756 | |||
757 | hr = PathBackslashTerminate(&sczTempPath); | ||
758 | PathExitOnFailure(hr, "Failed to ensure path ends in backslash: %ls", wzDirectory); | ||
759 | } | ||
760 | else | ||
761 | { | ||
762 | hr = StrAlloc(&sczTempPath, cchTempPath); | ||
763 | PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); | ||
764 | |||
765 | if (!::GetTempPathW(cchTempPath, sczTempPath)) | ||
766 | { | ||
767 | PathExitWithLastError(hr, "Failed to get temp path."); | ||
768 | } | ||
769 | } | ||
770 | |||
771 | for (DWORD i = 1; i <= dwUniqueCount; ++i) | ||
772 | { | ||
773 | hr = StrAllocFormatted(&scz, wzDirectoryNameTemplate, i); | ||
774 | PathExitOnFailure(hr, "Failed to allocate memory for directory name template."); | ||
775 | |||
776 | hr = StrAllocFormatted(psczTempDirectory, L"%s%s", sczTempPath, scz); | ||
777 | PathExitOnFailure(hr, "Failed to allocate temp directory name."); | ||
778 | |||
779 | if (!::CreateDirectoryW(*psczTempDirectory, NULL)) | ||
780 | { | ||
781 | DWORD er = ::GetLastError(); | ||
782 | if (ERROR_ALREADY_EXISTS == er) | ||
783 | { | ||
784 | hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); | ||
785 | continue; | ||
786 | } | ||
787 | else if (ERROR_PATH_NOT_FOUND == er) | ||
788 | { | ||
789 | hr = DirEnsureExists(*psczTempDirectory, NULL); | ||
790 | break; | ||
791 | } | ||
792 | else | ||
793 | { | ||
794 | hr = HRESULT_FROM_WIN32(er); | ||
795 | break; | ||
796 | } | ||
797 | } | ||
798 | else | ||
799 | { | ||
800 | hr = S_OK; | ||
801 | break; | ||
802 | } | ||
803 | } | ||
804 | PathExitOnFailure(hr, "Failed to create temp directory."); | ||
805 | |||
806 | hr = PathBackslashTerminate(psczTempDirectory); | ||
807 | PathExitOnFailure(hr, "Failed to ensure temp directory is backslash terminated."); | ||
808 | |||
809 | LExit: | ||
810 | ReleaseStr(scz); | ||
811 | ReleaseStr(sczTempPath); | ||
812 | |||
813 | return hr; | ||
814 | } | ||
815 | |||
816 | |||
817 | DAPI_(HRESULT) PathGetKnownFolder( | ||
818 | __in int csidl, | ||
819 | __out LPWSTR* psczKnownFolder | ||
820 | ) | ||
821 | { | ||
822 | HRESULT hr = S_OK; | ||
823 | |||
824 | hr = StrAlloc(psczKnownFolder, MAX_PATH); | ||
825 | PathExitOnFailure(hr, "Failed to allocate memory for known folder."); | ||
826 | |||
827 | hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); | ||
828 | PathExitOnFailure(hr, "Failed to get known folder path."); | ||
829 | |||
830 | hr = PathBackslashTerminate(psczKnownFolder); | ||
831 | PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); | ||
832 | |||
833 | LExit: | ||
834 | return hr; | ||
835 | } | ||
836 | |||
837 | |||
838 | DAPI_(BOOL) PathIsAbsolute( | ||
839 | __in_z LPCWSTR wzPath | ||
840 | ) | ||
841 | { | ||
842 | return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); | ||
843 | } | ||
844 | |||
845 | |||
846 | DAPI_(HRESULT) PathConcat( | ||
847 | __in_opt LPCWSTR wzPath1, | ||
848 | __in_opt LPCWSTR wzPath2, | ||
849 | __deref_out_z LPWSTR* psczCombined | ||
850 | ) | ||
851 | { | ||
852 | return PathConcatCch(wzPath1, 0, wzPath2, 0, psczCombined); | ||
853 | } | ||
854 | |||
855 | |||
856 | DAPI_(HRESULT) PathConcatCch( | ||
857 | __in_opt LPCWSTR wzPath1, | ||
858 | __in SIZE_T cchPath1, | ||
859 | __in_opt LPCWSTR wzPath2, | ||
860 | __in SIZE_T cchPath2, | ||
861 | __deref_out_z LPWSTR* psczCombined | ||
862 | ) | ||
863 | { | ||
864 | HRESULT hr = S_OK; | ||
865 | |||
866 | if (!wzPath2 || !*wzPath2) | ||
867 | { | ||
868 | hr = StrAllocString(psczCombined, wzPath1, cchPath1); | ||
869 | PathExitOnFailure(hr, "Failed to copy just path1 to output."); | ||
870 | } | ||
871 | else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) | ||
872 | { | ||
873 | hr = StrAllocString(psczCombined, wzPath2, cchPath2); | ||
874 | PathExitOnFailure(hr, "Failed to copy just path2 to output."); | ||
875 | } | ||
876 | else | ||
877 | { | ||
878 | hr = StrAllocString(psczCombined, wzPath1, cchPath1); | ||
879 | PathExitOnFailure(hr, "Failed to copy path1 to output."); | ||
880 | |||
881 | hr = PathBackslashTerminate(psczCombined); | ||
882 | PathExitOnFailure(hr, "Failed to backslashify."); | ||
883 | |||
884 | hr = StrAllocConcat(psczCombined, wzPath2, cchPath2); | ||
885 | PathExitOnFailure(hr, "Failed to append path2 to output."); | ||
886 | } | ||
887 | |||
888 | LExit: | ||
889 | return hr; | ||
890 | } | ||
891 | |||
892 | |||
893 | DAPI_(HRESULT) PathEnsureQuoted( | ||
894 | __inout LPWSTR* ppszPath, | ||
895 | __in BOOL fDirectory | ||
896 | ) | ||
897 | { | ||
898 | Assert(ppszPath && *ppszPath); | ||
899 | |||
900 | HRESULT hr = S_OK; | ||
901 | size_t cchPath = 0; | ||
902 | |||
903 | hr = ::StringCchLengthW(*ppszPath, STRSAFE_MAX_CCH, &cchPath); | ||
904 | PathExitOnFailure(hr, "Failed to get the length of the path."); | ||
905 | |||
906 | // Handle simple special cases. | ||
907 | if (0 == cchPath || (1 == cchPath && L'"' == (*ppszPath)[0])) | ||
908 | { | ||
909 | hr = StrAllocString(ppszPath, L"\"\"", 2); | ||
910 | PathExitOnFailure(hr, "Failed to allocate a quoted empty string."); | ||
911 | |||
912 | ExitFunction(); | ||
913 | } | ||
914 | |||
915 | if (L'"' != (*ppszPath)[0]) | ||
916 | { | ||
917 | hr = StrAllocPrefix(ppszPath, L"\"", 1); | ||
918 | PathExitOnFailure(hr, "Failed to allocate an opening quote."); | ||
919 | |||
920 | // Add a char for the opening quote. | ||
921 | ++cchPath; | ||
922 | } | ||
923 | |||
924 | if (L'"' != (*ppszPath)[cchPath - 1]) | ||
925 | { | ||
926 | hr = StrAllocConcat(ppszPath, L"\"", 1); | ||
927 | PathExitOnFailure(hr, "Failed to allocate a closing quote."); | ||
928 | |||
929 | // Add a char for the closing quote. | ||
930 | ++cchPath; | ||
931 | } | ||
932 | |||
933 | if (fDirectory) | ||
934 | { | ||
935 | if (L'\\' != (*ppszPath)[cchPath - 2]) | ||
936 | { | ||
937 | // Change the last char to a backslash and re-append the closing quote. | ||
938 | (*ppszPath)[cchPath - 1] = L'\\'; | ||
939 | |||
940 | hr = StrAllocConcat(ppszPath, L"\"", 1); | ||
941 | PathExitOnFailure(hr, "Failed to allocate another closing quote after the backslash."); | ||
942 | } | ||
943 | } | ||
944 | |||
945 | LExit: | ||
946 | |||
947 | return hr; | ||
948 | } | ||
949 | |||
950 | |||
951 | DAPI_(HRESULT) PathCompare( | ||
952 | __in_z LPCWSTR wzPath1, | ||
953 | __in_z LPCWSTR wzPath2, | ||
954 | __out int* pnResult | ||
955 | ) | ||
956 | { | ||
957 | HRESULT hr = S_OK; | ||
958 | LPWSTR sczPath1 = NULL; | ||
959 | LPWSTR sczPath2 = NULL; | ||
960 | |||
961 | hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); | ||
962 | PathExitOnFailure(hr, "Failed to expand path1."); | ||
963 | |||
964 | hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); | ||
965 | PathExitOnFailure(hr, "Failed to expand path2."); | ||
966 | |||
967 | *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); | ||
968 | |||
969 | LExit: | ||
970 | ReleaseStr(sczPath2); | ||
971 | ReleaseStr(sczPath1); | ||
972 | |||
973 | return hr; | ||
974 | } | ||
975 | |||
976 | |||
977 | DAPI_(HRESULT) PathCompress( | ||
978 | __in_z LPCWSTR wzPath | ||
979 | ) | ||
980 | { | ||
981 | HRESULT hr = S_OK; | ||
982 | HANDLE hPath = INVALID_HANDLE_VALUE; | ||
983 | |||
984 | hPath = ::CreateFileW(wzPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | ||
985 | if (INVALID_HANDLE_VALUE == hPath) | ||
986 | { | ||
987 | PathExitWithLastError(hr, "Failed to open path %ls for compression.", wzPath); | ||
988 | } | ||
989 | |||
990 | DWORD dwBytesReturned = 0; | ||
991 | USHORT usCompressionFormat = COMPRESSION_FORMAT_DEFAULT; | ||
992 | if (0 == ::DeviceIoControl(hPath, FSCTL_SET_COMPRESSION, &usCompressionFormat, sizeof(usCompressionFormat), NULL, 0, &dwBytesReturned, NULL)) | ||
993 | { | ||
994 | // ignore compression attempts on file systems that don't support it | ||
995 | DWORD er = ::GetLastError(); | ||
996 | if (ERROR_INVALID_FUNCTION != er) | ||
997 | { | ||
998 | PathExitOnWin32Error(er, hr, "Failed to set compression state for path %ls.", wzPath); | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | LExit: | ||
1003 | ReleaseFile(hPath); | ||
1004 | |||
1005 | return hr; | ||
1006 | } | ||
1007 | |||
1008 | DAPI_(HRESULT) PathGetHierarchyArray( | ||
1009 | __in_z LPCWSTR wzPath, | ||
1010 | __deref_inout_ecount_opt(*pcPathArray) LPWSTR **prgsczPathArray, | ||
1011 | __inout LPUINT pcPathArray | ||
1012 | ) | ||
1013 | { | ||
1014 | HRESULT hr = S_OK; | ||
1015 | LPWSTR sczPathCopy = NULL; | ||
1016 | LPWSTR sczNewPathCopy = NULL; | ||
1017 | DWORD cArraySpacesNeeded = 0; | ||
1018 | size_t cchPath = 0; | ||
1019 | |||
1020 | hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); | ||
1021 | PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); | ||
1022 | |||
1023 | if (!cchPath) | ||
1024 | { | ||
1025 | ExitFunction1(hr = E_INVALIDARG); | ||
1026 | } | ||
1027 | |||
1028 | for (size_t i = 0; i < cchPath; ++i) | ||
1029 | { | ||
1030 | if (wzPath[i] == L'\\') | ||
1031 | { | ||
1032 | ++cArraySpacesNeeded; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | if (wzPath[cchPath - 1] != L'\\') | ||
1037 | { | ||
1038 | ++cArraySpacesNeeded; | ||
1039 | } | ||
1040 | |||
1041 | // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. | ||
1042 | if (wzPath[0] == L'\\' && wzPath[1] == L'\\') | ||
1043 | { | ||
1044 | cArraySpacesNeeded -= 3; | ||
1045 | } | ||
1046 | |||
1047 | Assert(cArraySpacesNeeded >= 1); | ||
1048 | |||
1049 | hr = MemEnsureArraySize(reinterpret_cast<void **>(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); | ||
1050 | PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); | ||
1051 | *pcPathArray = cArraySpacesNeeded; | ||
1052 | |||
1053 | hr = StrAllocString(&sczPathCopy, wzPath, 0); | ||
1054 | PathExitOnFailure(hr, "Failed to allocate copy of original path"); | ||
1055 | |||
1056 | for (DWORD i = 0; i < cArraySpacesNeeded; ++i) | ||
1057 | { | ||
1058 | hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); | ||
1059 | PathExitOnFailure(hr, "Failed to copy path"); | ||
1060 | |||
1061 | DWORD cchPathCopy = lstrlenW(sczPathCopy); | ||
1062 | |||
1063 | // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path | ||
1064 | if (wzPath[cchPathCopy - 1] == L'\\') | ||
1065 | { | ||
1066 | sczPathCopy[cchPathCopy - 1] = L'\0'; | ||
1067 | } | ||
1068 | |||
1069 | hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); | ||
1070 | PathExitOnFailure(hr, "Failed to get directory portion of path"); | ||
1071 | |||
1072 | ReleaseStr(sczPathCopy); | ||
1073 | sczPathCopy = sczNewPathCopy; | ||
1074 | sczNewPathCopy = NULL; | ||
1075 | } | ||
1076 | |||
1077 | hr = S_OK; | ||
1078 | |||
1079 | LExit: | ||
1080 | ReleaseStr(sczPathCopy); | ||
1081 | |||
1082 | return hr; | ||
1083 | } | ||