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