reshuffling of dlls
[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 = NULL;
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 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
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 = NtCreateKey(&Reboot,
113 KEY_QUERY_VALUE | KEY_SET_VALUE,
114 &ObjectAttributes,
115 0,
116 NULL,
117 REG_OPTION_NON_VOLATILE,
118 NULL);
119
120 if (!NT_SUCCESS(Status))
121 {
122 DPRINT1("NtCreateKey() failed (Status 0x%lx)\n", Status);
123 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
124 RtlFreeHeap( RtlGetProcessHeap(), 0, dest_name.Buffer );
125 return FALSE;
126 }
127
128 len1 = source_name.Length + sizeof(WCHAR);
129 if (dest)
130 {
131 len2 = dest_name.Length + sizeof(WCHAR);
132 if (flags & MOVEFILE_REPLACE_EXISTING)
133 len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
134 }
135 else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
136
137 RtlInitUnicodeString( &nameW, ValueName );
138
139 /* First we check if the key exists and if so how many bytes it already contains. */
140 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
141 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
142 {
143 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
144 goto Quit;
145 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
146 Buffer, DataSize, &DataSize )) goto Quit;
147 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
148 if (info->Type != REG_MULTI_SZ) goto Quit;
149 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
150 }
151 else
152 {
153 DataSize = info_size;
154 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
155 goto Quit;
156 }
157
158 memcpy( Buffer + DataSize, source_name.Buffer, len1 );
159 DataSize += len1;
160 p = (WCHAR *)(Buffer + DataSize);
161 if (dest)
162 {
163 if (flags & MOVEFILE_REPLACE_EXISTING)
164 *p++ = '!';
165 memcpy( p, dest_name.Buffer, len2 );
166 DataSize += len2;
167 }
168 else
169 {
170 *p = 0;
171 DataSize += sizeof(WCHAR);
172 }
173
174 /* add final null */
175 p = (WCHAR *)(Buffer + DataSize);
176 *p = 0;
177 DataSize += sizeof(WCHAR);
178
179 rc = NT_SUCCESS(NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size));
180
181 Quit:
182 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
183 RtlFreeHeap( RtlGetProcessHeap(), 0, dest_name.Buffer );
184 if (Reboot) NtClose(Reboot);
185 HeapFree( GetProcessHeap(), 0, Buffer );
186 return(rc);
187 }
188
189
190 /*
191 * @implemented
192 */
193 BOOL
194 STDCALL
195 MoveFileWithProgressW (
196 LPCWSTR lpExistingFileName,
197 LPCWSTR lpNewFileName,
198 LPPROGRESS_ROUTINE lpProgressRoutine,
199 LPVOID lpData,
200 DWORD dwFlags
201 )
202 {
203 HANDLE hFile = NULL;
204 IO_STATUS_BLOCK IoStatusBlock;
205 PFILE_RENAME_INFORMATION FileRename;
206 NTSTATUS errCode;
207 BOOL Result;
208 UNICODE_STRING DstPathU;
209 BOOL folder = FALSE;
210
211 DPRINT("MoveFileWithProgressW()\n");
212
213 if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)
214 return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags );
215
216 hFile = CreateFileW (lpExistingFileName,
217 GENERIC_ALL,
218 FILE_SHARE_WRITE|FILE_SHARE_READ,
219 NULL,
220 OPEN_EXISTING,
221 FILE_FLAG_BACKUP_SEMANTICS,
222 NULL);
223
224 if (hFile == INVALID_HANDLE_VALUE)
225 {
226 return FALSE;
227 }
228
229
230 /* validate & translate the filename */
231 if (!RtlDosPathNameToNtPathName_U (lpNewFileName,
232 &DstPathU,
233 NULL,
234 NULL))
235 {
236 DPRINT("Invalid destination path\n");
237 CloseHandle(hFile);
238 SetLastError(ERROR_PATH_NOT_FOUND);
239 return FALSE;
240 }
241
242 FileRename = alloca(sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
243 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == MOVEFILE_REPLACE_EXISTING)
244 FileRename->ReplaceIfExists = TRUE;
245 else
246 FileRename->ReplaceIfExists = FALSE;
247
248 memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
249 RtlFreeHeap (RtlGetProcessHeap (),
250 0,
251 DstPathU.Buffer);
252 /*
253 * FIXME:
254 * Is the length the count of characters or the length of the buffer?
255 */
256 FileRename->FileNameLength = DstPathU.Length / sizeof(WCHAR);
257 errCode = NtSetInformationFile (hFile,
258 &IoStatusBlock,
259 FileRename,
260 sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
261 FileRenameInformation);
262 CloseHandle(hFile);
263
264 if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY)
265 {
266 folder = TRUE;
267 }
268
269
270 /*
271 * FIXME:
272 * Fail now move the folder
273 * Before we fail at CreateFileW
274 */
275
276
277 if (NT_SUCCESS(errCode))
278 {
279 Result = TRUE;
280 }
281 else
282 {
283 if (folder==FALSE)
284 {
285 Result = CopyFileExW (lpExistingFileName,
286 lpNewFileName,
287 lpProgressRoutine,
288 lpData,
289 NULL,
290 FileRename->ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS);
291 if (Result)
292 {
293 /* Cleanup the source file */
294 Result = DeleteFileW (lpExistingFileName);
295 }
296 }
297 else
298 {
299 /* move folder code start */
300 WIN32_FIND_DATAW findBuffer;
301 LPWSTR lpExistingFileName2 = NULL;
302 LPWSTR lpNewFileName2 = NULL;
303 LPWSTR lpDeleteFile = NULL;
304 INT size;
305 INT size2;
306 BOOL loop = TRUE;
307 BOOL Result = FALSE;
308 INT max_size = MAX_PATH;
309
310
311 /* Build the string */
312 size = wcslen(lpExistingFileName);
313 if (size+6> max_size)
314 max_size = size + 6;
315
316 lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
317 if (lpDeleteFile == NULL)
318 return FALSE;
319
320 lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
321 if (lpNewFileName2 == NULL)
322 {
323 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
324 return FALSE;
325 }
326
327 lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
328 if (lpNewFileName2 == NULL)
329 {
330 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
331 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
332 return FALSE;
333 }
334
335 wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName);
336 wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0");
337
338 /* Get the file name */
339 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
340 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
341 if (hFile == NULL)
342 loop=FALSE;
343
344 if (findBuffer.cFileName[0] == L'\0')
345 loop=FALSE;
346
347
348 /* FIXME
349 * remove readonly flag from source folder and do not set the readonly flag to dest folder
350 */
351 RemoveReadOnlyAttributeW(lpExistingFileName);
352 RemoveReadOnlyAttributeW(lpNewFileName);
353 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
354 CreateDirectoryW(lpNewFileName, NULL);
355
356 /* search the files/folders and move them */
357 while (loop==TRUE)
358 {
359 Result = TRUE;
360
361 if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L".")))
362 {
363 loop = FindNextFileW(hFile, &findBuffer);
364
365 if (!loop)
366 {
367 size = wcslen(lpExistingFileName2)-4;
368 FindClose(hFile);
369 wcscpy( &lpExistingFileName2[size],L"\0");
370
371 if (wcsncmp(lpExistingFileName,lpExistingFileName2,size))
372 {
373 DWORD Attributes;
374
375 FindClose(hFile);
376
377 /* delete folder */
378 DPRINT("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile);
379
380 /* remove system folder flag other wise we can not delete the folder */
381 Attributes = GetFileAttributesW(lpExistingFileName2);
382 if (Attributes != INVALID_FILE_ATTRIBUTES)
383 {
384 SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
385 }
386
387 RemoveReadOnlyAttributeW(lpExistingFileName2);
388
389 Result = RemoveDirectoryW(lpExistingFileName2);
390 if (Result == FALSE)
391 break;
392
393 loop=TRUE;
394 size = wcslen(lpExistingFileName);
395
396 if (size+6>max_size)
397 {
398 if (lpNewFileName2 != NULL)
399 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
400
401 if (lpExistingFileName2 != NULL)
402 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
403
404 if (lpDeleteFile != NULL)
405 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
406
407 return FALSE;
408 }
409
410 wcscpy( lpExistingFileName2,lpExistingFileName);
411 wcscpy( &lpExistingFileName2[size],L"\\*.*\0");
412
413 /* Get the file name */
414 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
415 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
416 }
417 }
418 continue;
419 }
420
421 if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
422 {
423
424 /* Build the new src string */
425 size = wcslen(findBuffer.cFileName);
426 size2= wcslen(lpExistingFileName2);
427
428 if (size2+size+6>max_size)
429 {
430 FindClose(hFile);
431
432 if (lpNewFileName2 != NULL)
433 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
434
435 if (lpExistingFileName2 != NULL)
436 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
437
438 if (lpDeleteFile != NULL)
439 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
440
441 return FALSE;
442 }
443
444 wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName);
445 wcscpy( &lpExistingFileName2[size2+size-3],L"\0");
446
447
448 /* Continue */
449 wcscpy( lpDeleteFile,lpExistingFileName2);
450 wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0");
451
452
453 /* Build the new dst string */
454 size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName);
455 size2 = wcslen(lpExistingFileName);
456
457 if (size>max_size)
458 {
459 FindClose(hFile);
460
461 if (lpNewFileName2 != NULL)
462 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
463
464 if (lpExistingFileName2 != NULL)
465 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
466
467 if (lpDeleteFile != NULL)
468 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
469
470 return FALSE;
471 }
472
473 wcscpy( lpNewFileName2,lpNewFileName);
474 size = wcslen(lpNewFileName);
475 wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]);
476 size = wcslen(lpNewFileName2);
477 wcscpy( &lpNewFileName2[size-4],L"\0");
478
479 /* Create Folder */
480
481 /* FIXME
482 * remove readonly flag from source folder and do not set the readonly flag to dest folder
483 */
484 RemoveReadOnlyAttributeW(lpDeleteFile);
485 RemoveReadOnlyAttributeW(lpNewFileName2);
486
487 CreateDirectoryW(lpNewFileName2,NULL);
488 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
489
490
491 /* set new search path from src string */
492 FindClose(hFile);
493 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
494 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
495 }
496 else
497 {
498
499 /* Build the new string */
500 size = wcslen(findBuffer.cFileName);
501 size2= wcslen(lpExistingFileName2);
502 wcscpy( lpDeleteFile,lpExistingFileName2);
503 wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName);
504
505 /* Build dest string */
506 size = wcslen(lpDeleteFile) + wcslen(lpNewFileName);
507 size2 = wcslen(lpExistingFileName);
508
509 if (size>max_size)
510 {
511 FindClose(hFile);
512
513 if (lpNewFileName2 != NULL)
514 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
515
516 if (lpExistingFileName2 != NULL)
517 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
518
519 if (lpDeleteFile != NULL)
520 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
521
522 return FALSE;
523 }
524
525 wcscpy( lpNewFileName2,lpNewFileName);
526 size = wcslen(lpNewFileName);
527 wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]);
528
529
530 /* overrite existsen file, if the file got the flag have readonly
531 * we need reomve that flag
532 */
533
534 /* copy file */
535
536 DPRINT("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2);
537 RemoveReadOnlyAttributeW(lpDeleteFile);
538 RemoveReadOnlyAttributeW(lpNewFileName2);
539
540 Result = CopyFileExW (lpDeleteFile,
541 lpNewFileName2,
542 lpProgressRoutine,
543 lpData,
544 NULL,
545 0);
546
547 if (Result == FALSE)
548 break;
549
550 /* delete file */
551 DPRINT("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2);
552 Result = RemoveReadOnlyAttributeW(lpDeleteFile);
553 if (Result == FALSE)
554 break;
555
556 DPRINT("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile);
557 Result = DeleteFileW(lpDeleteFile);
558 if (Result == FALSE)
559 break;
560
561 }
562 loop = FindNextFileW(hFile, &findBuffer);
563 }
564
565
566 /* Remove last folder */
567 if ((loop == FALSE) && (Result != FALSE))
568 {
569 DWORD Attributes;
570
571 FindClose(hFile);
572 Attributes = GetFileAttributesW(lpDeleteFile);
573 if (Attributes != INVALID_FILE_ATTRIBUTES)
574 {
575 SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
576 }
577
578 Result = RemoveDirectoryW(lpExistingFileName);
579 }
580
581 /* Cleanup */
582 FindClose(hFile);
583
584 if (lpNewFileName2 != NULL)
585 {
586 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
587 lpNewFileName2 = NULL;
588 }
589
590 if (lpExistingFileName2 != NULL)
591 {
592 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
593 lpExistingFileName2 = NULL;
594 }
595
596 if (lpDeleteFile != NULL)
597 {
598 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
599 lpDeleteFile = NULL;
600 }
601
602 return Result;
603
604 // end move folder code
605 }
606 }
607
608
609 return Result;
610 }
611
612
613 /*
614 * @implemented
615 */
616 BOOL
617 STDCALL
618 MoveFileWithProgressA (
619 LPCSTR lpExistingFileName,
620 LPCSTR lpNewFileName,
621 LPPROGRESS_ROUTINE lpProgressRoutine,
622 LPVOID lpData,
623 DWORD dwFlags
624 )
625 {
626 PWCHAR ExistingFileNameW;
627 PWCHAR NewFileNameW;
628 BOOL ret;
629
630 if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
631 return FALSE;
632
633 if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
634 return FALSE;
635
636 ret = MoveFileWithProgressW (ExistingFileNameW ,
637 NewFileNameW,
638 lpProgressRoutine,
639 lpData,
640 dwFlags);
641
642 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
643
644 return ret;
645 }
646
647
648 /*
649 * @implemented
650 */
651 BOOL
652 STDCALL
653 MoveFileW (
654 LPCWSTR lpExistingFileName,
655 LPCWSTR lpNewFileName
656 )
657 {
658 return MoveFileExW (lpExistingFileName,
659 lpNewFileName,
660 MOVEFILE_COPY_ALLOWED);
661 }
662
663
664 /*
665 * @implemented
666 */
667 BOOL
668 STDCALL
669 MoveFileExW (
670 LPCWSTR lpExistingFileName,
671 LPCWSTR lpNewFileName,
672 DWORD dwFlags
673 )
674 {
675 return MoveFileWithProgressW (lpExistingFileName,
676 lpNewFileName,
677 NULL,
678 NULL,
679 dwFlags);
680 }
681
682
683 /*
684 * @implemented
685 */
686 BOOL
687 STDCALL
688 MoveFileA (
689 LPCSTR lpExistingFileName,
690 LPCSTR lpNewFileName
691 )
692 {
693 return MoveFileExA (lpExistingFileName,
694 lpNewFileName,
695 MOVEFILE_COPY_ALLOWED);
696 }
697
698
699 /*
700 * @implemented
701 */
702 BOOL
703 STDCALL
704 MoveFileExA (
705 LPCSTR lpExistingFileName,
706 LPCSTR lpNewFileName,
707 DWORD dwFlags
708 )
709 {
710 return MoveFileWithProgressA (lpExistingFileName,
711 lpNewFileName,
712 NULL,
713 NULL,
714 dwFlags);
715 }
716
717 /* EOF */