From: "Juan P. Montiel" jpmontielr@gmail.com
--- dlls/shell32/shlfileop.c | 83 ++++++++++++++++++++++++++++++++-- dlls/shell32/tests/shlfileop.c | 48 ++++++++++---------- 2 files changed, 102 insertions(+), 29 deletions(-)
diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c index f24c7b7df23..53e7d0d1927 100644 --- a/dlls/shell32/shlfileop.c +++ b/dlls/shell32/shlfileop.c @@ -1836,6 +1836,7 @@ enum copy_engine_opcode { COPY_ENGINE_MOVE, COPY_ENGINE_REMOVE_DIRECTORY_SILENT, + COPY_ENGINE_NEW_ITEM_DIRECTORY, };
#define TSF_UNKNOWN_MEGRE_FLAG 0x1000 @@ -2180,6 +2181,57 @@ static HRESULT perform_file_operations(struct file_operation *operation) IShellItem_Release(p.new_item); break; } + + case COPY_ENGINE_NEW_ITEM_DIRECTORY: + { + WCHAR *folder_path; + WCHAR name_path[MAX_PATH]; + size_t name_len; + DWORD attrb; + + /* Native crashes with a NULL IShellItem *, so do we */ + IShellItem_GetDisplayName(op->folder, SIGDN_FILESYSPATH, &folder_path); + attrb = GetFileAttributesW(folder_path); + if(attrb == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND) + { + if (!CreateDirectoryW(folder_path, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + } + if(IsAttribFile(attrb)) + { + return E_FAIL; + } + + name_len = wcslen(op->name); + if (name_len == 0 || name_len + 1 >= 256) + return E_UNEXPECTED; + + if (!PathCombineW(name_path, folder_path, op->name)) + return HRESULT_FROM_WIN32(GetLastError()); + + attrb = GetFileAttributesW(name_path); + if(attrb == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND) + { + if (!CreateDirectoryW(name_path, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + } + else if (attrb == FILE_ATTRIBUTE_DIRECTORY) + { + unsigned int n = 2; + WCHAR name_path_n[MAX_PATH]; + do { + swprintf(name_path_n, ARRAY_SIZE(name_path_n), L"%s (%u)", name_path, n); + attrb = GetFileAttributesW(name_path_n); + ++n; + } while (attrb == FILE_ATTRIBUTE_DIRECTORY); + + if (!CreateDirectoryW(name_path_n, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + } + + CoTaskMemFree(folder_path); + break; + } } set_file_operation_progress(operation, list_count(&operation->ops), operation->progress_sofar + 1); TRACE("op %d, hr %#lx.\n", op->opcode, hr); @@ -2402,13 +2454,34 @@ static HRESULT WINAPI file_operation_DeleteItems(IFileOperation *iface, IUnknown return E_NOTIMPL; }
-static HRESULT WINAPI file_operation_NewItem(IFileOperation *iface, IShellItem *folder, DWORD attributes, - LPCWSTR name, LPCWSTR template, IFileOperationProgressSink *sink) +static HRESULT WINAPI file_operation_NewItem(IFileOperation *iface, IShellItem *folder, + DWORD attributes, LPCWSTR name, LPCWSTR template, IFileOperationProgressSink *sink) { - FIXME("(%p, %p, %lx, %s, %s, %p): stub.\n", iface, folder, attributes, - debugstr_w(name), debugstr_w(template), sink); + size_t name_len;
- return E_NOTIMPL; + TRACE("(%p, %p, %lx, %s, %s, %p).\n", iface, folder, attributes, + debugstr_w(name), debugstr_w(template), sink); + + /* Native crashes with a NULL name, so do we */ + name_len = wcslen(name); + + if (attributes == FILE_ATTRIBUTE_DIRECTORY) + { + add_operation(impl_from_IFileOperation(iface), + COPY_ENGINE_NEW_ITEM_DIRECTORY, NULL, folder, name, NULL, 0, NULL); + } + else + { + FIXME("Unsupported attributes %#lx.\n", attributes); + return E_NOTIMPL; + } + + if (name_len == 0) + return E_INVALIDARG; + else if (name_len + 1 >= 256) + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + + return S_OK; }
static HRESULT WINAPI file_operation_PerformOperations(IFileOperation *iface) diff --git a/dlls/shell32/tests/shlfileop.c b/dlls/shell32/tests/shlfileop.c index 35b3d179517..67702f8ca93 100644 --- a/dlls/shell32/tests/shlfileop.c +++ b/dlls/shell32/tests/shlfileop.c @@ -3081,30 +3081,30 @@ static void test_file_operation(void)
/* NewItem then PerformOperations, NewItem then PerformOperations */ hr = IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); - 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); PathCombineW(path, dirpath, L"test_dir"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n");
hr = IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); - 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); PathCombineW(path, dirpath, L"test_dir (2)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n");
/* NewItem, NewItem, then PerformOperations */ hr = IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); - 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); PathCombineW(path, dirpath, L"test_dir (3)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n"); PathCombineW(path, dirpath, L"test_dir (4)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n");
/* In-between dir suffix */ IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); @@ -3113,25 +3113,25 @@ static void test_file_operation(void) IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"test_dir", NULL, NULL); IFileOperation_PerformOperations(operation); PathCombineW(path, dirpath, L"test_dir (5)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n"); PathCombineW(path, dirpath, L"test_dir (6)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n"); PathCombineW(path, dirpath, L"test_dir (7)"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n");
/* Intermediate folder deletion but creation through its IShellItem * */ PathCombineW(path, dirpath, L"test_dir"); hr = SHCreateItemFromParsingName(path, NULL, &IID_IShellItem, (void**)&item); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); RemoveDirectoryW(path);
hr = IFileOperation_NewItem(operation, item, FILE_ATTRIBUTE_DIRECTORY, L"nested_test_dir", NULL, NULL); - 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); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(dir_existsW(path), "directory should exist.\n"); PathCombineW(path, dirpath, L"test_dir\nested_test_dir"); - todo_wine ok(dir_existsW(path), "directory should exist.\n"); + ok(dir_existsW(path), "directory should exist.\n");
/* Native NewItem passes with a NULL IShellIem pointer, but then PerformOperations crashes */ if (0) @@ -3147,9 +3147,9 @@ static void test_file_operation(void) hr = SHCreateItemFromParsingName(tmpfile, NULL, &IID_IShellItem, (void**)&item2); ok(hr == S_OK, "got %#lx.\n", hr); hr = IFileOperation_NewItem(operation, item2, FILE_ATTRIBUTE_DIRECTORY, L"nested_test_dir", NULL, NULL); - 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 == E_FAIL, "got %#lx.\n", hr); + ok(hr == E_FAIL, "got %#lx.\n", hr);
/* Bad IShellItem pointer Crashes native NewItem */ if (0) @@ -3160,7 +3160,7 @@ static void test_file_operation(void)
/* Empty name for the new item */ hr = IFileOperation_NewItem(operation, folder, FILE_ATTRIBUTE_DIRECTORY, L"", NULL, NULL); - todo_wine ok(hr == S_OK /* Windows 7 */ || hr == E_INVALIDARG, "got %#lx.\n", hr); + ok(hr == S_OK /* Windows 7 */ || hr == E_INVALIDARG, "got %#lx.\n", hr); if (hr == E_INVALIDARG) { /* Crashes w7 */ @@ -3186,7 +3186,7 @@ static void test_file_operation(void) L"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" L"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", NULL, NULL); - todo_wine ok(hr == S_OK /* Windows 8.1, 10 v1507 */ || + ok(hr == S_OK /* Windows 8.1, 10 v1507 */ || hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) /* 7, newer 10, 11 */, "got %#lx.\n", hr); if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { @@ -3218,7 +3218,7 @@ static void test_file_operation(void) if (item) { refcount = IShellItem_Release(item); - todo_wine ok(!refcount, "got %ld.\n", refcount); + ok(!refcount, "got %ld.\n", refcount); }
refcount = IShellItem_Release(item2);