Skip to content

Commit

Permalink
Fix issue with empty treeview in Windows PE
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
derceg committed Dec 1, 2022
1 parent 6429b62 commit f1b7f71
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Explorer++/Explorer++/ShellTreeView/ShellTreeView.cpp
Expand Up @@ -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))
{
Expand Down
32 changes: 21 additions & 11 deletions Explorer++/Helper/ShellHelper.cpp
Expand Up @@ -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<IShellFolder> 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)
Expand Down
1 change: 1 addition & 0 deletions Explorer++/Helper/ShellHelper.h
Expand Up @@ -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,
Expand Down

0 comments on commit f1b7f71

Please sign in to comment.