Aric Stewart : shell32: Improve the FO_MOVE operation.
Alexandre Julliard
julliard at winehq.org
Thu Feb 27 14:49:24 CST 2014
Module: wine
Branch: master
Commit: a2c905244b0e2c0b58026b70571b58f0d9646ccc
URL: http://source.winehq.org/git/wine.git/?a=commit;h=a2c905244b0e2c0b58026b70571b58f0d9646ccc
Author: Aric Stewart <aric at codeweavers.com>
Date: Thu Feb 27 20:09:11 2014 +0900
shell32: Improve the FO_MOVE operation.
---
dlls/shell32/shlfileop.c | 42 ++++++++++++----
dlls/shell32/tests/shlfileop.c | 105 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 136 insertions(+), 11 deletions(-)
diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c
index 4ef7803..e6d44b0 100644
--- a/dlls/shell32/shlfileop.c
+++ b/dlls/shell32/shlfileop.c
@@ -53,6 +53,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell);
#define FO_MASK 0xF
+#define DE_SAMEFILE 0x71
+#define DE_DESTSAMETREE 0x7D
+
static const WCHAR wWildcardFile[] = {'*',0};
static const WCHAR wWildcardChars[] = {'*','?',0};
@@ -1428,11 +1431,15 @@ static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, co
static DWORD move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
{
DWORD i;
+ INT mismatched = 0;
const FILE_ENTRY *entryToMove;
const FILE_ENTRY *fileDest;
- if (!flFrom->dwNumFiles || !flTo->dwNumFiles)
- return ERROR_CANCELLED;
+ if (!flFrom->dwNumFiles)
+ return ERROR_SUCCESS;
+
+ if (!flTo->dwNumFiles)
+ return ERROR_FILE_NOT_FOUND;
if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
@@ -1450,29 +1457,44 @@ static DWORD move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, con
if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
return ERROR_CANCELLED;
- if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
- flFrom->dwNumFiles != flTo->dwNumFiles)
- {
- return ERROR_CANCELLED;
- }
+ if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
+ mismatched = flFrom->dwNumFiles - flTo->dwNumFiles;
fileDest = &flTo->feFiles[0];
for (i = 0; i < flFrom->dwNumFiles; i++)
{
entryToMove = &flFrom->feFiles[i];
- if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
- fileDest = &flTo->feFiles[i];
-
if (!PathFileExistsW(fileDest->szDirectory))
return ERROR_CANCELLED;
+ if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
+ {
+ if (i >= flTo->dwNumFiles)
+ break;
+ fileDest = &flTo->feFiles[i];
+ if (mismatched && !fileDest->bExists)
+ {
+ create_dest_dirs(flTo->feFiles[i].szFullPath);
+ flTo->feFiles[i].bExists = TRUE;
+ flTo->feFiles[i].attributes = FILE_ATTRIBUTE_DIRECTORY;
+ }
+ }
+
if (fileDest->bExists && IsAttribDir(fileDest->attributes))
move_to_dir(lpFileOp, entryToMove, fileDest);
else
SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath);
}
+ if (mismatched > 0)
+ {
+ if (flFrom->bAnyDirectories)
+ return DE_DESTSAMETREE;
+ else
+ return DE_SAMEFILE;
+ }
+
return ERROR_SUCCESS;
}
diff --git a/dlls/shell32/tests/shlfileop.c b/dlls/shell32/tests/shlfileop.c
index 14ace24..64d0fbc 100644
--- a/dlls/shell32/tests/shlfileop.c
+++ b/dlls/shell32/tests/shlfileop.c
@@ -1871,7 +1871,8 @@ static void test_move(void)
init_shfo_tests();
- /* number of sources do not correspond to number of targets */
+ /* number of sources do not correspond to number of targets,
+ include directories */
set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
set_curr_dir_path(to, "test6.txt\0test7.txt\0");
retval = SHFileOperationA(&shfo2);
@@ -1903,6 +1904,108 @@ static void test_move(void)
}
init_shfo_tests();
+ /* number of sources do not correspond to number of targets,
+ files only,
+ from exceeds to */
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test3.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0");
+ retval = SHFileOperationA(&shfo2);
+ if (dir_exists("test6.txt"))
+ {
+ if (retval == ERROR_SUCCESS)
+ {
+ /* Old shell32 */
+ DeleteFileA("test6.txt\\test1.txt");
+ DeleteFileA("test6.txt\\test2.txt");
+ RemoveDirectoryA("test6.txt\\test4.txt");
+ RemoveDirectoryA("test6.txt");
+ }
+ else
+ {
+ /* Vista and W2K8 (broken or new behavior ?) */
+ ok(retval == DE_SAMEFILE, "Expected DE_SAMEFILE, got %d\n", retval);
+ ok(DeleteFileA("test6.txt\\test1.txt"), "The file is not moved\n");
+ RemoveDirectoryA("test6.txt");
+ ok(DeleteFileA("test7.txt\\test2.txt"), "The file is not moved\n");
+ RemoveDirectoryA("test7.txt");
+ ok(file_exists("test3.txt"), "File should not be moved\n");
+ }
+ }
+ else
+ {
+ expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* Win9x, NT4 */);
+ ok(!file_exists("test6.txt"), "The file is not moved - many files are "
+ "specified as a target\n");
+ }
+
+ init_shfo_tests();
+ /* number of sources do not correspond to number of targets,
+ files only,
+ too exceeds from */
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ retval = SHFileOperationA(&shfo2);
+ if (dir_exists("test6.txt"))
+ {
+ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+ ok(DeleteFileA("test6.txt\\test1.txt"),"The file is not moved\n");
+ ok(DeleteFileA("test7.txt\\test2.txt"),"The file is not moved\n");
+ ok(!dir_exists("test8.txt") && !file_exists("test8.txt"),
+ "Directory should not be created\n");
+ RemoveDirectoryA("test6.txt");
+ RemoveDirectoryA("test7.txt");
+ }
+ else
+ {
+ expect_retval(ERROR_CANCELLED, DE_OPCANCELLED /* WinXp, Win2k */);
+ ok(!file_exists("test6.txt"), "The file is not moved - many files are "
+ "specified as a target\n");
+ }
+
+ init_shfo_tests();
+ /* number of sources do not correspond to number of targets,
+ target directories */
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test3.txt\0");
+ set_curr_dir_path(to, "test4.txt\0test5.txt\0");
+ retval = SHFileOperationA(&shfo2);
+ if (dir_exists("test5.txt"))
+ {
+ ok(retval == DE_SAMEFILE, "Expected DE_SAMEFILE, got %d\n", retval);
+ ok(DeleteFileA("test4.txt\\test1.txt"),"The file is not moved\n");
+ ok(DeleteFileA("test5.txt\\test2.txt"),"The file is not moved\n");
+ ok(file_exists("test3.txt"), "The file is not moved\n");
+ RemoveDirectoryA("test4.txt");
+ RemoveDirectoryA("test5.txt");
+ }
+ else
+ {
+ ok(retval == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", retval);
+ ok(DeleteFileA("test4.txt\\test1.txt"),"The file is not moved\n");
+ ok(DeleteFileA("test4.txt\\test2.txt"),"The file is not moved\n");
+ ok(DeleteFileA("test4.txt\\test3.txt"),"The file is not moved\n");
+ }
+
+
+ init_shfo_tests();
+ /* 0 incomming files */
+ set_curr_dir_path(from, "\0\0");
+ set_curr_dir_path(to, "test6.txt\0\0");
+ retval = SHFileOperationA(&shfo2);
+ ok(retval == ERROR_SUCCESS || retval == ERROR_ACCESS_DENIED
+ , "Expected ERROR_SUCCESS || ERROR_ACCESS_DENIED, got %d\n", retval);
+ ok(!file_exists("test6.txt"), "The file should not exist\n");
+
+ init_shfo_tests();
+ /* 0 outgoing files */
+ set_curr_dir_path(from, "test1\0\0");
+ set_curr_dir_path(to, "\0\0");
+ retval = SHFileOperationA(&shfo2);
+ ok(retval == ERROR_FILE_NOT_FOUND ||
+ broken(retval == 1026)
+ , "Expected ERROR_FILE_NOT_FOUND, got %d\n", retval);
+ ok(!file_exists("test6.txt"), "The file should not exist\n");
+
+ init_shfo_tests();
set_curr_dir_path(from, "test3.txt\0");
set_curr_dir_path(to, "test4.txt\\test1.txt\0");
More information about the wine-cvs
mailing list