* Sync to trunk HEAD (r53298).
[reactos.git] / dll / win32 / kernel32 / client / file / move.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/file.c
6 * PURPOSE: Directory functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * Gerhard W. Gruber (sparhawk_at_gmx.at)
9 * Dmitry Philippov (shedon@mail.ru)
10 * UPDATE HISTORY:
11 * Created 01/11/98
12 * DP (29/07/2006)
13 * Fix some bugs in the add_boot_rename_entry function
14 */
15
16 /* INCLUDES *****************************************************************/
17
18 #include <k32.h>
19 #include <malloc.h>
20 #define NDEBUG
21 #include <debug.h>
22 DEBUG_CHANNEL(kernel32file);
23
24 /* GLOBALS *****************************************************************/
25
26 /* FUNCTIONS ****************************************************************/
27 static BOOL
28 RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName)
29 {
30 DWORD Attributes;
31 Attributes = GetFileAttributesW(lpFileName);
32 if (Attributes != INVALID_FILE_ATTRIBUTES)
33 {
34 return SetFileAttributesW(lpFileName,Attributes -
35 (Attributes & ~FILE_ATTRIBUTE_READONLY));
36 }
37
38 return FALSE;
39 }
40
41
42 /***********************************************************************
43 * add_boot_rename_entry
44 *
45 * Adds an entry to the registry that is loaded when windows boots and
46 * checks if there are some files to be removed or renamed/moved.
47 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
48 * non-NULL then the file is moved, otherwise it is deleted. The
49 * entry of the registrykey is always appended with two zero
50 * terminated strings. If <fn2> is NULL then the second entry is
51 * simply a single 0-byte. Otherwise the second filename goes
52 * there. The entries are prepended with \??\ before the path and the
53 * second filename gets also a '!' as the first character if
54 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
55 * 0-byte follows to indicate the end of the strings.
56 * i.e.:
57 * \??\D:\test\file1[0]
58 * !\??\D:\test\file1_renamed[0]
59 * \??\D:\Test|delete[0]
60 * [0] <- file is to be deleted, second string empty
61 * \??\D:\test\file2[0]
62 * !\??\D:\test\file2_renamed[0]
63 * [0] <- indicates end of strings
64 *
65 * or:
66 * \??\D:\test\file1[0]
67 * !\??\D:\test\file1_renamed[0]
68 * \??\D:\Test|delete[0]
69 * [0] <- file is to be deleted, second string empty
70 * [0] <- indicates end of strings
71 *
72 */
73 static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
74 {
75 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
76 'F','i','l','e','R','e','n','a','m','e',
77 'O','p','e','r','a','t','i','o','n','s',0};
78
79 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
80
81 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
82
83 OBJECT_ATTRIBUTES ObjectAttributes;
84 UNICODE_STRING nameW, source_name, dest_name;
85 KEY_VALUE_PARTIAL_INFORMATION *info;
86 BOOL rc = FALSE;
87 HANDLE Reboot = NULL;
88 DWORD len1, len2;
89 DWORD DestLen = 0;
90 DWORD DataSize = 0;
91 BYTE *Buffer = NULL;
92 WCHAR *p;
93 NTSTATUS Status;
94
95 TRACE("add_boot_rename_entry( %S, %S, %d ) \n", source, dest, flags);
96
97 if(dest)
98 DestLen = wcslen(dest);
99
100 if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL ))
101 {
102 SetLastError( ERROR_PATH_NOT_FOUND );
103 return FALSE;
104 }
105 dest_name.Buffer = NULL;
106 if (DestLen && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL ))
107 {
108 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
109 SetLastError( ERROR_PATH_NOT_FOUND );
110 return FALSE;
111 }
112
113 InitializeObjectAttributes(&ObjectAttributes,
114 &KeyName,
115 OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
116 NULL,
117 NULL);
118
119 Status = NtCreateKey(&Reboot,
120 KEY_QUERY_VALUE | KEY_SET_VALUE,
121 &ObjectAttributes,
122 0,
123 NULL,
124 REG_OPTION_NON_VOLATILE,
125 NULL);
126
127 if (Status == STATUS_ACCESS_DENIED)
128 {
129 Status = NtCreateKey(
130 &Reboot,
131 KEY_QUERY_VALUE | KEY_SET_VALUE,
132 &ObjectAttributes,
133 0,
134 NULL,
135 REG_OPTION_BACKUP_RESTORE,
136 NULL);
137 }
138
139 if (!NT_SUCCESS(Status))
140 {
141 WARN("NtCreateKey() failed (Status 0x%lx)\n", Status);
142 if (source_name.Buffer)
143 RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
144 if (dest_name.Buffer)
145 RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
146 return FALSE;
147 }
148
149 len1 = source_name.Length + sizeof(WCHAR);
150 if (DestLen)
151 {
152 len2 = dest_name.Length + sizeof(WCHAR);
153 if (flags & MOVEFILE_REPLACE_EXISTING)
154 len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
155 }
156 else
157 {
158 len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
159 }
160
161 RtlInitUnicodeString( &nameW, ValueName );
162
163 /* First we check if the key exists and if so how many bytes it already contains. */
164 Status = NtQueryValueKey(
165 Reboot,
166 &nameW,
167 KeyValuePartialInformation,
168 NULL,
169 0,
170 &DataSize );
171 if ((Status == STATUS_BUFFER_OVERFLOW) ||
172 (Status == STATUS_BUFFER_TOO_SMALL))
173 {
174 if (!(Buffer = HeapAlloc(GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR))))
175 goto Quit;
176 Status = NtQueryValueKey(Reboot, &nameW, KeyValuePartialInformation,
177 Buffer, DataSize, &DataSize);
178 if(!NT_SUCCESS(Status))
179 goto Quit;
180 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
181 if (info->Type != REG_MULTI_SZ) goto Quit;
182 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
183 }
184 else
185 {
186 DataSize = info_size;
187 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
188 goto Quit;
189 }
190
191 memcpy( Buffer + DataSize, source_name.Buffer, len1 );
192 DataSize += len1;
193 p = (WCHAR *)(Buffer + DataSize);
194 if (DestLen)
195 {
196 if (flags & MOVEFILE_REPLACE_EXISTING)
197 *p++ = '!';
198 memcpy( p, dest_name.Buffer, len2 );
199 DataSize += len2;
200 }
201 else
202 {
203 *p = 0;
204 DataSize += sizeof(WCHAR);
205 }
206
207 /* add final null */
208 p = (WCHAR *)(Buffer + DataSize);
209 *p = 0;
210 DataSize += sizeof(WCHAR);
211
212 rc = NT_SUCCESS(NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size));
213
214 Quit:
215 RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
216 if (dest_name.Buffer)
217 RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
218 NtClose(Reboot);
219 if(Buffer)
220 HeapFree(GetProcessHeap(), 0, Buffer);
221 return(rc);
222 }
223
224
225 /*
226 * @implemented
227 */
228 BOOL
229 WINAPI
230 MoveFileWithProgressW (
231 LPCWSTR lpExistingFileName,
232 LPCWSTR lpNewFileName,
233 LPPROGRESS_ROUTINE lpProgressRoutine,
234 LPVOID lpData,
235 DWORD dwFlags
236 )
237 {
238 HANDLE hFile = NULL, hNewFile = NULL;
239 IO_STATUS_BLOCK IoStatusBlock;
240 OBJECT_ATTRIBUTES ObjectAttributes;
241 PFILE_RENAME_INFORMATION FileRename;
242 NTSTATUS errCode;
243 BOOL Result;
244 UNICODE_STRING DstPathU;
245 BOOL folder = FALSE;
246
247 TRACE("MoveFileWithProgressW()\n");
248
249 if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)
250 return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags );
251
252 // if (dwFlags & MOVEFILE_WRITE_THROUGH)
253 // FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n");
254
255 if (!lpNewFileName)
256 return DeleteFileW(lpExistingFileName);
257
258 /* validate & translate the filename */
259 if (!RtlDosPathNameToNtPathName_U (lpNewFileName,
260 &DstPathU,
261 NULL,
262 NULL))
263 {
264 WARN("Invalid destination path\n");
265 SetLastError(ERROR_PATH_NOT_FOUND);
266 return FALSE;
267 }
268
269 InitializeObjectAttributes(&ObjectAttributes,
270 &DstPathU,
271 OBJ_CASE_INSENSITIVE,
272 NULL,
273 NULL);
274
275 errCode = NtOpenFile( &hNewFile,
276 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
277 &ObjectAttributes,
278 &IoStatusBlock,
279 0,
280 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
281 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0) );
282
283 if (NT_SUCCESS(errCode)) /* Destination exists */
284 {
285 NtClose(hNewFile);
286
287 if (!(dwFlags & MOVEFILE_REPLACE_EXISTING))
288 {
289 SetLastError(ERROR_ALREADY_EXISTS);
290 return FALSE;
291 }
292 else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY)
293 {
294 SetLastError(ERROR_ACCESS_DENIED);
295 return FALSE;
296 }
297 }
298
299 hFile = CreateFileW (lpExistingFileName,
300 GENERIC_ALL,
301 FILE_SHARE_WRITE|FILE_SHARE_READ,
302 NULL,
303 OPEN_EXISTING,
304 FILE_FLAG_BACKUP_SEMANTICS |
305 ((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_FLAG_WRITE_THROUGH : 0),
306 NULL);
307
308 if (hFile == INVALID_HANDLE_VALUE)
309 {
310 return FALSE;
311 }
312
313 FileRename = RtlAllocateHeap(
314 RtlGetProcessHeap(),
315 HEAP_ZERO_MEMORY,
316 sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
317 if( !FileRename ) {
318 CloseHandle(hFile);
319 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
320 return FALSE;
321 }
322 if( dwFlags & MOVEFILE_REPLACE_EXISTING ) {
323 FileRename->ReplaceIfExists = TRUE;
324 }
325 else {
326 FileRename->ReplaceIfExists = FALSE;
327 }
328
329
330 memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
331 RtlFreeHeap (RtlGetProcessHeap (),
332 0,
333 DstPathU.Buffer);
334
335 FileRename->FileNameLength = DstPathU.Length;
336 errCode = NtSetInformationFile (hFile,
337 &IoStatusBlock,
338 FileRename,
339 sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
340 FileRenameInformation);
341 CloseHandle(hFile);
342 RtlFreeHeap(RtlGetProcessHeap(), 0, FileRename);
343
344 if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY)
345 {
346 folder = TRUE;
347 }
348
349
350 /*
351 * FIXME:
352 * Fail now move the folder
353 * Before we fail at CreateFileW
354 */
355
356
357 if (NT_SUCCESS(errCode))
358 {
359 Result = TRUE;
360 }
361 else
362 {
363 if (folder==FALSE)
364 {
365 Result = CopyFileExW (lpExistingFileName,
366 lpNewFileName,
367 lpProgressRoutine,
368 lpData,
369 NULL,
370 (dwFlags & MOVEFILE_REPLACE_EXISTING) ? 0 : COPY_FILE_FAIL_IF_EXISTS);
371 if (Result)
372 {
373 /* Cleanup the source file */
374 Result = DeleteFileW (lpExistingFileName);
375 }
376 }
377 else
378 {
379 /* move folder code start */
380 WIN32_FIND_DATAW findBuffer;
381 LPWSTR lpExistingFileName2 = NULL;
382 LPWSTR lpNewFileName2 = NULL;
383 LPWSTR lpDeleteFile = NULL;
384 INT size;
385 INT size2;
386 BOOL loop = TRUE;
387 BOOL Result = FALSE;
388 INT max_size = MAX_PATH;
389
390
391 /* Build the string */
392 size = wcslen(lpExistingFileName);
393 if (size+6> max_size)
394 max_size = size + 6;
395
396 lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
397 if (lpDeleteFile == NULL)
398 return FALSE;
399
400 lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
401 if (lpNewFileName2 == NULL)
402 {
403 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
404 return FALSE;
405 }
406
407 lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
408 if (lpExistingFileName2 == NULL)
409 {
410 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
411 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
412 return FALSE;
413 }
414
415 wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName);
416 wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0");
417
418 /* Get the file name */
419 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
420 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
421 if (hFile == INVALID_HANDLE_VALUE)
422 loop=FALSE;
423
424 if (findBuffer.cFileName[0] == L'\0')
425 loop=FALSE;
426
427
428 /* FIXME
429 * remove readonly flag from source folder and do not set the readonly flag to dest folder
430 */
431 RemoveReadOnlyAttributeW(lpExistingFileName);
432 RemoveReadOnlyAttributeW(lpNewFileName);
433 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
434 CreateDirectoryW(lpNewFileName, NULL);
435
436 /* search the files/folders and move them */
437 while (loop==TRUE)
438 {
439 Result = TRUE;
440
441 if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L".")))
442 {
443 loop = FindNextFileW(hFile, &findBuffer);
444
445 if (!loop)
446 {
447 size = wcslen(lpExistingFileName2)-4;
448 FindClose(hFile);
449 hFile = INVALID_HANDLE_VALUE;
450
451 wcscpy( &lpExistingFileName2[size],L"\0");
452
453 if (wcsncmp(lpExistingFileName,lpExistingFileName2,size))
454 {
455 DWORD Attributes;
456
457 /* delete folder */
458 TRACE("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile);
459
460 /* remove system folder flag other wise we can not delete the folder */
461 Attributes = GetFileAttributesW(lpExistingFileName2);
462 if (Attributes != INVALID_FILE_ATTRIBUTES)
463 {
464 SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
465 }
466
467 RemoveReadOnlyAttributeW(lpExistingFileName2);
468
469 Result = RemoveDirectoryW(lpExistingFileName2);
470 if (Result == FALSE)
471 break;
472
473 loop=TRUE;
474 size = wcslen(lpExistingFileName);
475
476 if (size+6>max_size)
477 {
478 if (lpNewFileName2 != NULL)
479 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
480
481 if (lpExistingFileName2 != NULL)
482 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
483
484 if (lpDeleteFile != NULL)
485 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
486
487 return FALSE;
488 }
489
490 wcscpy( lpExistingFileName2,lpExistingFileName);
491 wcscpy( &lpExistingFileName2[size],L"\\*.*\0");
492
493 /* Get the file name */
494 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
495 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
496 }
497 }
498 continue;
499 }
500
501 if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
502 {
503
504 /* Build the new src string */
505 size = wcslen(findBuffer.cFileName);
506 size2= wcslen(lpExistingFileName2);
507
508 if (size2+size+6>max_size)
509 {
510 FindClose(hFile);
511
512 if (lpNewFileName2 != NULL)
513 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
514
515 if (lpExistingFileName2 != NULL)
516 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
517
518 if (lpDeleteFile != NULL)
519 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
520
521 return FALSE;
522 }
523
524 wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName);
525 wcscpy( &lpExistingFileName2[size2+size-3],L"\0");
526
527
528 /* Continue */
529 wcscpy( lpDeleteFile,lpExistingFileName2);
530 wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0");
531
532
533 /* Build the new dst string */
534 size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName);
535 size2 = wcslen(lpExistingFileName);
536
537 if (size>max_size)
538 {
539 FindClose(hFile);
540
541 if (lpNewFileName2 != NULL)
542 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
543
544 if (lpExistingFileName2 != NULL)
545 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
546
547 if (lpDeleteFile != NULL)
548 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
549
550 return FALSE;
551 }
552
553 wcscpy( lpNewFileName2,lpNewFileName);
554 size = wcslen(lpNewFileName);
555 wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]);
556 size = wcslen(lpNewFileName2);
557 wcscpy( &lpNewFileName2[size-4],L"\0");
558
559 /* Create Folder */
560
561 /* FIXME
562 * remove readonly flag from source folder and do not set the readonly flag to dest folder
563 */
564 RemoveReadOnlyAttributeW(lpDeleteFile);
565 RemoveReadOnlyAttributeW(lpNewFileName2);
566
567 CreateDirectoryW(lpNewFileName2,NULL);
568 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
569
570
571 /* set new search path from src string */
572 FindClose(hFile);
573 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
574 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
575 }
576 else
577 {
578
579 /* Build the new string */
580 size = wcslen(findBuffer.cFileName);
581 size2= wcslen(lpExistingFileName2);
582 wcscpy( lpDeleteFile,lpExistingFileName2);
583 wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName);
584
585 /* Build dest string */
586 size = wcslen(lpDeleteFile) + wcslen(lpNewFileName);
587 size2 = wcslen(lpExistingFileName);
588
589 if (size>max_size)
590 {
591 FindClose(hFile);
592
593 if (lpNewFileName2 != NULL)
594 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
595
596 if (lpExistingFileName2 != NULL)
597 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
598
599 if (lpDeleteFile != NULL)
600 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
601
602 return FALSE;
603 }
604
605 wcscpy( lpNewFileName2,lpNewFileName);
606 size = wcslen(lpNewFileName);
607 wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]);
608
609
610 /* overrite existsen file, if the file got the flag have readonly
611 * we need reomve that flag
612 */
613
614 /* copy file */
615
616 TRACE("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2);
617 RemoveReadOnlyAttributeW(lpDeleteFile);
618 RemoveReadOnlyAttributeW(lpNewFileName2);
619
620 Result = CopyFileExW (lpDeleteFile,
621 lpNewFileName2,
622 lpProgressRoutine,
623 lpData,
624 NULL,
625 0);
626
627 if (Result == FALSE)
628 break;
629
630 /* delete file */
631 TRACE("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2);
632 Result = RemoveReadOnlyAttributeW(lpDeleteFile);
633 if (Result == FALSE)
634 break;
635
636 TRACE("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile);
637 Result = DeleteFileW(lpDeleteFile);
638 if (Result == FALSE)
639 break;
640
641 }
642 loop = FindNextFileW(hFile, &findBuffer);
643 }
644
645
646 /* Remove last folder */
647 if ((loop == FALSE) && (Result != FALSE))
648 {
649 DWORD Attributes;
650
651 Attributes = GetFileAttributesW(lpDeleteFile);
652 if (Attributes != INVALID_FILE_ATTRIBUTES)
653 {
654 SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
655 }
656
657 Result = RemoveDirectoryW(lpExistingFileName);
658 }
659
660 /* Cleanup */
661 FindClose(hFile);
662
663 if (lpNewFileName2 != NULL)
664 {
665 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
666 lpNewFileName2 = NULL;
667 }
668
669 if (lpExistingFileName2 != NULL)
670 {
671 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
672 lpExistingFileName2 = NULL;
673 }
674
675 if (lpDeleteFile != NULL)
676 {
677 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
678 lpDeleteFile = NULL;
679 }
680
681 return Result;
682
683 // end move folder code
684 }
685 }
686
687
688 return Result;
689 }
690
691
692 /*
693 * @implemented
694 */
695 BOOL
696 WINAPI
697 MoveFileWithProgressA (
698 LPCSTR lpExistingFileName,
699 LPCSTR lpNewFileName,
700 LPPROGRESS_ROUTINE lpProgressRoutine,
701 LPVOID lpData,
702 DWORD dwFlags
703 )
704 {
705 PWCHAR ExistingFileNameW;
706 PWCHAR NewFileNameW;
707 BOOL ret;
708
709 if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
710 return FALSE;
711
712 if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
713 return FALSE;
714
715 ret = MoveFileWithProgressW (ExistingFileNameW ,
716 NewFileNameW,
717 lpProgressRoutine,
718 lpData,
719 dwFlags);
720
721 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
722
723 return ret;
724 }
725
726
727 /*
728 * @implemented
729 */
730 BOOL
731 WINAPI
732 MoveFileW (
733 LPCWSTR lpExistingFileName,
734 LPCWSTR lpNewFileName
735 )
736 {
737 return MoveFileExW (lpExistingFileName,
738 lpNewFileName,
739 MOVEFILE_COPY_ALLOWED);
740 }
741
742
743 /*
744 * @implemented
745 */
746 BOOL
747 WINAPI
748 MoveFileExW (
749 LPCWSTR lpExistingFileName,
750 LPCWSTR lpNewFileName,
751 DWORD dwFlags
752 )
753 {
754 return MoveFileWithProgressW (lpExistingFileName,
755 lpNewFileName,
756 NULL,
757 NULL,
758 dwFlags);
759 }
760
761
762 /*
763 * @implemented
764 */
765 BOOL
766 WINAPI
767 MoveFileA (
768 LPCSTR lpExistingFileName,
769 LPCSTR lpNewFileName
770 )
771 {
772 return MoveFileExA (lpExistingFileName,
773 lpNewFileName,
774 MOVEFILE_COPY_ALLOWED);
775 }
776
777
778 /*
779 * @implemented
780 */
781 BOOL
782 WINAPI
783 MoveFileExA (
784 LPCSTR lpExistingFileName,
785 LPCSTR lpNewFileName,
786 DWORD dwFlags
787 )
788 {
789 return MoveFileWithProgressA (lpExistingFileName,
790 lpNewFileName,
791 NULL,
792 NULL,
793 dwFlags);
794 }
795
796 /* EOF */