Skip to content

Commit 6d10925

Browse files
authored
[SHELL32] Correctly compare pidls for SHBrowseForFolder BFFM_SETEXPANDED (reactos#7499)
_ILIsEqualSimple just does a memcmp and that does not work for all items. Should improve shell change notifications (CORE-13950).
1 parent 81860b4 commit 6d10925

File tree

9 files changed

+297
-114
lines changed

9 files changed

+297
-114
lines changed

dll/win32/shell32/CDefView.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,9 +4608,9 @@ HRESULT WINAPI CDefView::GetAdvise(DWORD *pAspects, DWORD *pAdvf, IAdviseSink **
46084608

46094609
HRESULT STDMETHODCALLTYPE CDefView::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
46104610
{
4611-
if (IsEqualIID(guidService, SID_IShellBrowser))
4611+
if (IsEqualIID(guidService, SID_IShellBrowser) && m_pShellBrowser)
46124612
return m_pShellBrowser->QueryInterface(riid, ppvObject);
4613-
else if(IsEqualIID(guidService, SID_IFolderView))
4613+
else if (IsEqualIID(guidService, SID_IFolderView))
46144614
return QueryInterface(riid, ppvObject);
46154615

46164616
return E_NOINTERFACE;

dll/win32/shell32/brfolder.cpp

Lines changed: 103 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,36 @@
1111
#include "precomp.h"
1212

1313
#include <ui/layout.h> // Resizable window
14+
#include <compat_undoc.h> // RosGetProcessEffectiveVersion
1415

1516
WINE_DEFAULT_DEBUG_CHANNEL(shell);
1617

1718
#define SHV_CHANGE_NOTIFY (WM_USER + 0x1111)
1819

20+
static LPITEMIDLIST
21+
ILCloneToDepth(LPCITEMIDLIST pidlSrc, UINT depth)
22+
{
23+
SIZE_T cb = 0;
24+
for (LPCITEMIDLIST pidl = pidlSrc; pidl && depth--; pidl = ILGetNext(pidl))
25+
cb += pidl->mkid.cb;
26+
27+
LPITEMIDLIST pidlOut = (LPITEMIDLIST)SHAlloc(cb + sizeof(WORD));
28+
if (pidlOut)
29+
{
30+
CopyMemory(pidlOut, pidlSrc, cb);
31+
ZeroMemory(((BYTE*)pidlOut) + cb, sizeof(WORD));
32+
}
33+
return pidlOut;
34+
}
35+
36+
static INT
37+
GetIconIndex(PCIDLIST_ABSOLUTE pidl, UINT uFlags)
38+
{
39+
SHFILEINFOW sfi;
40+
SHGetFileInfoW((LPCWSTR)pidl, 0, &sfi, sizeof(sfi), uFlags);
41+
return sfi.iIcon;
42+
}
43+
1944
struct BrFolder
2045
{
2146
LPBROWSEINFOW lpBrowseInfo;
@@ -145,12 +170,9 @@ BrFolder_InitTreeView(BrFolder *info)
145170
{
146171
HIMAGELIST hImageList;
147172
HRESULT hr;
148-
CComPtr<IShellFolder> lpsfParent;
149-
CComPtr<IEnumIDList> pEnumChildren;
150173
HTREEITEM hItem;
151174

152175
Shell_GetImageLists(NULL, &hImageList);
153-
154176
if (hImageList)
155177
TreeView_SetImageList(info->hwndTreeView, hImageList, 0);
156178

@@ -171,54 +193,25 @@ BrFolder_InitTreeView(BrFolder *info)
171193
ILRemoveLastID(pidlParent);
172194
PCIDLIST_RELATIVE pidlChild = ILFindLastID(pidlRoot);
173195

174-
if (_ILIsDesktop(pidlParent))
175-
{
176-
hr = SHGetDesktopFolder(&lpsfParent);
177-
if (FAILED_UNEXPECTEDLY(hr))
178-
return;
179-
}
180-
else
181-
{
182-
CComPtr<IShellFolder> lpsfDesktop;
183-
hr = SHGetDesktopFolder(&lpsfDesktop);
184-
if (FAILED_UNEXPECTEDLY(hr))
185-
return;
186-
187-
hr = lpsfDesktop->BindToObject(pidlParent, NULL, IID_PPV_ARG(IShellFolder, &lpsfParent));
188-
if (FAILED_UNEXPECTEDLY(hr))
189-
return;
190-
}
196+
CComPtr<IShellFolder> lpsfParent;
197+
hr = SHBindToObject(NULL, pidlParent, /*NULL, */ IID_PPV_ARG(IShellFolder, &lpsfParent));
198+
if (FAILED_UNEXPECTEDLY(hr))
199+
return;
191200

192201
TreeView_DeleteItem(info->hwndTreeView, TVI_ROOT);
193202
hItem = BrFolder_InsertItem(info, lpsfParent, pidlChild, pidlParent, TVI_ROOT);
194203
TreeView_Expand(info->hwndTreeView, hItem, TVE_EXPAND);
195204
}
196205

197-
static INT
198-
BrFolder_GetIcon(PCIDLIST_ABSOLUTE pidl, UINT uFlags)
199-
{
200-
SHFILEINFOW sfi;
201-
SHGetFileInfoW((LPCWSTR)pidl, 0, &sfi, sizeof(sfi), uFlags);
202-
return sfi.iIcon;
203-
}
204-
205206
static void
206207
BrFolder_GetIconPair(PCIDLIST_ABSOLUTE pidl, LPTVITEMW pItem)
207208
{
208-
DWORD flags;
209-
210-
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlDesktop;
209+
static const ITEMIDLIST idlDesktop = { };
211210
if (!pidl)
212-
{
213-
pidlDesktop.Attach(_ILCreateDesktop());
214-
pidl = pidlDesktop;
215-
}
216-
217-
flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
218-
pItem->iImage = BrFolder_GetIcon(pidl, flags);
219-
220-
flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
221-
pItem->iSelectedImage = BrFolder_GetIcon(pidl, flags);
211+
pidl = &idlDesktop;
212+
DWORD flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
213+
pItem->iImage = GetIconIndex(pidl, flags);
214+
pItem->iSelectedImage = GetIconIndex(pidl, flags | SHGFI_OPENICON);
222215
}
223216

224217
/******************************************************************************
@@ -766,6 +759,19 @@ BrFolder_OnInitDialog(HWND hWnd, BrFolder *info)
766759
SHCNE_ALLEVENTS,
767760
SHV_CHANGE_NOTIFY, 1, &ntreg);
768761

762+
if (!lpBrowseInfo->pidlRoot)
763+
{
764+
UINT csidl = (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) ? CSIDL_PERSONAL : CSIDL_DRIVES;
765+
LPITEMIDLIST pidl = SHCloneSpecialIDList(NULL, csidl, TRUE);
766+
if (pidl)
767+
{
768+
SendMessageW(info->hWnd, BFFM_SETSELECTION, FALSE, (LPARAM)pidl);
769+
if (csidl == CSIDL_DRIVES)
770+
SendMessageW(info->hWnd, BFFM_SETEXPANDED, FALSE, (LPARAM)pidl);
771+
ILFree(pidl);
772+
}
773+
}
774+
769775
BrFolder_Callback(info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0);
770776

771777
SHAutoComplete(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT),
@@ -985,60 +991,56 @@ BrFolder_OnContextMenu(BrFolder &info, LPARAM lParam)
985991
}
986992

987993
static BOOL
988-
BrFolder_OnSetExpandedPidl(BrFolder *info, LPITEMIDLIST pidlSelection, HTREEITEM *phItem)
994+
BrFolder_ExpandToPidl(BrFolder *info, LPITEMIDLIST pidlSelection, HTREEITEM *phItem)
989995
{
996+
LPITEMIDLIST pidlCurrent = pidlSelection;
990997
if (_ILIsDesktop(pidlSelection))
991998
{
992999
if (phItem)
9931000
*phItem = TVI_ROOT;
9941001
return TRUE;
9951002
}
9961003

997-
// Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
998-
// the sub-tree currently displayed.
999-
PCIDLIST_ABSOLUTE pidlRoot = info->lpBrowseInfo->pidlRoot;
1000-
LPITEMIDLIST pidlCurrent = pidlSelection;
1001-
while (!_ILIsEmpty(pidlRoot) && _ILIsEqualSimple(pidlRoot, pidlCurrent))
1002-
{
1003-
pidlRoot = ILGetNext(pidlRoot);
1004-
pidlCurrent = ILGetNext(pidlCurrent);
1005-
}
1006-
1007-
// The given ID List is not part of the SHBrowseForFolder's current sub-tree.
1008-
if (!_ILIsEmpty(pidlRoot))
1009-
{
1010-
if (phItem)
1011-
*phItem = NULL;
1012-
return FALSE;
1013-
}
1014-
10151004
// Initialize item to point to the first child of the root folder.
10161005
TVITEMEXW item = { TVIF_PARAM };
10171006
item.hItem = TreeView_GetRoot(info->hwndTreeView);
10181007
if (item.hItem)
10191008
item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem);
10201009

10211010
// Walk the tree along the nodes corresponding to the remaining ITEMIDLIST
1022-
while (item.hItem && !_ILIsEmpty(pidlCurrent))
1011+
UINT depth = _ILGetDepth(info->lpBrowseInfo->pidlRoot);
1012+
while (item.hItem && pidlCurrent)
10231013
{
1014+
LPITEMIDLIST pidlNeedle = ILCloneToDepth(pidlSelection, ++depth);
1015+
if (_ILIsEmpty(pidlNeedle))
1016+
{
1017+
ILFree(pidlNeedle);
1018+
item.hItem = NULL; // Failure
1019+
break;
1020+
}
1021+
next:
10241022
TreeView_GetItem(info->hwndTreeView, &item);
1025-
BrItemData *pItemData = (BrItemData *)item.lParam;
1026-
1027-
if (_ILIsEqualSimple(pItemData->pidlChild, pidlCurrent))
1023+
const BrItemData *pItemData = (BrItemData *)item.lParam;
1024+
if (ILIsEqual(pItemData->pidlFull, pidlNeedle))
10281025
{
1029-
pidlCurrent = ILGetNext(pidlCurrent);
1030-
if (!_ILIsEmpty(pidlCurrent))
1026+
BOOL done = _ILGetDepth(pidlSelection) == _ILGetDepth(pidlNeedle);
1027+
if (done)
1028+
{
1029+
pidlCurrent = NULL; // Success
1030+
}
1031+
else
10311032
{
1032-
// Only expand current node and move on to its first child,
1033-
// if we didn't already reach the last SHITEMID
10341033
TreeView_Expand(info->hwndTreeView, item.hItem, TVE_EXPAND);
10351034
item.hItem = TreeView_GetChild(info->hwndTreeView, item.hItem);
10361035
}
10371036
}
10381037
else
10391038
{
10401039
item.hItem = TreeView_GetNextSibling(info->hwndTreeView, item.hItem);
1040+
if (item.hItem)
1041+
goto next;
10411042
}
1043+
ILFree(pidlNeedle);
10421044
}
10431045

10441046
if (phItem)
@@ -1048,19 +1050,26 @@ BrFolder_OnSetExpandedPidl(BrFolder *info, LPITEMIDLIST pidlSelection, HTREEITEM
10481050
}
10491051

10501052
static BOOL
1051-
BrFolder_OnSetExpandedString(BrFolder *info, LPWSTR pszString, HTREEITEM *phItem)
1053+
BrFolder_ExpandToString(BrFolder *info, LPWSTR pszString, HTREEITEM *phItem)
10521054
{
1053-
CComPtr<IShellFolder> psfDesktop;
1054-
HRESULT hr = SHGetDesktopFolder(&psfDesktop);
1055-
if (FAILED_UNEXPECTEDLY(hr))
1056-
return FALSE;
1057-
10581055
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlSelection;
1059-
hr = psfDesktop->ParseDisplayName(NULL, NULL, pszString, NULL, &pidlSelection, NULL);
1060-
if (FAILED_UNEXPECTEDLY(hr))
1061-
return FALSE;
1056+
HRESULT hr = SHParseDisplayName(pszString, NULL, &pidlSelection, 0, NULL);
1057+
return SUCCEEDED(hr) && BrFolder_ExpandToPidl(info, pidlSelection, phItem);
1058+
}
1059+
1060+
static BOOL
1061+
BrFolder_OnSetExpanded(BrFolder *info, LPITEMIDLIST pidlSelection, LPWSTR pszString)
1062+
{
1063+
HTREEITEM hItem;
1064+
BOOL ret;
1065+
if (pszString)
1066+
ret = BrFolder_ExpandToString(info, pszString, &hItem);
1067+
else
1068+
ret = BrFolder_ExpandToPidl(info, pidlSelection, &hItem);
10621069

1063-
return BrFolder_OnSetExpandedPidl(info, pidlSelection, phItem);
1070+
if (ret)
1071+
TreeView_Expand(info->hwndTreeView, hItem, TVE_EXPAND);
1072+
return ret;
10641073
}
10651074

10661075
static BOOL
@@ -1070,7 +1079,7 @@ BrFolder_OnSetSelectionPidl(BrFolder *info, LPITEMIDLIST pidlSelection)
10701079
return FALSE;
10711080

10721081
HTREEITEM hItem;
1073-
BOOL ret = BrFolder_OnSetExpandedPidl(info, pidlSelection, &hItem);
1082+
BOOL ret = BrFolder_ExpandToPidl(info, pidlSelection, &hItem);
10741083
if (ret)
10751084
TreeView_SelectItem(info->hwndTreeView, hItem);
10761085
return ret;
@@ -1083,7 +1092,7 @@ BrFolder_OnSetSelectionW(BrFolder *info, LPWSTR pszSelection)
10831092
return FALSE;
10841093

10851094
HTREEITEM hItem;
1086-
BOOL ret = BrFolder_OnSetExpandedString(info, pszSelection, &hItem);
1095+
BOOL ret = BrFolder_ExpandToString(info, pszSelection, &hItem);
10871096
if (ret)
10881097
TreeView_SelectItem(info->hwndTreeView, hItem);
10891098
return ret;
@@ -1304,9 +1313,9 @@ BrFolderDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
13041313

13051314
case BFFM_SETEXPANDED: // Unicode only
13061315
if (wParam) // String
1307-
return BrFolder_OnSetExpandedString(info, (LPWSTR)lParam, NULL);
1316+
return BrFolder_OnSetExpanded(info, NULL, (LPWSTR)lParam);
13081317
else // PIDL
1309-
return BrFolder_OnSetExpandedPidl(info, (LPITEMIDLIST)lParam, NULL);
1318+
return BrFolder_OnSetExpanded(info, (LPITEMIDLIST)lParam, NULL);
13101319

13111320
case SHV_CHANGE_NOTIFY:
13121321
BrFolder_OnChange(info, wParam, lParam);
@@ -1362,16 +1371,23 @@ SHBrowseForFolderW(LPBROWSEINFOW lpbi)
13621371
{
13631372
TRACE("%p\n", lpbi);
13641373

1374+
// MSDN says the caller must initialize COM. We do it anyway in case the caller forgot.
1375+
COleInit OleInit;
13651376
BrFolder info = { lpbi };
13661377

1367-
HRESULT hr = OleInitialize(NULL);
1368-
13691378
INT id = ((lpbi->ulFlags & BIF_USENEWUI) ? IDD_BROWSE_FOR_FOLDER_NEW : IDD_BROWSE_FOR_FOLDER);
13701379
INT_PTR ret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(id), lpbi->hwndOwner,
13711380
BrFolderDlgProc, (LPARAM)&info);
1372-
if (SUCCEEDED(hr))
1373-
OleUninitialize();
1374-
1381+
if (ret == IDOK && !(lpbi->ulFlags & BIF_NOTRANSLATETARGETS) &&
1382+
RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WINXP)
1383+
{
1384+
PIDLIST_ABSOLUTE pidlTarget;
1385+
if (SHELL_GetIDListTarget(info.pidlRet, &pidlTarget) == S_OK)
1386+
{
1387+
ILFree(info.pidlRet);
1388+
info.pidlRet = pidlTarget;
1389+
}
1390+
}
13751391
if (ret != IDOK)
13761392
{
13771393
ILFree(info.pidlRet);

dll/win32/shell32/folders/CFSFolder.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,20 @@ static LPCWSTR GetItemFileName(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
2020
if (pDataW)
2121
return pDataW->wszName;
2222
LPPIDLDATA pdata = _ILGetDataPointer(pidl);
23-
if ((pdata->type & PT_VALUEW) == PT_VALUEW)
23+
if (_ILGetFSType(pidl) & PT_FS_UNICODE_FLAG)
2424
return (LPWSTR)pdata->u.file.szNames;
2525
if (_ILSimpleGetTextW(pidl, Buf, cchMax))
2626
return Buf;
2727
return NULL;
2828
}
2929

30+
static BOOL IsRealItem(const ITEMIDLIST &idl)
31+
{
32+
// PIDLs created with SHSimpleIDListFromPath contain no data, otherwise, the item is "real"
33+
FileStruct &fsitem = ((PIDLDATA*)idl.mkid.abID)->u.file;
34+
return fsitem.dwFileSize | fsitem.uFileDate;
35+
}
36+
3037
static HKEY OpenKeyFromFileType(LPCWSTR pExtension, LPCWSTR KeyName)
3138
{
3239
HKEY hkey;
@@ -1079,7 +1086,10 @@ HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
10791086
/* When sorting between a File and a Folder, the Folder gets sorted first */
10801087
if (bIsFolder1 != bIsFolder2)
10811088
{
1082-
return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
1089+
// ...but only if neither of them were generated by SHSimpleIDListFromPath
1090+
// because in that case we cannot tell if it's a file or a folder.
1091+
if (IsRealItem(*pidl1) && IsRealItem(*pidl2))
1092+
return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
10831093
}
10841094

10851095
int result = 0;

dll/win32/shell32/precomp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ SHBindToObjectEx(
219219

220220
DWORD
221221
SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes);
222+
HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl);
222223
HRESULT SHCoInitializeAnyApartment(VOID);
223224

224225
HRESULT

dll/win32/shell32/shlfileop.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,7 +1633,8 @@ static HRESULT delete_files(FILE_OPERATION *op, const FILE_LIST *flFrom)
16331633
BOOL bDelete;
16341634
if (TRASH_TrashFile(fileEntry->szFullPath))
16351635
{
1636-
SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fileEntry->szFullPath, NULL);
1636+
UINT event = IsAttribFile(fileEntry->attributes) ? SHCNE_DELETE : SHCNE_RMDIR;
1637+
SHChangeNotify(event, SHCNF_PATHW, fileEntry->szFullPath, NULL);
16371638
continue;
16381639
}
16391640

@@ -1652,9 +1653,7 @@ static HRESULT delete_files(FILE_OPERATION *op, const FILE_LIST *flFrom)
16521653

16531654
/* delete the file or directory */
16541655
if (IsAttribFile(fileEntry->attributes))
1655-
{
16561656
bPathExists = (ERROR_SUCCESS == SHNotifyDeleteFileW(op, fileEntry->szFullPath));
1657-
}
16581657
else
16591658
bPathExists = SHELL_DeleteDirectoryW(op, fileEntry->szFullPath, FALSE);
16601659

0 commit comments

Comments
 (0)