From: Paul Gofman pgofman@codeweavers.com
--- dlls/shell32/shlfileop.c | 96 +++++++++++++++++++++++++++++++++- dlls/shell32/tests/shlfileop.c | 20 +++---- 2 files changed, 104 insertions(+), 12 deletions(-)
diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c index 3cb9b28de81..9cf834d37fe 100644 --- a/dlls/shell32/shlfileop.c +++ b/dlls/shell32/shlfileop.c @@ -1849,6 +1849,21 @@ HRESULT WINAPI SHMultiFileProperties(IDataObject *pdtobj, DWORD flags) return E_NOTIMPL; }
+enum copy_engine_opcode +{ + COPY_ENGINE_MOVE, +}; + +struct copy_engine_operation +{ + struct list entry; + IFileOperationProgressSink *sink; + enum copy_engine_opcode opcode; + IShellItem *folder; + PIDLIST_ABSOLUTE item_pidl; + WCHAR *name; +}; + struct file_operation_sink { struct list entry; @@ -1862,8 +1877,74 @@ struct file_operation LONG ref; struct list sinks; DWORD next_cookie; + struct list ops; };
+static void free_file_operation_ops(struct file_operation *operation) +{ + struct copy_engine_operation *op, *next; + + LIST_FOR_EACH_ENTRY_SAFE(op, next, &operation->ops, struct copy_engine_operation, entry) + { + if (op->sink) + IFileOperationProgressSink_Release(op->sink); + ILFree(op->item_pidl); + if (op->folder) + IShellItem_Release(op->folder); + CoTaskMemFree(op->name); + list_remove(&op->entry); + free(op); + } +} + +static HRESULT add_operation(struct file_operation *operation, enum copy_engine_opcode opcode, IShellItem *item, + IShellItem *folder, const WCHAR *name, IFileOperationProgressSink *sink) +{ + struct copy_engine_operation *op; + HRESULT hr; + + if (!name) + name = L""; + + if (!(op = calloc(1, sizeof(*op)))) + return E_OUTOFMEMORY; + + op->opcode = opcode; + if (item && FAILED((hr = SHGetIDListFromObject((IUnknown *)item, &op->item_pidl)))) + { + hr = E_INVALIDARG; + goto error; + } + + if (folder) + { + IShellItem_AddRef(folder); + op->folder = folder; + } + if (!(op->name = wcsdup(name))) + { + hr = E_OUTOFMEMORY; + goto error; + } + if (sink) + { + IFileOperationProgressSink_AddRef(sink); + op->sink = sink; + } + list_add_tail(&operation->ops, &op->entry); + return S_OK; + +error: + ILFree(op->item_pidl); + if (op->folder) + IShellItem_Release(op->folder); + if (op->sink) + IFileOperationProgressSink_Release(sink); + CoTaskMemFree(op->name); + free(op); + return hr; +} + static inline struct file_operation *impl_from_IFileOperation(IFileOperation *iface) { return CONTAINING_RECORD(iface, struct file_operation, IFileOperation_iface); @@ -1916,6 +1997,7 @@ static ULONG WINAPI file_operation_Release(IFileOperation *iface) list_remove(&sink->entry); free(sink); } + free_file_operation_ops(operation); free(operation); }
@@ -2027,9 +2109,12 @@ static HRESULT WINAPI file_operation_RenameItems(IFileOperation *iface, IUnknown static HRESULT WINAPI file_operation_MoveItem(IFileOperation *iface, IShellItem *item, IShellItem *folder, LPCWSTR name, IFileOperationProgressSink *sink) { - FIXME("(%p, %p, %p, %s, %p): stub.\n", iface, item, folder, debugstr_w(name), sink); + TRACE("(%p, %p, %p, %s, %p).\n", iface, item, folder, debugstr_w(name), sink);
- return E_NOTIMPL; + if (!folder || !item) + return E_INVALIDARG; + + return add_operation(impl_from_IFileOperation(iface), COPY_ENGINE_MOVE, item, folder, name, sink); }
static HRESULT WINAPI file_operation_MoveItems(IFileOperation *iface, IUnknown *items, IShellItem *folder) @@ -2080,8 +2165,14 @@ 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); + FIXME("(%p): stub.\n", iface);
+ if (list_empty(&operation->ops)) + return E_UNEXPECTED; + + free_file_operation_ops(operation); return E_NOTIMPL; }
@@ -2130,6 +2221,7 @@ HRESULT WINAPI IFileOperation_Constructor(IUnknown *outer, REFIID riid, void **o
object->IFileOperation_iface.lpVtbl = &file_operation_vtbl; list_init(&object->sinks); + list_init(&object->ops); object->ref = 1;
hr = IFileOperation_QueryInterface(&object->IFileOperation_iface, riid, out); diff --git a/dlls/shell32/tests/shlfileop.c b/dlls/shell32/tests/shlfileop.c index ec8e8e5869f..ae978b0e32e 100644 --- a/dlls/shell32/tests/shlfileop.c +++ b/dlls/shell32/tests/shlfileop.c @@ -3219,7 +3219,7 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == E_UNEXPECTED, "got %#lx.\n", hr); + ok(hr == E_UNEXPECTED, "got %#lx.\n", hr);
hr = CoCreateInstance(&CLSID_ShellItem, NULL, CLSCTX_INPROC_SERVER, &IID_IShellItem, (void **)&item); ok(hr == S_OK, "got %#lx.\n", hr); @@ -3227,7 +3227,7 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_MoveItem(operation, item, folder, L"test", NULL); - todo_wine ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr);
GetTempPathW(ARRAY_SIZE(dirpath), dirpath); PathCombineW(tmpfile, dirpath, L"testfile1"); @@ -3243,7 +3243,7 @@ static void test_file_operation(void) progress_init_check_notifications(progress, ARRAY_SIZE(notifications1), notifications1, dirpath, &expected_notif); progress_init_check_notifications(progress2, ARRAY_SIZE(notifications1), notifications1, dirpath, &expected_notif); hr = IFileOperation_MoveItem(operation, item, folder, L"test", progress2); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + 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); hr = IFileOperation_PerformOperations(operation); @@ -3255,11 +3255,11 @@ static void test_file_operation(void) progress_end_check_notifications(progress);
hr = IFileOperation_PerformOperations(operation); - todo_wine ok(hr == E_UNEXPECTED, "got %#lx.\n", hr); + ok(hr == E_UNEXPECTED, "got %#lx.\n", hr);
/* Input file does not exist: PerformOperations succeeds, 'aborted' is set. */ hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + 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); @@ -3277,7 +3277,7 @@ static void test_file_operation(void) PathCombineW(path, dirpath, L"test"); set_shell_item_path(item, path, TRUE); hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + 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); @@ -3348,7 +3348,7 @@ static void test_file_operation(void)
/* Source is directory, destination test2 is file. */ hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + 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); @@ -3371,7 +3371,7 @@ static void test_file_operation(void) todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_MoveItem(operation, item, folder, L"test2", NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + 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); @@ -3406,9 +3406,9 @@ static void test_file_operation(void) ok(hr == S_OK, "got %#lx.\n", hr);
hr = IFileOperation_MoveItem(operation, item, folder, L"test2", progress2); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_MoveItem(operation, item2, folder, NULL, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); refcount = IShellItem_Release(item2); ok(!refcount, "got %ld.\n", refcount); progress_init_check_notifications(progress, ARRAY_SIZE(notifications6), notifications6, dirpath, &expected_notif);