[SHELL32] Handle sort order in RegItems (#7906)

* [SHELL32] Handle sort order in RegItems

This makes the TweakUI "First icon on desktop" setting work correctly.
This commit is contained in:
Whindmar Saksit
2025-04-30 13:31:58 +02:00
committed by GitHub
parent b1cf981c52
commit ba6760683b
5 changed files with 96 additions and 44 deletions

View File

@@ -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 =
{

View File

@@ -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 =
{

View File

@@ -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<ITEMIDLIST> 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);

View File

@@ -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)));

View File

@@ -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. */