Module: wine Branch: master Commit: c03409e9d782ccd96ce4f14913814acc0184ee43 URL: https://gitlab.winehq.org/wine/wine/-/commit/c03409e9d782ccd96ce4f14913814ac...
Author: Zhiyi Zhang zzhang@codeweavers.com Date: Fri Jul 8 17:13:49 2022 +0800
shell32: Implement SHOpenFolderAndSelectItems().
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=39987 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com
---
dlls/shell32/shlfolder.c | 138 ++++++++++++++++++++++++++++++++++++++++- dlls/shell32/tests/shlfolder.c | 11 ---- programs/explorer/explorer.c | 39 ++++++++++++ 3 files changed, 175 insertions(+), 13 deletions(-)
diff --git a/dlls/shell32/shlfolder.c b/dlls/shell32/shlfolder.c index 7308f49c7d5..ab99ed6b4c9 100644 --- a/dlls/shell32/shlfolder.c +++ b/dlls/shell32/shlfolder.c @@ -618,8 +618,142 @@ HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, LPDATAOBJECT lpDataObje HRESULT WINAPI SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD flags) { - FIXME("%p %u %p 0x%lx: stub\n", pidlFolder, cidl, apidl, flags); - return E_NOTIMPL; + static const unsigned int magic = 0xe32ee32e; + unsigned int i, uint_flags, size, child_count = 0; + const ITEMIDLIST *pidl_parent, *pidl_child; + VARIANT var_parent, var_empty; + ITEMIDLIST *pidl_tmp = NULL; + SHELLEXECUTEINFOW sei = {0}; + COPYDATASTRUCT cds = {0}; + IDispatch *dispatch; + int timeout = 1000; + unsigned char *ptr; + IShellWindows *sw; + BOOL ret = FALSE; + HRESULT hr; + LONG hwnd; + + TRACE("%p %u %p 0x%lx\n", pidlFolder, cidl, apidl, flags); + + if (!pidlFolder) + return E_INVALIDARG; + + if (flags & OFASI_OPENDESKTOP) + FIXME("Ignoring unsupported OFASI_OPENDESKTOP flag.\n"); + + if (flags & OFASI_EDIT && cidl > 1) + flags &= ~OFASI_EDIT; + + hr = CoCreateInstance(&CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, &IID_IShellWindows, + (void **)&sw); + if (FAILED(hr)) + return hr; + + if (!cidl) + { + pidl_tmp = ILClone(pidlFolder); + ILRemoveLastID(pidl_tmp); + pidl_parent = pidl_tmp; + + pidl_child = ILFindLastID(pidlFolder); + apidl = &pidl_child; + cidl = 1; + } + else + { + pidl_parent = pidlFolder; + } + + /* Find the existing explorer window for the parent path. Create a new one if not present. */ + VariantInit(&var_empty); + VariantInit(&var_parent); + size = ILGetSize(pidl_parent); + V_VT(&var_parent) = VT_ARRAY | VT_UI1; + V_ARRAY(&var_parent) = SafeArrayCreateVector(VT_UI1, 0, size); + memcpy(V_ARRAY(&var_parent)->pvData, pidl_parent, size); + hr = IShellWindows_FindWindowSW(sw, &var_parent, &var_empty, SWC_EXPLORER, &hwnd, 0, &dispatch); + if (hr != S_OK) + { + sei.cbSize = sizeof(sei); + sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_IDLIST | SEE_MASK_NOASYNC | SEE_MASK_WAITFORINPUTIDLE; + sei.lpVerb = L"explore"; + sei.lpIDList = (void *)pidl_parent; + sei.nShow = SW_NORMAL; + if (!ShellExecuteExW(&sei)) + { + WARN("Failed to create a explorer window.\n"); + goto done; + } + + while (timeout > 0) + { + hr = IShellWindows_FindWindowSW(sw, &var_parent, &var_empty, SWC_EXPLORER, &hwnd, 0, + &dispatch); + if (hr == S_OK) + break; + + timeout -= 100; + Sleep(100); + } + + if (hr != S_OK) + { + WARN("Failed to find the explorer window.\n"); + goto done; + } + } + + /* Send WM_COPYDATA to tell explorer.exe to open windows */ + size = sizeof(cidl) + sizeof(uint_flags); + for (i = 0; i < cidl; ++i) + size += ILGetSize(apidl[i]); + + cds.dwData = magic; + cds.cbData = size; + cds.lpData = malloc(size); + if (!cds.lpData) + { + hr = E_OUTOFMEMORY; + goto done; + } + + /* Add the count of child ITEMIDLIST, set its value at the end */ + ptr = (unsigned char *)cds.lpData + sizeof(cidl); + + /* Add flags. Have to use unsigned int because DWORD may have a different size */ + uint_flags = flags; + memcpy(ptr, &uint_flags, sizeof(uint_flags)); + ptr += sizeof(uint_flags); + + /* Add child ITEMIDLIST */ + for (i = 0; i < cidl; ++i) + { + if (apidl != &pidl_child) + pidl_child = ILFindChild(pidl_parent, apidl[i]); + + if (pidl_child) + { + size = ILGetSize(pidl_child); + memcpy(ptr, pidl_child, size); + ptr += size; + ++child_count; + } + } + + /* Set the count of child ITEMIDLIST */ + memcpy(cds.lpData, &child_count, sizeof(child_count)); + + SetForegroundWindow(GetAncestor((HWND)(LONG_PTR)hwnd, GA_ROOT)); + ret = SendMessageW((HWND)(LONG_PTR)hwnd, WM_COPYDATA, 0, (LPARAM)&cds); + hr = ret ? S_OK : E_FAIL; + +done: + free(cds.lpData); + VariantClear(&var_parent); + if (pidl_tmp) + ILFree(pidl_tmp); + IShellWindows_Release(sw); + return hr; }
/*********************************************************************** diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c index 10a989e9ca4..267c81a0ec5 100644 --- a/dlls/shell32/tests/shlfolder.c +++ b/dlls/shell32/tests/shlfolder.c @@ -5448,15 +5448,12 @@ static void test_SHOpenFolderAndSelectItems(void)
/* NULL folder */ hr = SHOpenFolderAndSelectItems(NULL, 0, NULL, 0); - todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
/* Open and select folder without child items */ folder = ILCreateFromPathW(L"C:\Windows\System32"); hr = SHOpenFolderAndSelectItems(folder, 0, NULL, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(check_window_exists("Windows"), "Failed to create window.\n"); ILFree(folder);
@@ -5464,9 +5461,7 @@ static void test_SHOpenFolderAndSelectItems(void) folder = ILCreateFromPathW(L"C:\Windows"); items[0] = ILCreateFromPathW(L"C:\Windows\System32"); hr = SHOpenFolderAndSelectItems(folder, 1, (PCUITEMID_CHILD_ARRAY)items, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(check_window_exists("Windows"), "Failed to create window.\n"); ILFree(items[0]); ILFree(folder); @@ -5476,9 +5471,7 @@ static void test_SHOpenFolderAndSelectItems(void) items[0] = ILCreateFromPathW(L"C:\Windows\System32"); items[1] = ILCreateFromPathW(L"C:\Windows\Resources"); hr = SHOpenFolderAndSelectItems(folder, 2, (PCUITEMID_CHILD_ARRAY)items, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(check_window_exists("Windows"), "Failed to create window.\n"); ILFree(items[1]); ILFree(items[0]); @@ -5488,9 +5481,7 @@ static void test_SHOpenFolderAndSelectItems(void) folder = ILCreateFromPathW(L"C:\Windows"); items[0] = ILCreateFromPathW(L"C:\Windows\System32"); hr = SHOpenFolderAndSelectItems(folder, 1, (PCUITEMID_CHILD_ARRAY)items, OFASI_EDIT); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(check_window_exists("Windows"), "Failed to create window.\n"); ILFree(items[0]); ILFree(folder); @@ -5500,9 +5491,7 @@ static void test_SHOpenFolderAndSelectItems(void) items[0] = ILCreateFromPathW(L"C:\Windows\System32"); items[1] = ILCreateFromPathW(L"C:\Windows\Resources"); hr = SHOpenFolderAndSelectItems(folder, 2, (PCUITEMID_CHILD_ARRAY)items, 0); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(check_window_exists("Windows"), "Failed to create window.\n"); ILFree(items[1]); ILFree(items[0]); diff --git a/programs/explorer/explorer.c b/programs/explorer/explorer.c index 26928912649..7924991383f 100644 --- a/programs/explorer/explorer.c +++ b/programs/explorer/explorer.c @@ -667,6 +667,43 @@ static LRESULT explorer_on_notify(explorer_info* info,NMHDR* notification) return 0; }
+static BOOL handle_copydata(const explorer_info *info, const COPYDATASTRUCT *cds) +{ + static const unsigned int magic = 0xe32ee32e; + unsigned int i, flags, count; + const ITEMIDLIST *child; + unsigned char *ptr; + IShellView *sv; + SVSIF sv_flags; + + TRACE("\n"); + + /* For SHOpenFolderAndSelectItems() */ + if (cds->dwData != magic) + return FALSE; + + ptr = cds->lpData; + memcpy(&count, ptr, sizeof(count)); + ptr += sizeof(count); + memcpy(&flags, ptr, sizeof(flags)); + ptr += sizeof(flags); + + sv_flags = flags & OFASI_EDIT ? SVSI_EDIT : SVSI_SELECT; + + IExplorerBrowser_GetCurrentView(info->browser, &IID_IShellView, (void **)&sv); + for (i = 0; i < count; ++i) + { + child = (const ITEMIDLIST *)ptr; + if (i == 0) + IShellView_SelectItem(sv, child, sv_flags | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_DESELECTOTHERS); + else + IShellView_SelectItem(sv, child, sv_flags); + ptr += ILGetSize(child); + } + IShellView_Release(sv); + return TRUE; +} + static LRESULT CALLBACK explorer_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { explorer_info *info @@ -718,6 +755,8 @@ static LRESULT CALLBACK explorer_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, L case WM_SIZE: update_window_size(info,HIWORD(lParam),LOWORD(lParam)); break; + case WM_COPYDATA: + return handle_copydata(info, (const COPYDATASTRUCT *)lParam); default: return DefWindowProcW(hwnd,uMsg,wParam,lParam); }