aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/fileutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/fileutil.cpp2032
1 files changed, 2032 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
new file mode 100644
index 00000000..1822727a
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
@@ -0,0 +1,2032 @@
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 FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
8#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
9#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
10#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
11#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
12#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__)
13#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
14#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
15#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__)
16#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__)
17#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__)
18#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__)
19
20// constants
21
22const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
23const BYTE UTF16BOM[] = {0xFF, 0xFE};
24
25const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
26const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
27
28/*******************************************************************
29 FileFromPath - returns a pointer to the file part of the path
30
31********************************************************************/
32extern "C" LPWSTR DAPI FileFromPath(
33 __in_z LPCWSTR wzPath
34 )
35{
36 if (!wzPath)
37 return NULL;
38
39 LPWSTR wzFile = const_cast<LPWSTR>(wzPath);
40 for (LPWSTR wz = wzFile; *wz; ++wz)
41 {
42 // valid delineators
43 // \ => Windows path
44 // / => unix and URL path
45 // : => relative path from mapped root
46 if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
47 wzFile = wz + 1;
48 }
49
50 return wzFile;
51}
52
53
54/*******************************************************************
55 FileResolvePath - gets the full path to a file resolving environment
56 variables along the way.
57
58********************************************************************/
59extern "C" HRESULT DAPI FileResolvePath(
60 __in_z LPCWSTR wzRelativePath,
61 __out LPWSTR *ppwzFullPath
62 )
63{
64 Assert(wzRelativePath && *wzRelativePath);
65
66 HRESULT hr = S_OK;
67 DWORD cch = 0;
68 LPWSTR pwzExpandedPath = NULL;
69 DWORD cchExpandedPath = 0;
70
71 LPWSTR pwzFullPath = NULL;
72 DWORD cchFullPath = 0;
73
74 LPWSTR wzFileName = NULL;
75
76 //
77 // First, expand any environment variables.
78 //
79 cchExpandedPath = MAX_PATH;
80 hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
81 FileExitOnFailure(hr, "Failed to allocate space for expanded path.");
82
83 cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
84 if (0 == cch)
85 {
86 FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
87 }
88 else if (cchExpandedPath < cch)
89 {
90 cchExpandedPath = cch;
91 hr = StrAlloc(&pwzExpandedPath, cchExpandedPath);
92 FileExitOnFailure(hr, "Failed to re-allocate more space for expanded path.");
93
94 cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath);
95 if (0 == cch)
96 {
97 FileExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath);
98 }
99 else if (cchExpandedPath < cch)
100 {
101 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
102 FileExitOnRootFailure(hr, "Failed to allocate buffer for expanded path.");
103 }
104 }
105
106 //
107 // Second, get the full path.
108 //
109 cchFullPath = MAX_PATH;
110 hr = StrAlloc(&pwzFullPath, cchFullPath);
111 FileExitOnFailure(hr, "Failed to allocate space for full path.");
112
113 cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
114 if (0 == cch)
115 {
116 FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
117 }
118 else if (cchFullPath < cch)
119 {
120 cchFullPath = cch;
121 hr = StrAlloc(&pwzFullPath, cchFullPath);
122 FileExitOnFailure(hr, "Failed to re-allocate more space for full path.");
123
124 cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName);
125 if (0 == cch)
126 {
127 FileExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath);
128 }
129 else if (cchFullPath < cch)
130 {
131 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
132 FileExitOnRootFailure(hr, "Failed to allocate buffer for full path.");
133 }
134 }
135
136 *ppwzFullPath = pwzFullPath;
137 pwzFullPath = NULL;
138
139LExit:
140 ReleaseStr(pwzFullPath);
141 ReleaseStr(pwzExpandedPath);
142
143 return hr;
144}
145
146
147/*******************************************************************
148FileStripExtension - Strip extension from filename
149********************************************************************/
150extern "C" HRESULT DAPI FileStripExtension(
151__in_z LPCWSTR wzFileName,
152__out LPWSTR *ppwzFileNameNoExtension
153)
154{
155 Assert(wzFileName && *wzFileName);
156
157 HRESULT hr = S_OK;
158 size_t cchFileName = 0;
159 LPWSTR pwzFileNameNoExtension = NULL;
160 size_t cchFileNameNoExtension = 0;
161 errno_t err = 0;
162
163 hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_LENGTH, &cchFileName);
164 FileExitOnRootFailure(hr, "failed to get length of file name: %ls", wzFileName);
165
166 cchFileNameNoExtension = cchFileName + 1;
167
168 hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension);
169 FileExitOnFailure(hr, "failed to allocate space for File Name without extension");
170
171 // _wsplitpath_s can handle drive/path/filename/extension
172 err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL);
173 if (err)
174 {
175 hr = E_INVALIDARG;
176 FileExitOnRootFailure(hr, "failed to parse filename: '%ls', error: %d", wzFileName, err);
177 }
178
179 *ppwzFileNameNoExtension = pwzFileNameNoExtension;
180 pwzFileNameNoExtension = NULL;
181
182LExit:
183 ReleaseStr(pwzFileNameNoExtension);
184
185 return hr;
186}
187
188
189/*******************************************************************
190FileChangeExtension - Changes the extension of a filename
191********************************************************************/
192extern "C" HRESULT DAPI FileChangeExtension(
193 __in_z LPCWSTR wzFileName,
194 __in_z LPCWSTR wzNewExtension,
195 __out LPWSTR *ppwzFileNameNewExtension
196 )
197{
198 Assert(wzFileName && *wzFileName);
199
200 HRESULT hr = S_OK;
201 LPWSTR sczFileName = NULL;
202
203 hr = FileStripExtension(wzFileName, &sczFileName);
204 FileExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName);
205
206 hr = StrAllocConcat(&sczFileName, wzNewExtension, 0);
207 FileExitOnFailure(hr, "Failed to add new extension.");
208
209 *ppwzFileNameNewExtension = sczFileName;
210 sczFileName = NULL;
211
212LExit:
213 ReleaseStr(sczFileName);
214
215 return hr;
216}
217
218
219/*******************************************************************
220FileAddSuffixToBaseName - Adds a suffix the base portion of a file
221name; e.g., file.ext to fileSuffix.ext.
222********************************************************************/
223extern "C" HRESULT DAPI FileAddSuffixToBaseName(
224 __in_z LPCWSTR wzFileName,
225 __in_z LPCWSTR wzSuffix,
226 __out_z LPWSTR* psczNewFileName
227 )
228{
229 Assert(wzFileName && *wzFileName);
230
231 HRESULT hr = S_OK;
232 LPWSTR sczNewFileName = NULL;
233 size_t cchFileName = 0;
234
235 hr = ::StringCchLengthW(wzFileName, STRSAFE_MAX_CCH, &cchFileName);
236 FileExitOnRootFailure(hr, "Failed to get length of file name: %ls", wzFileName);
237
238 LPCWSTR wzExtension = wzFileName + cchFileName;
239 while (wzFileName < wzExtension && L'.' != *wzExtension)
240 {
241 --wzExtension;
242 }
243
244 if (wzFileName < wzExtension)
245 {
246 // found an extension so add the suffix before it
247 hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast<int>(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension);
248 }
249 else
250 {
251 // no extension, so add the suffix at the end of the whole name
252 hr = StrAllocString(&sczNewFileName, wzFileName, 0);
253 FileExitOnFailure(hr, "Failed to allocate new file name.");
254
255 hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0);
256 }
257 FileExitOnFailure(hr, "Failed to allocate new file name with suffix.");
258
259 *psczNewFileName = sczNewFileName;
260 sczNewFileName = NULL;
261
262LExit:
263 ReleaseStr(sczNewFileName);
264
265 return hr;
266}
267
268
269/*******************************************************************
270 FileVersion
271
272********************************************************************/
273extern "C" HRESULT DAPI FileVersion(
274 __in_z LPCWSTR wzFilename,
275 __out DWORD *pdwVerMajor,
276 __out DWORD* pdwVerMinor
277 )
278{
279 HRESULT hr = S_OK;
280
281 DWORD dwHandle = 0;
282 UINT cbVerBuffer = 0;
283 LPVOID pVerBuffer = NULL;
284 VS_FIXEDFILEINFO* pvsFileInfo = NULL;
285 UINT cbFileInfo = 0;
286
287 if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle)))
288 {
289 FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
290 }
291
292 pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer);
293 FileExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename);
294
295 if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer))
296 {
297 FileExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename);
298 }
299
300 if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo))
301 {
302 FileExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename);
303 }
304
305 *pdwVerMajor = pvsFileInfo->dwFileVersionMS;
306 *pdwVerMinor = pvsFileInfo->dwFileVersionLS;
307
308LExit:
309 if (pVerBuffer)
310 {
311 ::GlobalFree(pVerBuffer);
312 }
313 return hr;
314}
315
316
317/*******************************************************************
318 FileVersionFromString
319
320*******************************************************************/
321extern "C" HRESULT DAPI FileVersionFromString(
322 __in_z LPCWSTR wzVersion,
323 __out DWORD* pdwVerMajor,
324 __out DWORD* pdwVerMinor
325 )
326{
327 Assert(pdwVerMajor && pdwVerMinor);
328
329 HRESULT hr = S_OK;
330 LPCWSTR pwz = wzVersion;
331 DWORD dw;
332
333 *pdwVerMajor = 0;
334 *pdwVerMinor = 0;
335
336 if ((L'v' == *pwz) || (L'V' == *pwz))
337 {
338 ++pwz;
339 }
340
341 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
342 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
343 {
344 *pdwVerMajor = dw << 16;
345
346 if (!*pwz)
347 {
348 ExitFunction1(hr = S_OK);
349 }
350 ++pwz;
351 }
352 else
353 {
354 ExitFunction1(hr = S_FALSE);
355 }
356
357 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
358 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
359 {
360 *pdwVerMajor |= dw;
361
362 if (!*pwz)
363 {
364 ExitFunction1(hr = S_OK);
365 }
366 ++pwz;
367 }
368 else
369 {
370 ExitFunction1(hr = S_FALSE);
371 }
372
373 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
374 if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz)
375 {
376 *pdwVerMinor = dw << 16;
377
378 if (!*pwz)
379 {
380 ExitFunction1(hr = S_OK);
381 }
382 ++pwz;
383 }
384 else
385 {
386 ExitFunction1(hr = S_FALSE);
387 }
388
389 dw = wcstoul(pwz, (WCHAR**)&pwz, 10);
390 if (pwz && L'\0' == *pwz && dw < 0x10000)
391 {
392 *pdwVerMinor |= dw;
393 }
394 else
395 {
396 ExitFunction1(hr = S_FALSE);
397 }
398
399LExit:
400 return hr;
401}
402
403
404/*******************************************************************
405 FileVersionFromStringEx
406
407*******************************************************************/
408extern "C" HRESULT DAPI FileVersionFromStringEx(
409 __in_z LPCWSTR wzVersion,
410 __in SIZE_T cchVersion,
411 __out DWORD64* pqwVersion
412 )
413{
414 Assert(wzVersion);
415 Assert(pqwVersion);
416
417 HRESULT hr = S_OK;
418 LPCWSTR wzEnd = NULL;
419 LPCWSTR wzPartBegin = wzVersion;
420 LPCWSTR wzPartEnd = wzVersion;
421 DWORD iPart = 0;
422 USHORT us = 0;
423 DWORD64 qwVersion = 0;
424
425 // get string length if not provided
426 if (0 >= cchVersion)
427 {
428 hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchVersion));
429 FileExitOnRootFailure(hr, "Failed to get length of file version string: %ls", wzVersion);
430
431 if (0 >= cchVersion)
432 {
433 ExitFunction1(hr = E_INVALIDARG);
434 }
435 }
436
437 if ((L'v' == *wzVersion) || (L'V' == *wzVersion))
438 {
439 ++wzVersion;
440 --cchVersion;
441 wzPartBegin = wzVersion;
442 wzPartEnd = wzVersion;
443 }
444
445 // save end pointer
446 wzEnd = wzVersion + cchVersion;
447
448 // loop through parts
449 for (;;)
450 {
451 if (4 <= iPart)
452 {
453 // error, too many parts
454 ExitFunction1(hr = E_INVALIDARG);
455 }
456
457 // find end of part
458 while (wzPartEnd < wzEnd && L'.' != *wzPartEnd)
459 {
460 ++wzPartEnd;
461 }
462 if (wzPartBegin == wzPartEnd)
463 {
464 // error, empty part
465 ExitFunction1(hr = E_INVALIDARG);
466 }
467
468 DWORD cchPart;
469 hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
470 FileExitOnFailure(hr, "Version number part was too long.");
471
472 // parse version part
473 hr = StrStringToUInt16(wzPartBegin, cchPart, &us);
474 FileExitOnFailure(hr, "Failed to parse version number part.");
475
476 // add part to qword version
477 qwVersion |= (DWORD64)us << ((3 - iPart) * 16);
478
479 if (wzPartEnd >= wzEnd)
480 {
481 // end of string
482 break;
483 }
484
485 wzPartBegin = ++wzPartEnd; // skip over separator
486 ++iPart;
487 }
488
489 *pqwVersion = qwVersion;
490
491LExit:
492 return hr;
493}
494
495/*******************************************************************
496 FileVersionFromStringEx - Formats the DWORD64 as a string version.
497
498*******************************************************************/
499extern "C" HRESULT DAPI FileVersionToStringEx(
500 __in DWORD64 qwVersion,
501 __out LPWSTR* psczVersion
502 )
503{
504 HRESULT hr = S_OK;
505 WORD wMajor = 0;
506 WORD wMinor = 0;
507 WORD wBuild = 0;
508 WORD wRevision = 0;
509
510 // Mask and shift each WORD for each field.
511 wMajor = (WORD)(qwVersion >> 48 & 0xffff);
512 wMinor = (WORD)(qwVersion >> 32 & 0xffff);
513 wBuild = (WORD)(qwVersion >> 16 & 0xffff);
514 wRevision = (WORD)(qwVersion & 0xffff);
515
516 // Format and return the version string.
517 hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision);
518 FileExitOnFailure(hr, "Failed to allocate and format the version number.");
519
520LExit:
521 return hr;
522}
523
524/*******************************************************************
525 FileSetPointer - sets the file pointer.
526
527********************************************************************/
528extern "C" HRESULT DAPI FileSetPointer(
529 __in HANDLE hFile,
530 __in DWORD64 dw64Move,
531 __out_opt DWORD64* pdw64NewPosition,
532 __in DWORD dwMoveMethod
533 )
534{
535 Assert(INVALID_HANDLE_VALUE != hFile);
536
537 HRESULT hr = S_OK;
538 LARGE_INTEGER liMove;
539 LARGE_INTEGER liNewPosition;
540
541 liMove.QuadPart = dw64Move;
542 if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod))
543 {
544 FileExitWithLastError(hr, "Failed to set file pointer.");
545 }
546
547 if (pdw64NewPosition)
548 {
549 *pdw64NewPosition = liNewPosition.QuadPart;
550 }
551
552LExit:
553 return hr;
554}
555
556
557/*******************************************************************
558 FileSize
559
560********************************************************************/
561extern "C" HRESULT DAPI FileSize(
562 __in_z LPCWSTR pwzFileName,
563 __out LONGLONG* pllSize
564 )
565{
566 HRESULT hr = S_OK;
567 HANDLE hFile = INVALID_HANDLE_VALUE;
568
569 FileExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided");
570
571 hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
572 if (INVALID_HANDLE_VALUE == hFile)
573 {
574 FileExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName);
575 }
576
577 hr = FileSizeByHandle(hFile, pllSize);
578 FileExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName);
579
580LExit:
581 ReleaseFileHandle(hFile);
582
583 return hr;
584}
585
586
587/*******************************************************************
588 FileSizeByHandle
589
590********************************************************************/
591extern "C" HRESULT DAPI FileSizeByHandle(
592 __in HANDLE hFile,
593 __out LONGLONG* pllSize
594 )
595{
596 Assert(INVALID_HANDLE_VALUE != hFile && pllSize);
597 HRESULT hr = S_OK;
598 LARGE_INTEGER li;
599
600 *pllSize = 0;
601
602 if (!::GetFileSizeEx(hFile, &li))
603 {
604 FileExitWithLastError(hr, "Failed to get size of file.");
605 }
606
607 *pllSize = li.QuadPart;
608
609LExit:
610 return hr;
611}
612
613
614/*******************************************************************
615 FileExistsEx
616
617********************************************************************/
618extern "C" BOOL DAPI FileExistsEx(
619 __in_z LPCWSTR wzPath,
620 __out_opt DWORD *pdwAttributes
621 )
622{
623 Assert(wzPath && *wzPath);
624 BOOL fExists = FALSE;
625
626 WIN32_FIND_DATAW fd = { };
627 HANDLE hff;
628
629 if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd)))
630 {
631 ::FindClose(hff);
632 if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
633 {
634 if (pdwAttributes)
635 {
636 *pdwAttributes = fd.dwFileAttributes;
637 }
638
639 fExists = TRUE;
640 }
641 }
642
643 return fExists;
644}
645
646
647/*******************************************************************
648 FileExistsAfterRestart - checks that a file exists and will continue
649 to exist after restart.
650
651********************************************************************/
652extern "C" BOOL DAPI FileExistsAfterRestart(
653 __in_z LPCWSTR wzPath,
654 __out_opt DWORD *pdwAttributes
655 )
656{
657 HRESULT hr = S_OK;
658 BOOL fExists = FALSE;
659 HKEY hkPendingFileRename = NULL;
660 LPWSTR* rgsczRenames = NULL;
661 DWORD cRenames = 0;
662 int nCompare = 0;
663
664 fExists = FileExistsEx(wzPath, pdwAttributes);
665 if (fExists)
666 {
667 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename);
668 if (E_FILENOTFOUND == hr)
669 {
670 ExitFunction1(hr = S_OK);
671 }
672 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
673
674 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
675 if (E_FILENOTFOUND == hr)
676 {
677 ExitFunction1(hr = S_OK);
678 }
679 FileExitOnFailure(hr, "Failed to read pending file renames.");
680
681 // The pending file renames array is pairs of source and target paths. We only care
682 // about checking the source paths so skip the target paths (i += 2).
683 for (DWORD i = 0; i < cRenames; i += 2)
684 {
685 LPWSTR wzRename = rgsczRenames[i];
686 if (wzRename && *wzRename)
687 {
688 // Skip the long path designator if present.
689 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
690 {
691 wzRename += 4;
692 }
693
694 hr = PathCompare(wzPath, wzRename, &nCompare);
695 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
696
697 if (CSTR_EQUAL == nCompare)
698 {
699 fExists = FALSE;
700 break;
701 }
702 }
703 }
704 }
705
706LExit:
707 ReleaseStrArray(rgsczRenames, cRenames);
708 ReleaseRegKey(hkPendingFileRename);
709
710 return fExists;
711}
712
713
714/*******************************************************************
715 FileRemoveFromPendingRename - removes the file path from the pending
716 file rename list.
717
718********************************************************************/
719extern "C" HRESULT DAPI FileRemoveFromPendingRename(
720 __in_z LPCWSTR wzPath
721 )
722{
723 HRESULT hr = S_OK;
724 HKEY hkPendingFileRename = NULL;
725 LPWSTR* rgsczRenames = NULL;
726 DWORD cRenames = 0;
727 int nCompare = 0;
728 BOOL fRemoved = FALSE;
729 DWORD cNewRenames = 0;
730
731 hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename);
732 if (E_FILENOTFOUND == hr)
733 {
734 ExitFunction1(hr = S_OK);
735 }
736 FileExitOnFailure(hr, "Failed to open pending file rename registry key.");
737
738 hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames);
739 if (E_FILENOTFOUND == hr)
740 {
741 ExitFunction1(hr = S_OK);
742 }
743 FileExitOnFailure(hr, "Failed to read pending file renames.");
744
745 // The pending file renames array is pairs of source and target paths. We only care
746 // about checking the source paths so skip the target paths (i += 2).
747 for (DWORD i = 0; i < cRenames; i += 2)
748 {
749 LPWSTR wzRename = rgsczRenames[i];
750 if (wzRename && *wzRename)
751 {
752 // Skip the long path designator if present.
753 if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3])
754 {
755 wzRename += 4;
756 }
757
758 hr = PathCompare(wzPath, wzRename, &nCompare);
759 FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path.");
760
761 // If we find our path in the list, null out the source and target slot and
762 // we'll compact the array next.
763 if (CSTR_EQUAL == nCompare)
764 {
765 ReleaseNullStr(rgsczRenames[i]);
766 ReleaseNullStr(rgsczRenames[i + 1]);
767 fRemoved = TRUE;
768 }
769 }
770 }
771
772 if (fRemoved)
773 {
774 // Compact the array by removing any nulls.
775 for (DWORD i = 0; i < cRenames; ++i)
776 {
777 LPWSTR wzRename = rgsczRenames[i];
778 if (wzRename)
779 {
780 rgsczRenames[cNewRenames] = wzRename;
781 ++cNewRenames;
782 }
783 }
784
785 cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already.
786
787 // Write the new array back to the pending file rename key.
788 hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames);
789 FileExitOnFailure(hr, "Failed to update pending file renames.");
790 }
791
792LExit:
793 ReleaseStrArray(rgsczRenames, cRenames);
794 ReleaseRegKey(hkPendingFileRename);
795
796 return hr;
797}
798
799
800/*******************************************************************
801 FileRead - read a file into memory
802
803********************************************************************/
804extern "C" HRESULT DAPI FileRead(
805 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
806 __out SIZE_T* pcbDest,
807 __in_z LPCWSTR wzSrcPath
808 )
809{
810 HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE);
811 return hr;
812}
813
814/*******************************************************************
815 FileRead - read a file into memory with specified share mode
816
817********************************************************************/
818extern "C" HRESULT DAPI FileReadEx(
819 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
820 __out SIZE_T* pcbDest,
821 __in_z LPCWSTR wzSrcPath,
822 __in DWORD dwShareMode
823 )
824{
825 HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode);
826 return hr;
827}
828
829/*******************************************************************
830 FileReadUntil - read a file into memory with a maximum size
831
832********************************************************************/
833extern "C" HRESULT DAPI FileReadUntil(
834 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
835 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
836 __in_z LPCWSTR wzSrcPath,
837 __in DWORD cbMaxRead
838 )
839{
840 HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE);
841 return hr;
842}
843
844
845/*******************************************************************
846 FileReadPartial - read a portion of a file into memory
847
848********************************************************************/
849extern "C" HRESULT DAPI FileReadPartial(
850 __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest,
851 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
852 __in_z LPCWSTR wzSrcPath,
853 __in BOOL fSeek,
854 __in DWORD cbStartPosition,
855 __in DWORD cbMaxRead,
856 __in BOOL fPartialOK
857 )
858{
859 return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE);
860}
861
862/*******************************************************************
863 FileReadPartial - read a portion of a file into memory
864 (with specified share mode)
865********************************************************************/
866extern "C" HRESULT DAPI FileReadPartialEx(
867 __deref_inout_bcount_full(*pcbDest) LPBYTE* ppbDest,
868 __out_range(<=, cbMaxRead) SIZE_T* pcbDest,
869 __in_z LPCWSTR wzSrcPath,
870 __in BOOL fSeek,
871 __in DWORD cbStartPosition,
872 __in DWORD cbMaxRead,
873 __in BOOL fPartialOK,
874 __in DWORD dwShareMode
875 )
876{
877 HRESULT hr = S_OK;
878
879 UINT er = ERROR_SUCCESS;
880 HANDLE hFile = INVALID_HANDLE_VALUE;
881 LARGE_INTEGER liFileSize = { };
882 DWORD cbData = 0;
883 BYTE* pbData = NULL;
884
885 FileExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest");
886 FileExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest");
887 FileExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath");
888 FileExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null");
889
890 hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
891 if (INVALID_HANDLE_VALUE == hFile)
892 {
893 er = ::GetLastError();
894 if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
895 {
896 ExitFunction1(hr = E_FILENOTFOUND);
897 }
898 FileExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath);
899 }
900
901 if (!::GetFileSizeEx(hFile, &liFileSize))
902 {
903 FileExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath);
904 }
905
906 if (fSeek)
907 {
908 if (cbStartPosition > liFileSize.QuadPart)
909 {
910 hr = E_INVALIDARG;
911 FileExitOnFailure(hr, "Start position %d bigger than file '%ls' size %llu", cbStartPosition, wzSrcPath, liFileSize.QuadPart);
912 }
913
914 DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT);
915 if (INVALID_SET_FILE_POINTER == dwErr)
916 {
917 FileExitOnLastError(hr, "Failed to seek position %d", cbStartPosition);
918 }
919 }
920 else
921 {
922 cbStartPosition = 0;
923 }
924
925 if (fPartialOK)
926 {
927 cbData = cbMaxRead;
928 }
929 else
930 {
931 cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD
932 if (cbMaxRead < liFileSize.QuadPart - cbStartPosition)
933 {
934 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
935 FileExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath);
936 }
937 }
938
939 if (*ppbDest)
940 {
941 if (0 == cbData)
942 {
943 ReleaseNullMem(*ppbDest);
944 *pcbDest = 0;
945 ExitFunction1(hr = S_OK);
946 }
947
948 LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE);
949 FileExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath);
950
951 pbData = static_cast<BYTE*>(pv);
952 }
953 else
954 {
955 if (0 == cbData)
956 {
957 *pcbDest = 0;
958 ExitFunction1(hr = S_OK);
959 }
960
961 pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
962 FileExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath);
963 }
964
965 DWORD cbTotalRead = 0;
966 DWORD cbRead = 0;
967 do
968 {
969 DWORD cbRemaining = 0;
970 hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining);
971 FileExitOnFailure(hr, "Underflow calculating remaining buffer size.");
972
973 if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL))
974 {
975 FileExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath);
976 }
977
978 cbTotalRead += cbRead;
979 } while (cbRead);
980
981 if (cbTotalRead != cbData)
982 {
983 hr = E_UNEXPECTED;
984 FileExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath);
985 }
986
987 *ppbDest = pbData;
988 pbData = NULL;
989 *pcbDest = cbData;
990
991LExit:
992 ReleaseMem(pbData);
993 ReleaseFile(hFile);
994
995 return hr;
996}
997
998extern "C" HRESULT DAPI FileReadHandle(
999 __in HANDLE hFile,
1000 __in_bcount(cbDest) LPBYTE pbDest,
1001 __in SIZE_T cbDest
1002 )
1003{
1004 HRESULT hr = 0;
1005 DWORD cbDataRead = 0;
1006 SIZE_T cbRemaining = cbDest;
1007 SIZE_T cbTotal = 0;
1008
1009 while (0 < cbRemaining)
1010 {
1011 if (!::ReadFile(hFile, pbDest + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataRead, NULL))
1012 {
1013 DWORD er = ::GetLastError();
1014 if (ERROR_MORE_DATA == er)
1015 {
1016 hr = S_OK;
1017 }
1018 else
1019 {
1020 hr = HRESULT_FROM_WIN32(er);
1021 }
1022 FileExitOnRootFailure(hr, "Failed to read data from file handle.");
1023 }
1024
1025 cbRemaining -= cbDataRead;
1026 cbTotal += cbDataRead;
1027 }
1028
1029LExit:
1030 return hr;
1031}
1032
1033
1034/*******************************************************************
1035 FileWrite - write a file from memory
1036
1037********************************************************************/
1038extern "C" HRESULT DAPI FileWrite(
1039 __in_z LPCWSTR pwzFileName,
1040 __in DWORD dwFlagsAndAttributes,
1041 __in_bcount_opt(cbData) LPCBYTE pbData,
1042 __in SIZE_T cbData,
1043 __out_opt HANDLE* pHandle
1044 )
1045{
1046 HRESULT hr = S_OK;
1047 HANDLE hFile = INVALID_HANDLE_VALUE;
1048
1049 // Open the file
1050 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL);
1051 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName);
1052
1053 hr = FileWriteHandle(hFile, pbData, cbData);
1054 FileExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName);
1055
1056 if (pHandle)
1057 {
1058 *pHandle = hFile;
1059 hFile = INVALID_HANDLE_VALUE;
1060 }
1061
1062LExit:
1063 ReleaseFile(hFile);
1064
1065 return hr;
1066}
1067
1068
1069/*******************************************************************
1070 FileWriteHandle - write to a file handle from memory
1071
1072********************************************************************/
1073extern "C" HRESULT DAPI FileWriteHandle(
1074 __in HANDLE hFile,
1075 __in_bcount_opt(cbData) LPCBYTE pbData,
1076 __in SIZE_T cbData
1077 )
1078{
1079 HRESULT hr = S_OK;
1080 DWORD cbDataWritten = 0;
1081 SIZE_T cbTotal = 0;
1082 SIZE_T cbRemaining = cbData;
1083
1084 // Write out all of the data.
1085 while (0 < cbRemaining)
1086 {
1087 if (!::WriteFile(hFile, pbData + cbTotal, (DWORD)min(DWORD_MAX, cbRemaining), &cbDataWritten, NULL))
1088 {
1089 FileExitOnLastError(hr, "Failed to write data to file handle.");
1090 }
1091
1092 cbRemaining -= cbDataWritten;
1093 cbTotal += cbDataWritten;
1094 }
1095
1096LExit:
1097 return hr;
1098}
1099
1100
1101/*******************************************************************
1102 FileCopyUsingHandles
1103
1104*******************************************************************/
1105extern "C" HRESULT DAPI FileCopyUsingHandles(
1106 __in HANDLE hSource,
1107 __in HANDLE hTarget,
1108 __in DWORD64 cbCopy,
1109 __out_opt DWORD64* pcbCopied
1110 )
1111{
1112 HRESULT hr = S_OK;
1113 DWORD64 cbTotalCopied = 0;
1114 BYTE rgbData[4 * 1024];
1115 DWORD cbRead = 0;
1116
1117 do
1118 {
1119 cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
1120 if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
1121 {
1122 FileExitWithLastError(hr, "Failed to read from source.");
1123 }
1124
1125 if (cbRead)
1126 {
1127 hr = FileWriteHandle(hTarget, rgbData, cbRead);
1128 FileExitOnFailure(hr, "Failed to write to target.");
1129 }
1130
1131 cbTotalCopied += cbRead;
1132 } while (cbTotalCopied < cbCopy && 0 != cbRead);
1133
1134 if (pcbCopied)
1135 {
1136 *pcbCopied = cbTotalCopied;
1137 }
1138
1139LExit:
1140 return hr;
1141}
1142
1143
1144/*******************************************************************
1145 FileCopyUsingHandlesWithProgress
1146
1147*******************************************************************/
1148extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress(
1149 __in HANDLE hSource,
1150 __in HANDLE hTarget,
1151 __in DWORD64 cbCopy,
1152 __in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
1153 __in_opt LPVOID lpData
1154 )
1155{
1156 HRESULT hr = S_OK;
1157 DWORD64 cbTotalCopied = 0;
1158 BYTE rgbData[64 * 1024];
1159 DWORD cbRead = 0;
1160
1161 LARGE_INTEGER liSourceSize = { };
1162 LARGE_INTEGER liTotalCopied = { };
1163 LARGE_INTEGER liZero = { };
1164 DWORD dwResult = 0;
1165
1166 hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart);
1167 FileExitOnFailure(hr, "Failed to get size of source.");
1168
1169 if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart)
1170 {
1171 liSourceSize.QuadPart = cbCopy;
1172 }
1173
1174 if (lpProgressRoutine)
1175 {
1176 dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData);
1177 switch (dwResult)
1178 {
1179 case PROGRESS_CONTINUE:
1180 break;
1181
1182 case PROGRESS_CANCEL:
1183 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1184
1185 case PROGRESS_STOP:
1186 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1187
1188 case PROGRESS_QUIET:
1189 lpProgressRoutine = NULL;
1190 break;
1191 }
1192 }
1193
1194 // Set size of the target file.
1195 ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN);
1196
1197 if (!::SetEndOfFile(hTarget))
1198 {
1199 FileExitWithLastError(hr, "Failed to set end of target file.");
1200 }
1201
1202 if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN))
1203 {
1204 FileExitWithLastError(hr, "Failed to reset target file pointer.");
1205 }
1206
1207 // Copy with progress.
1208 while (0 == cbCopy || cbTotalCopied < cbCopy)
1209 {
1210 cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
1211 if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
1212 {
1213 FileExitWithLastError(hr, "Failed to read from source.");
1214 }
1215
1216 if (cbRead)
1217 {
1218 hr = FileWriteHandle(hTarget, rgbData, cbRead);
1219 FileExitOnFailure(hr, "Failed to write to target.");
1220
1221 cbTotalCopied += cbRead;
1222
1223 if (lpProgressRoutine)
1224 {
1225 liTotalCopied.QuadPart = cbTotalCopied;
1226 dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData);
1227 switch (dwResult)
1228 {
1229 case PROGRESS_CONTINUE:
1230 break;
1231
1232 case PROGRESS_CANCEL:
1233 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1234
1235 case PROGRESS_STOP:
1236 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_REQUEST_ABORTED));
1237
1238 case PROGRESS_QUIET:
1239 lpProgressRoutine = NULL;
1240 break;
1241 }
1242 }
1243 }
1244 else
1245 {
1246 break;
1247 }
1248 }
1249
1250LExit:
1251 return hr;
1252}
1253
1254
1255/*******************************************************************
1256 FileEnsureCopy
1257
1258*******************************************************************/
1259extern "C" HRESULT DAPI FileEnsureCopy(
1260 __in_z LPCWSTR wzSource,
1261 __in_z LPCWSTR wzTarget,
1262 __in BOOL fOverwrite
1263 )
1264{
1265 HRESULT hr = S_OK;
1266 DWORD er;
1267
1268 // try to copy the file first
1269 if (::CopyFileW(wzSource, wzTarget, !fOverwrite))
1270 {
1271 ExitFunction(); // we're done
1272 }
1273
1274 er = ::GetLastError(); // check the error and do the right thing below
1275 if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1276 {
1277 // if not overwriting this is an expected error
1278 ExitFunction1(hr = S_FALSE);
1279 }
1280 else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist
1281 {
1282 // try to create the directory then do the copy
1283 LPWSTR pwzLastSlash = NULL;
1284 for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1285 {
1286 if (*pwz == L'\\')
1287 {
1288 pwzLastSlash = pwz;
1289 }
1290 }
1291
1292 if (pwzLastSlash)
1293 {
1294 *pwzLastSlash = L'\0'; // null terminate
1295 hr = DirEnsureExists(wzTarget, NULL);
1296 *pwzLastSlash = L'\\'; // now put the slash back
1297 FileExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget);
1298
1299 // try to copy again
1300 if (!::CopyFileW(wzSource, wzTarget, fOverwrite))
1301 {
1302 FileExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget);
1303 }
1304 }
1305 else // no path was specified so just return the error
1306 {
1307 hr = HRESULT_FROM_WIN32(er);
1308 }
1309 }
1310 else // unexpected error
1311 {
1312 hr = HRESULT_FROM_WIN32(er);
1313 }
1314
1315LExit:
1316 return hr;
1317}
1318
1319
1320/*******************************************************************
1321 FileEnsureCopyWithRetry
1322
1323*******************************************************************/
1324extern "C" HRESULT DAPI FileEnsureCopyWithRetry(
1325 __in LPCWSTR wzSource,
1326 __in LPCWSTR wzTarget,
1327 __in BOOL fOverwrite,
1328 __in DWORD cRetry,
1329 __in DWORD dwWaitMilliseconds
1330 )
1331{
1332 AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1333
1334 HRESULT hr = E_FAIL;
1335 DWORD i = 0;
1336
1337 for (i = 0; FAILED(hr) && i <= cRetry; ++i)
1338 {
1339 if (0 < i)
1340 {
1341 ::Sleep(dwWaitMilliseconds);
1342 }
1343
1344 hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite);
1345 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr
1346 || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr)
1347 {
1348 break; // no reason to retry these errors.
1349 }
1350 }
1351 FileExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1352
1353LExit:
1354 return hr;
1355}
1356
1357
1358/*******************************************************************
1359 FileEnsureMove
1360
1361*******************************************************************/
1362extern "C" HRESULT DAPI FileEnsureMove(
1363 __in_z LPCWSTR wzSource,
1364 __in_z LPCWSTR wzTarget,
1365 __in BOOL fOverwrite,
1366 __in BOOL fAllowCopy
1367 )
1368{
1369 HRESULT hr = S_OK;
1370 DWORD er;
1371
1372 DWORD dwFlags = 0;
1373
1374 if (fOverwrite)
1375 {
1376 dwFlags |= MOVEFILE_REPLACE_EXISTING;
1377 }
1378 if (fAllowCopy)
1379 {
1380 dwFlags |= MOVEFILE_COPY_ALLOWED;
1381 }
1382
1383 // try to move the file first
1384 if (::MoveFileExW(wzSource, wzTarget, dwFlags))
1385 {
1386 ExitFunction(); // we're done
1387 }
1388
1389 er = ::GetLastError(); // check the error and do the right thing below
1390 if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er))
1391 {
1392 // if not overwriting this is an expected error
1393 ExitFunction1(hr = S_FALSE);
1394 }
1395 else if (ERROR_FILE_NOT_FOUND == er)
1396 {
1397 // We are seeing some cases where ::MoveFileEx() says a file was not found
1398 // but the source file is actually present. In that case, return path not
1399 // found so we try to create the target path since that is most likely
1400 // what is missing. Otherwise, the source file is missing and we're obviously
1401 // not going to be recovering from that.
1402 if (FileExistsEx(wzSource, NULL))
1403 {
1404 er = ERROR_PATH_NOT_FOUND;
1405 }
1406 }
1407
1408 // If the path doesn't exist, try to create the directory tree then do the move.
1409 if (ERROR_PATH_NOT_FOUND == er)
1410 {
1411 LPWSTR pwzLastSlash = NULL;
1412 for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz)
1413 {
1414 if (*pwz == L'\\')
1415 {
1416 pwzLastSlash = pwz;
1417 }
1418 }
1419
1420 if (pwzLastSlash)
1421 {
1422 *pwzLastSlash = L'\0'; // null terminate
1423 hr = DirEnsureExists(wzTarget, NULL);
1424 *pwzLastSlash = L'\\'; // now put the slash back
1425 FileExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget);
1426
1427 // try to move again
1428 if (!::MoveFileExW(wzSource, wzTarget, dwFlags))
1429 {
1430 FileExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget);
1431 }
1432 }
1433 else // no path was specified so just return the error
1434 {
1435 hr = HRESULT_FROM_WIN32(er);
1436 }
1437 }
1438 else // unexpected error
1439 {
1440 hr = HRESULT_FROM_WIN32(er);
1441 }
1442
1443LExit:
1444 return hr;
1445}
1446
1447
1448/*******************************************************************
1449 FileEnsureMoveWithRetry
1450
1451*******************************************************************/
1452extern "C" HRESULT DAPI FileEnsureMoveWithRetry(
1453 __in LPCWSTR wzSource,
1454 __in LPCWSTR wzTarget,
1455 __in BOOL fOverwrite,
1456 __in BOOL fAllowCopy,
1457 __in DWORD cRetry,
1458 __in DWORD dwWaitMilliseconds
1459 )
1460{
1461 AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry.");
1462
1463 HRESULT hr = E_FAIL;
1464 DWORD i = 0;
1465
1466 for (i = 0; FAILED(hr) && i < cRetry + 1; ++i)
1467 {
1468 if (0 < i)
1469 {
1470 ::Sleep(dwWaitMilliseconds);
1471 }
1472
1473 hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy);
1474 }
1475 FileExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i);
1476
1477LExit:
1478 return hr;
1479}
1480
1481
1482/*******************************************************************
1483 FileCreateTemp - creates an empty temp file
1484
1485 NOTE: uses ANSI functions internally so it is Win9x safe
1486********************************************************************/
1487extern "C" HRESULT DAPI FileCreateTemp(
1488 __in_z LPCWSTR wzPrefix,
1489 __in_z LPCWSTR wzExtension,
1490 __deref_opt_out_z LPWSTR* ppwzTempFile,
1491 __out_opt HANDLE* phTempFile
1492 )
1493{
1494 Assert(wzPrefix && *wzPrefix);
1495 HRESULT hr = S_OK;
1496 LPSTR pszTempPath = NULL;
1497 DWORD cchTempPath = MAX_PATH;
1498
1499 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1500 LPSTR pszTempFile = NULL;
1501
1502 int i = 0;
1503
1504 hr = StrAnsiAlloc(&pszTempPath, cchTempPath);
1505 FileExitOnFailure(hr, "failed to allocate memory for the temp path");
1506 ::GetTempPathA(cchTempPath, pszTempPath);
1507
1508 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1509 {
1510 hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension);
1511 FileExitOnFailure(hr, "failed to allocate memory for log file");
1512
1513 hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1514 if (INVALID_HANDLE_VALUE == hTempFile)
1515 {
1516 // if the file already exists, just try again
1517 hr = HRESULT_FROM_WIN32(::GetLastError());
1518 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1519 {
1520 hr = S_OK;
1521 continue;
1522 }
1523 FileExitOnFailureDebugTrace(hr, "failed to create file: %hs", pszTempFile);
1524 }
1525 }
1526
1527 if (ppwzTempFile)
1528 {
1529 hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8);
1530 }
1531
1532 if (phTempFile)
1533 {
1534 *phTempFile = hTempFile;
1535 hTempFile = INVALID_HANDLE_VALUE;
1536 }
1537
1538LExit:
1539 ReleaseFile(hTempFile);
1540 ReleaseStr(pszTempFile);
1541 ReleaseStr(pszTempPath);
1542
1543 return hr;
1544}
1545
1546
1547/*******************************************************************
1548 FileCreateTempW - creates an empty temp file
1549
1550*******************************************************************/
1551extern "C" HRESULT DAPI FileCreateTempW(
1552 __in_z LPCWSTR wzPrefix,
1553 __in_z LPCWSTR wzExtension,
1554 __deref_opt_out_z LPWSTR* ppwzTempFile,
1555 __out_opt HANDLE* phTempFile
1556 )
1557{
1558 Assert(wzPrefix && *wzPrefix);
1559 HRESULT hr = E_FAIL;
1560
1561 WCHAR wzTempPath[MAX_PATH];
1562 DWORD cchTempPath = countof(wzTempPath);
1563 LPWSTR pwzTempFile = NULL;
1564
1565 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1566 int i = 0;
1567
1568 if (!::GetTempPathW(cchTempPath, wzTempPath))
1569 {
1570 FileExitOnLastError(hr, "failed to get temp path");
1571 }
1572
1573 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1574 {
1575 hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension);
1576 FileExitOnFailure(hr, "failed to allocate memory for temp filename");
1577
1578 hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1579 if (INVALID_HANDLE_VALUE == hTempFile)
1580 {
1581 // if the file already exists, just try again
1582 hr = HRESULT_FROM_WIN32(::GetLastError());
1583 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
1584 {
1585 hr = S_OK;
1586 continue;
1587 }
1588 FileExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile);
1589 }
1590 }
1591
1592 if (phTempFile)
1593 {
1594 *phTempFile = hTempFile;
1595 hTempFile = INVALID_HANDLE_VALUE;
1596 }
1597
1598 if (ppwzTempFile)
1599 {
1600 *ppwzTempFile = pwzTempFile;
1601 pwzTempFile = NULL;
1602 }
1603
1604LExit:
1605 ReleaseFile(hTempFile);
1606 ReleaseStr(pwzTempFile);
1607
1608 return hr;
1609}
1610
1611
1612/*******************************************************************
1613 FileIsSame
1614
1615********************************************************************/
1616extern "C" HRESULT DAPI FileIsSame(
1617 __in_z LPCWSTR wzFile1,
1618 __in_z LPCWSTR wzFile2,
1619 __out LPBOOL lpfSameFile
1620 )
1621{
1622 HRESULT hr = S_OK;
1623 HANDLE hFile1 = NULL;
1624 HANDLE hFile2 = NULL;
1625 BY_HANDLE_FILE_INFORMATION fileInfo1 = { };
1626 BY_HANDLE_FILE_INFORMATION fileInfo2 = { };
1627
1628 hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1629 FileExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1);
1630
1631 hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1632 FileExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2);
1633
1634 if (!::GetFileInformationByHandle(hFile1, &fileInfo1))
1635 {
1636 FileExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1);
1637 }
1638
1639 if (!::GetFileInformationByHandle(hFile2, &fileInfo2))
1640 {
1641 FileExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2);
1642 }
1643
1644 *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
1645 fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
1646 fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE;
1647
1648LExit:
1649 ReleaseFile(hFile1);
1650 ReleaseFile(hFile2);
1651
1652 return hr;
1653}
1654
1655/*******************************************************************
1656 FileEnsureDelete - deletes a file, first removing read-only,
1657 hidden, or system attributes if necessary.
1658********************************************************************/
1659extern "C" HRESULT DAPI FileEnsureDelete(
1660 __in_z LPCWSTR wzFile
1661 )
1662{
1663 HRESULT hr = S_OK;
1664
1665 DWORD dwAttrib = INVALID_FILE_ATTRIBUTES;
1666 if (FileExistsEx(wzFile, &dwAttrib))
1667 {
1668 if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM)
1669 {
1670 if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL))
1671 {
1672 FileExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile);
1673 }
1674 }
1675
1676 if (!::DeleteFileW(wzFile))
1677 {
1678 FileExitOnLastError(hr, "Failed to delete file: %ls", wzFile);
1679 }
1680 }
1681
1682LExit:
1683 return hr;
1684}
1685
1686/*******************************************************************
1687 FileGetTime - Gets the file time of a specified file
1688********************************************************************/
1689extern "C" HRESULT DAPI FileGetTime(
1690 __in_z LPCWSTR wzFile,
1691 __out_opt LPFILETIME lpCreationTime,
1692 __out_opt LPFILETIME lpLastAccessTime,
1693 __out_opt LPFILETIME lpLastWriteTime
1694 )
1695{
1696 HRESULT hr = S_OK;
1697 HANDLE hFile = NULL;
1698
1699 hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1700 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1701
1702 if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1703 {
1704 FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1705 }
1706
1707LExit:
1708 ReleaseFile(hFile);
1709 return hr;
1710}
1711
1712/*******************************************************************
1713 FileSetTime - Sets the file time of a specified file
1714********************************************************************/
1715extern "C" HRESULT DAPI FileSetTime(
1716 __in_z LPCWSTR wzFile,
1717 __in_opt const FILETIME *lpCreationTime,
1718 __in_opt const FILETIME *lpLastAccessTime,
1719 __in_opt const FILETIME *lpLastWriteTime
1720 )
1721{
1722 HRESULT hr = S_OK;
1723 HANDLE hFile = NULL;
1724
1725 hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1726 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1727
1728 if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime))
1729 {
1730 FileExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile);
1731 }
1732
1733LExit:
1734 ReleaseFile(hFile);
1735 return hr;
1736}
1737
1738/*******************************************************************
1739 FileReSetTime - ReSets a file's last acess and modified time to the
1740 creation time of the file
1741********************************************************************/
1742extern "C" HRESULT DAPI FileResetTime(
1743 __in_z LPCWSTR wzFile
1744 )
1745{
1746 HRESULT hr = S_OK;
1747 HANDLE hFile = NULL;
1748 FILETIME ftCreateTime;
1749
1750 hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1751 FileExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile);
1752
1753 if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL))
1754 {
1755 FileExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile);
1756 }
1757
1758 if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime))
1759 {
1760 FileExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile);
1761 }
1762
1763LExit:
1764 ReleaseFile(hFile);
1765 return hr;
1766}
1767
1768
1769/*******************************************************************
1770 FileExecutableArchitecture
1771
1772*******************************************************************/
1773extern "C" HRESULT DAPI FileExecutableArchitecture(
1774 __in_z LPCWSTR wzFile,
1775 __out FILE_ARCHITECTURE *pArchitecture
1776 )
1777{
1778 HRESULT hr = S_OK;
1779
1780 HANDLE hFile = INVALID_HANDLE_VALUE;
1781 DWORD cbRead = 0;
1782 IMAGE_DOS_HEADER DosImageHeader = { };
1783 IMAGE_NT_HEADERS NtImageHeader = { };
1784
1785 hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1786 if (hFile == INVALID_HANDLE_VALUE)
1787 {
1788 FileExitWithLastError(hr, "Failed to open file: %ls", wzFile);
1789 }
1790
1791 if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL))
1792 {
1793 FileExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile);
1794 }
1795
1796 if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE)
1797 {
1798 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1799 FileExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile);
1800 }
1801
1802 if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN))
1803 {
1804 FileExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile);
1805 }
1806
1807 if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL))
1808 {
1809 FileExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile);
1810 }
1811
1812 if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE)
1813 {
1814 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1815 FileExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile);
1816 }
1817
1818 if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem ||
1819 IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem ||
1820 IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem)
1821 {
1822 switch (NtImageHeader.FileHeader.Machine)
1823 {
1824 case IMAGE_FILE_MACHINE_I386:
1825 *pArchitecture = FILE_ARCHITECTURE_X86;
1826 break;
1827 case IMAGE_FILE_MACHINE_IA64:
1828 *pArchitecture = FILE_ARCHITECTURE_IA64;
1829 break;
1830 case IMAGE_FILE_MACHINE_AMD64:
1831 *pArchitecture = FILE_ARCHITECTURE_X64;
1832 break;
1833 default:
1834 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1835 break;
1836 }
1837 }
1838 else
1839 {
1840 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
1841 }
1842 FileExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile);
1843
1844LExit:
1845 if (hFile != INVALID_HANDLE_VALUE)
1846 {
1847 ::CloseHandle(hFile);
1848 }
1849
1850 return hr;
1851}
1852
1853/*******************************************************************
1854 FileToString
1855
1856*******************************************************************/
1857extern "C" HRESULT DAPI FileToString(
1858 __in_z LPCWSTR wzFile,
1859 __out LPWSTR *psczString,
1860 __out_opt FILE_ENCODING *pfeEncoding
1861 )
1862{
1863 HRESULT hr = S_OK;
1864 BYTE *pbFullFileBuffer = NULL;
1865 SIZE_T cbFullFileBuffer = 0;
1866 BOOL fNullCharFound = FALSE;
1867 LPWSTR sczFileText = NULL;
1868
1869 // Check if the file is ANSI
1870 hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile);
1871 FileExitOnFailure(hr, "Failed to read file: %ls", wzFile);
1872
1873 if (0 == cbFullFileBuffer)
1874 {
1875 *psczString = NULL;
1876 ExitFunction1(hr = S_OK);
1877 }
1878
1879 // UTF-8 BOM
1880 if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM)))
1881 {
1882 if (pfeEncoding)
1883 {
1884 *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM;
1885 }
1886
1887 hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8);
1888 FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile);
1889
1890 *psczString = sczFileText;
1891 sczFileText = NULL;
1892 }
1893 // UTF-16 BOM, little endian (windows regular UTF-16)
1894 else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM)))
1895 {
1896 if (pfeEncoding)
1897 {
1898 *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM;
1899 }
1900
1901 hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR));
1902 FileExitOnFailure(hr, "Failed to allocate copy of string");
1903 }
1904 // No BOM, let's try to detect
1905 else
1906 {
1907 for (DWORD i = 0; i < cbFullFileBuffer; ++i)
1908 {
1909 if (pbFullFileBuffer[i] == '\0')
1910 {
1911 fNullCharFound = TRUE;
1912 break;
1913 }
1914 }
1915
1916 if (!fNullCharFound)
1917 {
1918 if (pfeEncoding)
1919 {
1920 *pfeEncoding = FILE_ENCODING_UTF8;
1921 }
1922
1923 hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8);
1924 if (FAILED(hr))
1925 {
1926 if (E_OUTOFMEMORY == hr)
1927 {
1928 FileExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile);
1929 }
1930 }
1931 else
1932 {
1933 *psczString = sczFileText;
1934 sczFileText = NULL;
1935 }
1936 }
1937 else if (NULL == *psczString)
1938 {
1939 if (pfeEncoding)
1940 {
1941 *pfeEncoding = FILE_ENCODING_UTF16;
1942 }
1943
1944 hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR));
1945 FileExitOnFailure(hr, "Failed to allocate copy of string");
1946 }
1947 }
1948
1949LExit:
1950 ReleaseStr(sczFileText);
1951 ReleaseMem(pbFullFileBuffer);
1952
1953 return hr;
1954}
1955
1956/*******************************************************************
1957 FileFromString
1958
1959*******************************************************************/
1960extern "C" HRESULT DAPI FileFromString(
1961 __in_z LPCWSTR wzFile,
1962 __in DWORD dwFlagsAndAttributes,
1963 __in_z LPCWSTR sczString,
1964 __in FILE_ENCODING feEncoding
1965 )
1966{
1967 HRESULT hr = S_OK;
1968 LPSTR sczUtf8String = NULL;
1969 BYTE *pbFullFileBuffer = NULL;
1970 const BYTE *pcbFullFileBuffer = NULL;
1971 SIZE_T cbFullFileBuffer = 0;
1972 SIZE_T cbStrLen = 0;
1973
1974 switch (feEncoding)
1975 {
1976 case FILE_ENCODING_UTF8:
1977 hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1978 FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1979
1980 hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbFullFileBuffer));
1981 FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string");
1982
1983 pcbFullFileBuffer = reinterpret_cast<BYTE *>(sczUtf8String);
1984 break;
1985 case FILE_ENCODING_UTF8_WITH_BOM:
1986 hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8);
1987 FileExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file");
1988
1989 hr = ::StringCchLengthA(sczUtf8String, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
1990 FileExitOnRootFailure(hr, "Failed to get length of UTF-8 string");
1991
1992 cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen;
1993
1994 pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
1995 FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
1996
1997 memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM));
1998 memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen);
1999 pcbFullFileBuffer = pbFullFileBuffer;
2000 break;
2001 case FILE_ENCODING_UTF16:
2002 hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
2003 FileExitOnRootFailure(hr, "Failed to get length of string");
2004
2005 cbFullFileBuffer = cbStrLen * sizeof(WCHAR);
2006 pcbFullFileBuffer = reinterpret_cast<const BYTE *>(sczString);
2007 break;
2008 case FILE_ENCODING_UTF16_WITH_BOM:
2009 hr = ::StringCchLengthW(sczString, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cbStrLen));
2010 FileExitOnRootFailure(hr, "Failed to get length of string");
2011
2012 cbStrLen *= sizeof(WCHAR);
2013 cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen;
2014
2015 pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE));
2016 FileExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer");
2017
2018 memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM));
2019 memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen);
2020 pcbFullFileBuffer = pbFullFileBuffer;
2021 break;
2022 }
2023
2024 hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL);
2025 FileExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile);
2026
2027LExit:
2028 ReleaseStr(sczUtf8String);
2029 ReleaseMem(pbFullFileBuffer);
2030
2031 return hr;
2032}