Zhiyi Zhang : shell32: Implement SHOpenFolderAndSelectItems().

Alexandre Julliard julliard at winehq.org
Fri Jul 15 14:44:42 CDT 2022


Module: wine
Branch: master
Commit: c03409e9d782ccd96ce4f14913814acc0184ee43
URL:    https://gitlab.winehq.org/wine/wine/-/commit/c03409e9d782ccd96ce4f14913814acc0184ee43

Author: Zhiyi Zhang <zzhang at 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 at 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);
     }




More information about the wine-cvs mailing list