From: Paul Gofman pgofman@codeweavers.com
--- dlls/shell32/shlfileop.c | 104 +++++++++++++++++++++++++++++++-- dlls/shell32/tests/shlfileop.c | 60 +++++++++---------- 2 files changed, 128 insertions(+), 36 deletions(-)
diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c index 9cf834d37fe..6c310186f7a 100644 --- a/dlls/shell32/shlfileop.c +++ b/dlls/shell32/shlfileop.c @@ -41,6 +41,7 @@ #include "shlwapi.h" #include "shell32_main.h" #include "shfldr.h" +#include "sherrors.h" #include "wine/debug.h" #include "wine/list.h"
@@ -1878,6 +1879,8 @@ struct file_operation struct list sinks; DWORD next_cookie; struct list ops; + DWORD flags; + BOOL aborted; };
static void free_file_operation_ops(struct file_operation *operation) @@ -1945,6 +1948,82 @@ error: return hr; }
+static HRESULT copy_engine_move(struct file_operation *operation, struct copy_engine_operation *op, IShellItem *src_item, + IShellItem **dest_folder) +{ + WCHAR path[MAX_PATH], item_path[MAX_PATH]; + DWORD src_attrs, dst_attrs; + WCHAR *str, *ptr; + HRESULT hr; + + *dest_folder = NULL; + if (FAILED(hr = IShellItem_GetDisplayName(src_item, SIGDN_FILESYSPATH, &str))) + return hr; + wcscpy_s(item_path, ARRAY_SIZE(item_path), str); + if (!*op->name && (ptr = StrRChrW(str, NULL, '\'))) + { + free(op->name); + op->name = wcsdup(ptr + 1); + } + CoTaskMemFree(str); + + if (FAILED(hr = IShellItem_GetDisplayName(op->folder, SIGDN_FILESYSPATH, &str))) + return hr; + hr = PathCombineW(path, str, op->name) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); + CoTaskMemFree(str); + if (FAILED(hr)) + return hr; + + if ((src_attrs = GetFileAttributesW(item_path)) == INVALID_FILE_ATTRIBUTES) + return HRESULT_FROM_WIN32(GetLastError()); + dst_attrs = GetFileAttributesW(path); + if (IsAttribFile(src_attrs) && IsAttribDir(dst_attrs)) + return COPYENGINE_E_FILE_IS_FLD_DEST; + if (IsAttribDir(src_attrs) && IsAttribFile(dst_attrs)) + return COPYENGINE_E_FLD_IS_FILE_DEST; + if (dst_attrs == INVALID_FILE_ATTRIBUTES || (IsAttribFile(src_attrs) && IsAttribFile(dst_attrs))) + { + if (MoveFileExW(item_path, path, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) + { + if (FAILED((hr = SHCreateItemFromParsingName(path, NULL, &IID_IShellItem, (void **)dest_folder)))) + return hr; + return COPYENGINE_S_DONT_PROCESS_CHILDREN; + } + IShellItem_Release(*dest_folder); + return HRESULT_FROM_WIN32(GetLastError()); + } + + /* Merge directory to existing directory. */ + FIXME("Megre directories.\n"); + return E_FAIL; +} + +static HRESULT perform_file_operations(struct file_operation *operation) +{ + struct copy_engine_operation *op; + IShellItem *item, *created; + HRESULT hr; + + LIST_FOR_EACH_ENTRY(op, &operation->ops, struct copy_engine_operation, entry) + { + hr = E_FAIL; + switch (op->opcode) + { + case COPY_ENGINE_MOVE: + if (FAILED(hr = SHCreateItemFromIDList(op->item_pidl, &IID_IShellItem, (void**)&item))) + break; + hr = copy_engine_move(operation, op, item, &created); + IShellItem_Release(item); + if (SUCCEEDED(hr)) + IShellItem_Release(created); + break; + } + TRACE("op %d, hr %#lx.\n", op->opcode, hr); + operation->aborted = FAILED(hr) || operation->aborted; + } + return S_OK; +} + static inline struct file_operation *impl_from_IFileOperation(IFileOperation *iface) { return CONTAINING_RECORD(iface, struct file_operation, IFileOperation_iface); @@ -2044,9 +2123,12 @@ static HRESULT WINAPI file_operation_Unadvise(IFileOperation *iface, DWORD cooki
static HRESULT WINAPI file_operation_SetOperationFlags(IFileOperation *iface, DWORD flags) { - FIXME("(%p, %lx): stub.\n", iface, flags); + struct file_operation *operation = impl_from_IFileOperation(iface);
- return E_NOTIMPL; + TRACE("(%p, %lx).\n", iface, flags); + + operation->flags = flags; + return S_OK; }
static HRESULT WINAPI file_operation_SetProgressMessage(IFileOperation *iface, LPCWSTR message) @@ -2166,21 +2248,31 @@ static HRESULT WINAPI file_operation_NewItem(IFileOperation *iface, IShellItem * static HRESULT WINAPI file_operation_PerformOperations(IFileOperation *iface) { struct file_operation *operation = impl_from_IFileOperation(iface); + HRESULT hr;
- FIXME("(%p): stub.\n", iface); + TRACE("(%p).\n", iface);
if (list_empty(&operation->ops)) return E_UNEXPECTED;
+ if (operation->flags != FOF_NO_UI) + FIXME("Unhandled flags %#lx.\n", operation->flags); + hr = perform_file_operations(operation); free_file_operation_ops(operation); - return E_NOTIMPL; + return hr; }
static HRESULT WINAPI file_operation_GetAnyOperationsAborted(IFileOperation *iface, BOOL *aborted) { - FIXME("(%p, %p): stub.\n", iface, aborted); + struct file_operation *operation = impl_from_IFileOperation(iface);
- return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, aborted); + + if (!aborted) + return E_POINTER; + *aborted = operation->aborted; + TRACE("-> aborted %d.\n", *aborted); + return S_OK; }
static const IFileOperationVtbl file_operation_vtbl = diff --git a/dlls/shell32/tests/shlfileop.c b/dlls/shell32/tests/shlfileop.c index ae978b0e32e..018c503d59d 100644 --- a/dlls/shell32/tests/shlfileop.c +++ b/dlls/shell32/tests/shlfileop.c @@ -3063,14 +3063,14 @@ static IFileOperationProgressSink *create_progress_sink(unsigned int instance_id return &obj->IFileOperationProgressSink_iface; }
-static void set_shell_item_path(IShellItem *item, const WCHAR *path, BOOL todo) +static void set_shell_item_path(IShellItem *item, const WCHAR *path) { IPersistIDList *idlist; ITEMIDLIST *pidl; HRESULT hr;
hr = SHParseDisplayName(path, NULL, &pidl, 0, NULL); - todo_wine_if(todo) ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); if (FAILED(hr)) return; hr = IShellItem_QueryInterface(item, &IID_IPersistIDList, (void **)&idlist); @@ -3233,11 +3233,11 @@ static void test_file_operation(void) PathCombineW(tmpfile, dirpath, L"testfile1"); createTestFileW(tmpfile);
- set_shell_item_path(folder, dirpath, FALSE); - set_shell_item_path(item, tmpfile, FALSE); + set_shell_item_path(folder, dirpath); + set_shell_item_path(item, tmpfile);
hr = IFileOperation_SetOperationFlags(operation, 0); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr);
progress2 = create_progress_sink(1); progress_init_check_notifications(progress, ARRAY_SIZE(notifications1), notifications1, dirpath, &expected_notif); @@ -3245,13 +3245,13 @@ static void test_file_operation(void) hr = IFileOperation_MoveItem(operation, item, folder, L"test", progress2); ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_SetOperationFlags(operation, FOF_NO_UI); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); aborted = 0xdeadbeef; hr = IFileOperation_GetAnyOperationsAborted(operation, &aborted); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(!aborted, "got %d.\n", aborted); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(!aborted, "got %d.\n", aborted); progress_end_check_notifications(progress);
hr = IFileOperation_PerformOperations(operation); @@ -3262,36 +3262,36 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr); progress_init_check_notifications(progress, ARRAY_SIZE(notifications2), notifications2, dirpath, &expected_notif); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); progress_end_check_notifications(progress); aborted = 0; hr = IFileOperation_GetAnyOperationsAborted(operation, &aborted); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(aborted == TRUE, "got %d.\n", aborted); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(aborted == TRUE, "got %d.\n", aborted); aborted = 0; hr = IFileOperation_GetAnyOperationsAborted(operation, &aborted); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(aborted == TRUE, "got %d.\n", aborted); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(aborted == TRUE, "got %d.\n", aborted);
/* Input file exists: PerformOperations succeeds, the item data at the moment of MoveItem is used. */ PathCombineW(path, dirpath, L"test"); - set_shell_item_path(item, path, TRUE); + set_shell_item_path(item, path); hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); ok(hr == S_OK, "got %#lx.\n", hr); PathCombineW(tmpfile, dirpath, L"testfile2"); /* Actual paths are fetched at _MoveItem and not at _Perform operation: changing item after doesn't matter. */ createTestFileW(tmpfile); - set_shell_item_path(item, tmpfile, FALSE); + set_shell_item_path(item, tmpfile); bret = DeleteFileW(tmpfile); ok(bret, "got error %ld.\n", GetLastError()); progress_init_check_notifications(progress, ARRAY_SIZE(notifications3), notifications3, dirpath, &expected_notif); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); progress_end_check_notifications(progress); aborted = 0; hr = IFileOperation_GetAnyOperationsAborted(operation, &aborted); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(aborted == TRUE, "got %d.\n", aborted); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(aborted == TRUE, "got %d.\n", aborted); ret = GetFileAttributesW(tmpfile); ok(ret == INVALID_FILE_ATTRIBUTES, "got %#lx.\n", ret); PathCombineW(path, dirpath, L"test"); @@ -3299,9 +3299,9 @@ static void test_file_operation(void) ok(ret == INVALID_FILE_ATTRIBUTES, "got %#lx.\n", ret); PathCombineW(path, dirpath, L"test2"); ret = GetFileAttributesW(path); - todo_wine ok(ret != INVALID_FILE_ATTRIBUTES, "got %#lx.\n", ret); + ok(ret != INVALID_FILE_ATTRIBUTES, "got %#lx.\n", ret); bret = DeleteFileW(path); - todo_wine ok(bret, "got error %ld.\n", GetLastError()); + ok(bret, "got error %ld.\n", GetLastError());
refcount = IShellItem_Release(item); ok(!refcount, "got %ld.\n", refcount); @@ -3323,7 +3323,7 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_SetOperationFlags(operation, FOF_NO_UI); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_Advise(operation, progress, &cookie); ok(hr == S_OK, "got %#lx.\n", hr); @@ -3351,12 +3351,12 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr); progress_init_check_notifications(progress, ARRAY_SIZE(notifications4), notifications4, dirpath, &expected_notif); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); progress_end_check_notifications(progress); aborted = 0; hr = IFileOperation_GetAnyOperationsAborted(operation, &aborted); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(aborted, "got %d.\n", aborted); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(aborted, "got %d.\n", aborted);
bret = DeleteFileW(path); ok(bret, "got error %ld.\n", GetLastError()); @@ -3368,13 +3368,13 @@ static void test_file_operation(void) hr = IFileOperation_Advise(operation, progress, &cookie); ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_SetOperationFlags(operation, FOF_NO_UI); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); ok(hr == S_OK, "got %#lx.\n", hr); progress_init_check_notifications(progress, ARRAY_SIZE(notifications5), notifications5, dirpath, &expected_notif); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); progress_end_check_notifications(progress); IFileOperation_Release(operation);
@@ -3384,11 +3384,11 @@ static void test_file_operation(void) hr = IFileOperation_Advise(operation, progress, &cookie); ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_SetOperationFlags(operation, FOF_NO_UI); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr);
PathCombineW(path, dirpath, L"test_dir3"); bret = CreateDirectoryW(path, NULL); - set_shell_item_path(item, path, FALSE); + set_shell_item_path(item, path); ok(bret, "got error %ld.\n", GetLastError()); PathCombineW(tmpfile, path, L"testfile5"); createTestFileW(tmpfile); @@ -3413,7 +3413,7 @@ static void test_file_operation(void) ok(!refcount, "got %ld.\n", refcount); progress_init_check_notifications(progress, ARRAY_SIZE(notifications6), notifications6, dirpath, &expected_notif); hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); progress_end_check_notifications(progress);
PathCombineW(path, dirpath, L"test_dir2");