From ba6760683b67f47b1ff3eb9d22f10d24dd444b7d Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Wed, 30 Apr 2025 13:31:58 +0200 Subject: [PATCH] [SHELL32] Handle sort order in RegItems (#7906) * [SHELL32] Handle sort order in RegItems This makes the TweakUI "First icon on desktop" setting work correctly. --- dll/win32/shell32/folders/CDesktopFolder.cpp | 6 +- dll/win32/shell32/folders/CDrivesFolder.cpp | 2 +- dll/win32/shell32/folders/CRegFolder.cpp | 90 +++++++++++++------- dll/win32/shell32/wine/pidl.c | 18 ++-- dll/win32/shell32/wine/pidl.h | 24 +++++- 5 files changed, 96 insertions(+), 44 deletions(-) diff --git a/dll/win32/shell32/folders/CDesktopFolder.cpp b/dll/win32/shell32/folders/CDesktopFolder.cpp index 5749c04c755..27e2dafd08b 100644 --- a/dll/win32/shell32/folders/CDesktopFolder.cpp +++ b/dll/win32/shell32/folders/CDesktopFolder.cpp @@ -29,9 +29,9 @@ extern BOOL SHELL32_IsShellFolderNamespaceItemHidden(LPCWSTR SubKey, REFCLSID Cl static const REQUIREDREGITEM g_RequiredItems[] = { - { CLSID_MyComputer, "sysdm.cpl", 0x50 }, - { CLSID_NetworkPlaces, "ncpa.cpl", 0x58 }, - { CLSID_Internet, "inetcpl.cpl", 0x68 }, + { CLSID_MyComputer, "sysdm.cpl", REGITEMORDER_MYCOMPUTER }, + { CLSID_NetworkPlaces, "ncpa.cpl", REGITEMORDER_NETHOOD }, + { CLSID_Internet, "inetcpl.cpl", REGITEMORDER_INTERNET }, }; static const REGFOLDERINFO g_RegFolderInfo = { diff --git a/dll/win32/shell32/folders/CDrivesFolder.cpp b/dll/win32/shell32/folders/CDrivesFolder.cpp index 73d90de4a26..fc6cb265cea 100644 --- a/dll/win32/shell32/folders/CDrivesFolder.cpp +++ b/dll/win32/shell32/folders/CDrivesFolder.cpp @@ -60,7 +60,7 @@ static int iDriveTypeIds[7] = { IDS_DRIVE_FIXED, /* DRIVE_UNKNOWN */ static const REQUIREDREGITEM g_RequiredItems[] = { - { CLSID_ControlPanel, 0, 0x50 }, + { CLSID_ControlPanel, NULL, REGITEMORDER_MYCOMPUTER_CONTROLS }, }; static const REGFOLDERINFO g_RegFolderInfo = { diff --git a/dll/win32/shell32/folders/CRegFolder.cpp b/dll/win32/shell32/folders/CRegFolder.cpp index 603fa47f309..48f47e9df6e 100644 --- a/dll/win32/shell32/folders/CRegFolder.cpp +++ b/dll/win32/shell32/folders/CRegFolder.cpp @@ -25,17 +25,49 @@ WINE_DEFAULT_DEBUG_CHANNEL (shell); -#define DEFAULTSORTORDERINDEX 0x80 // The default for registry items according to Geoff Chappell - static HRESULT CRegItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder, HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IShellFolder *psf, IContextMenu **ppcm); +HRESULT FormatGUIDKey(LPWSTR KeyName, SIZE_T KeySize, LPCWSTR RegPath, const GUID* riid) +{ + WCHAR xriid[CHARS_IN_GUID]; + StringFromGUID2(*riid, xriid, _countof(xriid)); + return StringCchPrintfW(KeyName, KeySize, RegPath, xriid); +} + +static DWORD SHELL_QueryCLSIDValue(_In_ REFCLSID clsid, _In_opt_ LPCWSTR SubKey, _In_opt_ LPCWSTR Value, _In_opt_ PVOID pData, _In_opt_ PDWORD pSize) +{ + const UINT cchGuid = CHARS_IN_GUID - 1, cchClsidSlash = sizeof("CLSID\\") - 1; + WCHAR Path[200]; + wcscpy(Path, L"CLSID\\"); + StringFromGUID2(clsid, Path + 6, CHARS_IN_GUID); + if (SubKey) + { + *(Path + cchClsidSlash + cchGuid) = L'\\'; + wcscpy(Path + cchClsidSlash + cchGuid + 1, SubKey); + } + return RegGetValueW(HKEY_CLASSES_ROOT, Path, Value, RRF_RT_ANY, NULL, pData, pSize); +} + +static bool HasCLSIDShellFolderValue(REFCLSID clsid, LPCWSTR Value) +{ + return SHELL_QueryCLSIDValue(clsid, L"ShellFolder", Value, NULL, NULL) == ERROR_SUCCESS; +} + static inline UINT GetRegItemCLSIDOffset(PIDLTYPE type) { return type == PT_CONTROLS_NEWREGITEM ? 14 : 4; } -static LPITEMIDLIST CreateRegItem(PIDLTYPE type, REFCLSID clsid, BYTE order = 0) +static BYTE GetRegItemOrder(REFCLSID clsid) +{ + DWORD dwOrder, cb = sizeof(dwOrder); + if (SHELL_QueryCLSIDValue(clsid, NULL, L"SortOrderIndex", &dwOrder, &cb) || cb != sizeof(dwOrder)) + dwOrder = REGITEMORDER_DEFAULT; + return (BYTE)dwOrder; +} + +static LPITEMIDLIST CreateRegItem(PIDLTYPE type, REFCLSID clsid, int order = -1) { #if 1 // FIXME: CControlPanelFolder is not ready for this yet if (type == PT_CONTROLS_NEWREGITEM) @@ -49,7 +81,7 @@ static LPITEMIDLIST CreateRegItem(PIDLTYPE type, REFCLSID clsid, BYTE order = 0) ZeroMemory(pidl, cbTotal); // Note: This also initializes the terminator WORD pidl->mkid.cb = cb; pidl->mkid.abID[0] = type; - pidl->mkid.abID[1] = order; + pidl->mkid.abID[1] = order >= 0 ? (BYTE)order : GetRegItemOrder(clsid); *(CLSID*)(SIZE_T(pidl) + offset) = clsid; } return pidl; @@ -61,31 +93,6 @@ static LPITEMIDLIST CreateRegItem(PIDLTYPE type, LPCWSTR clsidstr) return SUCCEEDED(CLSIDFromString(clsidstr, &clsid)) ? CreateRegItem(type, clsid) : NULL; } -HRESULT FormatGUIDKey(LPWSTR KeyName, SIZE_T KeySize, LPCWSTR RegPath, const GUID* riid) -{ - WCHAR xriid[CHARS_IN_GUID]; - StringFromGUID2(*riid, xriid, _countof(xriid)); - return StringCchPrintfW(KeyName, KeySize, RegPath, xriid); -} - -static DWORD SHELL_QueryCLSIDValue(_In_ REFCLSID clsid, _In_opt_ LPCWSTR SubKey, _In_opt_ LPCWSTR Value, _In_opt_ PVOID pData, _In_opt_ PDWORD pSize) -{ - WCHAR Path[MAX_PATH]; - wcscpy(Path, L"CLSID\\"); - StringFromGUID2(clsid, Path + 6, 39); - if (SubKey) - { - wcscpy(Path + 6 + 38, L"\\"); - wcscpy(Path + 6 + 39, SubKey); - } - return RegGetValueW(HKEY_CLASSES_ROOT, Path, Value, RRF_RT_ANY, NULL, pData, pSize); -} - -static bool HasCLSIDShellFolderValue(REFCLSID clsid, LPCWSTR Value) -{ - return SHELL_QueryCLSIDValue(clsid, L"ShellFolder", Value, NULL, NULL) == ERROR_SUCCESS; -} - struct CRegFolderInfo { const REGFOLDERINFO *m_pInfo; @@ -118,6 +125,12 @@ struct CRegFolderInfo return CreateRegItem(GetPidlType(), item.clsid, item.Order); } + WORD GetRegItemOrder(LPCITEMIDLIST pidl) const + { + const CLSID *pCLSID = IsRegItem(pidl); + return pCLSID ? ::GetRegItemOrder(*pCLSID) : 0xffff; + } + LPCWSTR GetParsingPath() const { return m_pInfo->pszParsingPath; } UINT GetCLSIDOffset() const { return GetRegItemCLSIDOffset(m_pInfo->PidlType); } PIDLTYPE GetPidlType() const { return m_pInfo->PidlType; } @@ -314,6 +327,7 @@ class CRegFolder : IShellFolder *m_pOuterFolder; // Not ref-counted CComHeapPtr m_pidlRoot; + HRESULT CompareRegItemsSortOrder(PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2); HRESULT GetGuidItemAttributes (LPCITEMIDLIST pidl, LPDWORD pdwAttributes); BOOL _IsInNameSpace(_In_ LPCITEMIDLIST pidl); @@ -514,6 +528,18 @@ HRESULT WINAPI CRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserv return E_NOTIMPL; } +HRESULT CRegFolder::CompareRegItemsSortOrder(PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) +{ + LPPIDLDATA p1 = (LPPIDLDATA)pidl1->mkid.abID; + LPPIDLDATA p2 = (LPPIDLDATA)pidl2->mkid.abID; + int Order1 = p1->u.guid.uSortOrder > 0x40 ? p1->u.guid.uSortOrder : GetRegItemOrder(pidl1); + int Order2 = p2->u.guid.uSortOrder > 0x40 ? p2->u.guid.uSortOrder : GetRegItemOrder(pidl2); + int Cmp = Order1 - Order2; + if (Cmp != 0) + return MAKE_COMPARE_HRESULT(Cmp); + return SHELL32_CompareDetails(this, COL_NAME, pidl1, pidl2); +} + HRESULT WINAPI CRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) { if (!pidl1 || !pidl2 || pidl1->mkid.cb == 0 || pidl2->mkid.cb == 0) @@ -532,6 +558,12 @@ HRESULT WINAPI CRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, P } else if (clsid1 && clsid2) { + if ((lParam & SHCIDS_COLUMNMASK) == COL_NAME && !(lParam & SHCIDS_CANONICALONLY)) + { + HRESULT hrCmpOrder = CompareRegItemsSortOrder(pidl1, pidl2); + if (hrCmpOrder && SUCCEEDED(hrCmpOrder)) + return hrCmpOrder; + } if (memcmp(clsid1, clsid2, sizeof(GUID)) == 0) return SHELL32_CompareChildren(this, lParam, pidl1, pidl2); diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c index ad86794634e..d927cd48c9c 100644 --- a/dll/win32/shell32/wine/pidl.c +++ b/dll/win32/shell32/wine/pidl.c @@ -1798,19 +1798,19 @@ LPITEMIDLIST _ILCreateDesktop(void) LPITEMIDLIST _ILCreateMyComputer(void) { TRACE("()\n"); - return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_MyComputer); + return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_MyComputer, REGITEMORDER_MYCOMPUTER); } LPITEMIDLIST _ILCreateMyDocuments(void) { TRACE("()\n"); - return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_MyDocuments); + return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_MyDocuments, REGITEMORDER_MYDOCS_DEFAULT); } LPITEMIDLIST _ILCreateIExplore(void) { TRACE("()\n"); - return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_Internet); + return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_Internet, REGITEMORDER_INTERNET); } LPITEMIDLIST _ILCreateControlPanel(void) @@ -1820,7 +1820,7 @@ LPITEMIDLIST _ILCreateControlPanel(void) TRACE("()\n"); if (parent) { - LPITEMIDLIST cpl = _ILCreateGuid(PT_COMPUTER_REGITEM, &CLSID_ControlPanel); + LPITEMIDLIST cpl = _ILCreateGuid(PT_COMPUTER_REGITEM, &CLSID_ControlPanel, REGITEMORDER_MYCOMPUTER_CONTROLS); if (cpl) { ret = ILCombine(parent, cpl); @@ -1861,22 +1861,22 @@ LPITEMIDLIST _ILCreatePrinters(void) LPITEMIDLIST _ILCreateNetwork(void) { TRACE("()\n"); - return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_NetworkPlaces); + return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_NetworkPlaces, REGITEMORDER_NETHOOD); } LPITEMIDLIST _ILCreateBitBucket(void) { TRACE("()\n"); - return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_RecycleBin); + return _ILCreateGuid(PT_DESKTOP_REGITEM, &CLSID_RecycleBin, REGITEMORDER_RECYCLEBIN); } LPITEMIDLIST _ILCreateAdminTools(void) { TRACE("()\n"); - return _ILCreateGuid(PT_GUID, &CLSID_AdminFolderShortcut); //FIXME + return _ILCreateGuid(PT_GUID, &CLSID_AdminFolderShortcut, REGITEMORDER_DEFAULT); //FIXME } -LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) +LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid, BYTE SortOrder) { LPITEMIDLIST pidlOut; @@ -1886,7 +1886,7 @@ LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) if (pidlOut) { LPPIDLDATA pData = _ILGetDataPointer(pidlOut); - + pData->u.guid.uSortOrder = SortOrder; pData->u.guid.guid = *guid; TRACE("-- create GUID-pidl %s\n", debugstr_guid(&(pData->u.guid.guid))); diff --git a/dll/win32/shell32/wine/pidl.h b/dll/win32/shell32/wine/pidl.h index 0711f3f5a17..87f477ba5d8 100644 --- a/dll/win32/shell32/wine/pidl.h +++ b/dll/win32/shell32/wine/pidl.h @@ -119,7 +119,27 @@ extern "C" { #define PT_INTERNET_URL 0x61 #define PT_CONTROLS_OLDREGITEM 0x70 #define PT_CONTROLS_NEWREGITEM 0x71 + +#define REGITEMLOCATION_CONTROLS CSIDL_DRIVES +#define REGITEMLOCATION_PRINTERS CSIDL_CONTROLS + +#define REGITEMORDER_DEFAULT 0x80 +#define REGITEMORDER_LIBRARIES 0x42 +#define REGITEMORDER_USERSFILEFOLDER 0x44 +#define REGITEMORDER_MYCOMPUTER 0x50 +#define REGITEMORDER_MYDOCS_BEFOREMYCOMPUTER 0x48 // Tweak UI "Desktop => First Icon" only +#define REGITEMORDER_MYDOCS_AFTERMYCOMPUTER 0x54 // accepts these two values. +#define REGITEMORDER_MYDOCS_DEFAULT 0x48 +#define REGITEMORDER_NETHOOD 0x58 +#if (REGITEMLOCATION_CONTROLS == CSIDL_DESKTOP) +#define REGITEMORDER_RECYCLEBIN 0x78 +#else +#define REGITEMORDER_RECYCLEBIN 0x60 #endif +#define REGITEMORDER_INTERNET 0x68 +#define REGITEMORDER_DESKTOP_CONTROLS 0x70 // NT6 +#define REGITEMORDER_MYCOMPUTER_CONTROLS 0x1E // NT5 +#endif // __REACTOS__ static inline BYTE _ILGetType(LPCITEMIDLIST pidl) { @@ -165,7 +185,7 @@ typedef struct tagPIDLPrinterStruct typedef struct tagGUIDStruct { - BYTE dummy; /* offset 01 is unknown */ + BYTE uSortOrder; GUID guid; /* offset 02 */ } GUIDStruct; @@ -269,7 +289,7 @@ UINT _ILGetDepth(LPCITEMIDLIST pidl); /* Creates a PIDL with guid format and type type, which must be one of PT_GUID, * PT_SHELLEXT, or PT_YAGUID. */ -LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid) DECLSPEC_HIDDEN; +LPITEMIDLIST _ILCreateGuid(PIDLTYPE type, REFIID guid, BYTE SortOrder); #ifndef __REACTOS__ /* Like _ILCreateGuid, but using the string szGUID. */