From f1b7f71bfba294d9be911a23b383dd87c76bb074 Mon Sep 17 00:00:00 2001 From: David Erceg Date: Wed, 30 Nov 2022 18:10:18 +1100 Subject: [PATCH] Fix issue with empty treeview in Windows PE Previously, the treeview was using SHGetFolderLocation() with CSIDL_DESKTOP to retrieve the pidl of the root desktop folder. However, that method fails on Windows PE, where the filesystem desktop folder doesn't exist by default. That then meant that the treeview would be empty in Windows PE. To fix that, an alternative method is now used to retrieve the pidl of the root desktop folder. This issue was reported in https://explorerplusplus.com/forum/viewtopic.php?f=2&t=1232. --- .../ShellTreeView/ShellTreeView.cpp | 2 +- Explorer++/Helper/ShellHelper.cpp | 32 ++++++++++++------- Explorer++/Helper/ShellHelper.h | 1 + 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Explorer++/Explorer++/ShellTreeView/ShellTreeView.cpp b/Explorer++/Explorer++/ShellTreeView/ShellTreeView.cpp index 659d0c668..eae1e351e 100644 --- a/Explorer++/Explorer++/ShellTreeView/ShellTreeView.cpp +++ b/Explorer++/Explorer++/ShellTreeView/ShellTreeView.cpp @@ -316,7 +316,7 @@ HTREEITEM ShellTreeView::AddRoot() TreeView_DeleteAllItems(m_hTreeView); unique_pidl_absolute pidl; - HRESULT hr = SHGetFolderLocation(nullptr, CSIDL_DESKTOP, nullptr, 0, wil::out_param(pidl)); + HRESULT hr = GetRootPidl(wil::out_param(pidl)); if (FAILED(hr)) { diff --git a/Explorer++/Helper/ShellHelper.cpp b/Explorer++/Helper/ShellHelper.cpp index 2f85f9d7e..fab684404 100644 --- a/Explorer++/Helper/ShellHelper.cpp +++ b/Explorer++/Helper/ShellHelper.cpp @@ -188,19 +188,29 @@ HRESULT GetVirtualParentPath(PCIDLIST_ABSOLUTE pidlDirectory, PIDLIST_ABSOLUTE * } } -BOOL IsNamespaceRoot(PCIDLIST_ABSOLUTE pidl) +HRESULT GetRootPidl(PIDLIST_ABSOLUTE *pidl) { - BOOL bNamespaceRoot = FALSE; - unique_pidl_absolute pidlDesktop; - HRESULT hr = - SHGetFolderLocation(nullptr, CSIDL_DESKTOP, nullptr, 0, wil::out_param(pidlDesktop)); - - if (SUCCEEDED(hr)) - { - bNamespaceRoot = ArePidlsEquivalent(pidl, pidlDesktop.get()); - } + // While using SHGetKnownFolderIDList() with FOLDERID_Desktop would be simpler than the method + // used here, that method fails in Windows PE (with ERROR_FILE_NOT_FOUND). That failure is + // unusual, since although the filesystem desktop folder doesn't exist, the virtual desktop + // folder at the root of the shell namespace is still accessible and the pidl returned by + // SHGetKnownFolderIDList() represents the root folder. + // Retrieving the pidl using the method below works consistently, however. + wil::com_ptr_nothrow desktop; + RETURN_IF_FAILED(SHGetDesktopFolder(&desktop)); + return SHGetIDListFromObject(desktop.get(), pidl); +} - return bNamespaceRoot; +BOOL IsNamespaceRoot(PCIDLIST_ABSOLUTE pidl) +{ + // This method essentially just checks whether pidl->mkid.cb is 0. That will be the case for the + // pidl representing the root desktop folder. Although the documentation doesn't appear to state + // that, it can be confirmed by retrieving the root desktop pidl (pidl->mkid.cb will always be + // 0). + // The Wine test at + // https://gitlab.winehq.org/wine/wine/-/blob/7ed17ec2511c85ce2e1f1fed0a9d04d85f658a5b/dlls/shell32/tests/shellpath.c#L2916 + // also provides evidence that an empty pidl represents the root desktop folder. + return ILIsEmpty(pidl); } HRESULT DecodeFriendlyPath(const std::wstring &friendlyPath, std::wstring &parsingPath) diff --git a/Explorer++/Helper/ShellHelper.h b/Explorer++/Helper/ShellHelper.h index b2f2f0e6a..4e3ebea2a 100644 --- a/Explorer++/Helper/ShellHelper.h +++ b/Explorer++/Helper/ShellHelper.h @@ -74,6 +74,7 @@ HRESULT GetDisplayName(IShellFolder *shellFolder, PCITEMID_CHILD pidlChild, DWOR std::wstring &output); HRESULT GetCsidlDisplayName(int csidl, DWORD flags, std::wstring &output); HRESULT GetVirtualParentPath(PCIDLIST_ABSOLUTE pidlDirectory, PIDLIST_ABSOLUTE *pidlParent); +HRESULT GetRootPidl(PIDLIST_ABSOLUTE *pidl); BOOL IsNamespaceRoot(PCIDLIST_ABSOLUTE pidl); HRESULT BindToIdl(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv); HRESULT GetUIObjectOf(IShellFolder *pShellFolder, HWND hwndOwner, UINT cidl,