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