[SHELL32] Allow using custom desktop/folders/drives icons (#6421)

Implement proper reading the current user's icons from registry.

CORE-14758

- Load the icons specified by user in registry in the following keys:
  "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CLSID\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\DefaultIcon"
  (virtual namespace folders)
  "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons"
  (filesystem folders and drives)
- Implement two functions HCU/HLM_GetIconW for reading the icons
  from mentioned HKCU and HKLM keys accordingly.
- Use HCR_RegGetIconW for falling back to default icons.
  This function always loads only default icons, even when
  the custom ones are specified by user.
- Refactor SIC_LoadOverlayIcon to use newly implemented HLM_GetIconW.

These changes apply to:
- virtual namespace folders and other desktop items (like My Computer,
  My Documents, Network Places, Recycle Bin, Web Browser (aka Internet
  Explorer), Control Panel and some of its items);
- normal filesystem directories;
- all types of drives (fixed disk drives, removable drives, CD-ROMs,
  RamDisks and network drives). Handle invalid drives, setting blank
  icon for them, since they cannot be recognized or mounted correctly.
  Also, load the autorun icons first to avoid overriding them by the
  icons defined in registry.

I've rechecked twice: excluding Start Menu icons, Desktop Workspace icon
and some FS folder icons that have their own desktop.ini configuration
files (we probably should write the custom icons we load to these configs,
as Windows does it, perhaps with WritePrivateProfileStringW), all other
icons can be changed now (only ones that can be changed on XP SP3 / 2003
SP2) via built-in system tools (like Desktop icons in desk.cpl) or any
3rd-party tools without modifying system resources.

Also all icons for the known file types can be changed,
same as before my changes.

Regarding Start Menu icons:
- changing them should be correctly implemented in explorer
  instead of shell32, as the former is responsible for the Start Menu
  and partially for the taskbar;
- in order to actually use all of them, we need to implement modern
  Start Menu first.

Useful reference: http://www.winfaq.de/faq_html/Content/tip0000/onlinefaq.php?h=tip0162.htm
This commit is contained in:
Oleg Dubinskiy 2024-02-28 22:20:47 +01:00 committed by GitHub
parent 45321706dc
commit f9a5344254
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 154 additions and 50 deletions

View file

@ -511,15 +511,48 @@ HRESULT CDrivesExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl
DriveType = DRIVE_FIXED; DriveType = DRIVE_FIXED;
WCHAR wTemp[MAX_PATH]; WCHAR wTemp[MAX_PATH];
int icon_idx; int icon_idx, reg_idx;
UINT flags = 0; UINT flags = 0;
if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
(HCR_GetIconW(L"Drive", wTemp, NULL, MAX_PATH, &icon_idx))) switch (DriveType)
{
case DRIVE_FIXED:
case DRIVE_UNKNOWN:
reg_idx = IDI_SHELL_DRIVE;
break;
case DRIVE_CDROM:
reg_idx = IDI_SHELL_CDROM;
break;
case DRIVE_REMOTE:
reg_idx = IDI_SHELL_NETDRIVE;
break;
case DRIVE_REMOVABLE:
if (!IsDriveFloppyA(pszDrive))
reg_idx = IDI_SHELL_REMOVEABLE;
else
reg_idx = IDI_SHELL_3_14_FLOPPY;
break;
case DRIVE_RAMDISK:
reg_idx = IDI_SHELL_RAMDISK;
break;
case DRIVE_NO_ROOT_DIR:
default:
reg_idx = IDI_SHELL_DOCUMENT;
break;
}
hr = getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp),
&icon_idx, &flags);
if (SUCCEEDED(hr))
{ {
initIcon->SetNormalIcon(wTemp, icon_idx); initIcon->SetNormalIcon(wTemp, icon_idx);
} }
else if (SUCCEEDED(getIconLocationForDrive(psf, pidl, 0, wTemp, _countof(wTemp), else if (HLM_GetIconW(reg_idx - 1, wTemp, _countof(wTemp), &icon_idx))
&icon_idx, &flags))) {
initIcon->SetNormalIcon(wTemp, icon_idx);
}
else if ((DriveType == DRIVE_FIXED || DriveType == DRIVE_UNKNOWN) &&
(HCR_GetIconW(L"Drive", wTemp, NULL, _countof(wTemp), &icon_idx)))
{ {
initIcon->SetNormalIcon(wTemp, icon_idx); initIcon->SetNormalIcon(wTemp, icon_idx);
} }

View file

@ -135,12 +135,15 @@ HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pcl
static HRESULT static HRESULT
getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags) getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags)
{
if (!HLM_GetIconW(IDI_SHELL_FOLDER - 1, szIconFile, cchMax, piIndex))
{ {
if (!HCR_GetIconW(L"Folder", szIconFile, NULL, cchMax, piIndex)) if (!HCR_GetIconW(L"Folder", szIconFile, NULL, cchMax, piIndex))
{ {
lstrcpynW(szIconFile, swShell32Name, cchMax); StringCchCopyW(szIconFile, cchMax, swShell32Name);
*piIndex = -IDI_SHELL_FOLDER; *piIndex = -IDI_SHELL_FOLDER;
} }
}
if (uFlags & GIL_OPENICON) if (uFlags & GIL_OPENICON)
{ {

View file

@ -123,6 +123,16 @@ HRESULT CGuidItemContextMenu_CreateInstance(PCIDLIST_ABSOLUTE pidlFolder,
return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, RegFolderContextMenuCallback, cKeys, hKeys, ppcm); return CDefFolderMenu_Create2(pidlFolder, hwnd, cidl, apidl, psf, RegFolderContextMenuCallback, cKeys, hKeys, ppcm);
} }
HRESULT FormatGUIDKey(LPWSTR KeyName, SIZE_T KeySize, LPCWSTR RegPath, const GUID* riid)
{
WCHAR xriid[40];
if (!StringFromGUID2(*riid, xriid, _countof(xriid) - 1))
return E_FAIL;
return StringCchPrintfW(KeyName, KeySize, RegPath, xriid);
}
HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut) HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
{ {
CComPtr<IDefaultExtractIconInit> initIcon; CComPtr<IDefaultExtractIconInit> initIcon;
@ -145,14 +155,7 @@ HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVO
if (!riid) if (!riid)
return E_FAIL; return E_FAIL;
/* my computer and other shell extensions */ /* Choose a correct icon for Recycle Bin (full or empty) */
WCHAR xriid[50];
swprintf(xriid, L"CLSID\\{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
riid->Data1, riid->Data2, riid->Data3,
riid->Data4[0], riid->Data4[1], riid->Data4[2], riid->Data4[3],
riid->Data4[4], riid->Data4[5], riid->Data4[6], riid->Data4[7]);
const WCHAR* iconname = NULL; const WCHAR* iconname = NULL;
if (_ILIsBitBucket(pidl)) if (_ILIsBitBucket(pidl))
{ {
@ -179,23 +182,37 @@ HRESULT CGuidItemExtractIcon_CreateInstance(LPCITEMIDLIST pidl, REFIID iid, LPVO
} }
} }
if (HCR_GetIconW(xriid, wTemp, iconname, MAX_PATH, &icon_idx)) /* Prepare registry path for loading icons of My Computer and other shell extensions */
WCHAR KeyName[MAX_PATH];
hr = FormatGUIDKey(KeyName, _countof(KeyName),
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s",
riid);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
/* Load icon for the current user */
BOOL ret = HCU_GetIconW(KeyName, wTemp, iconname, _countof(wTemp), &icon_idx);
if (!ret)
{ {
/* Failed, load default system-wide icon */
hr = FormatGUIDKey(KeyName, _countof(KeyName), L"CLSID\\%s", riid);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
ret = HCR_GetIconW(KeyName, wTemp, iconname, _countof(wTemp), &icon_idx);
}
if (ret)
{
/* Success, set loaded icon */
initIcon->SetNormalIcon(wTemp, icon_idx); initIcon->SetNormalIcon(wTemp, icon_idx);
} }
else else
{ {
// FIXME: Delete these hacks and make HCR_GetIconW and registry working /* Everything has failed, set blank paper icon */
if (IsEqualGUID(*riid, CLSID_MyComputer)) WARN("Failed to load an icon for the item, setting blank icon\n");
initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_COMPUTER); initIcon->SetNormalIcon(swShell32Name, IDI_SHELL_DOCUMENT - 1);
else if (IsEqualGUID(*riid, CLSID_MyDocuments))
initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_DOCUMENTS);
else if (IsEqualGUID(*riid, CLSID_NetworkPlaces))
initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_MY_NETWORK_PLACES);
else if (IsEqualGUID(*riid, CLSID_Internet))
initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_WEB_BROWSER);
else
initIcon->SetNormalIcon(swShell32Name, -IDI_SHELL_FOLDER);
} }
return initIcon->QueryInterface(iid, ppvOut); return initIcon->QueryInterface(iid, ppvOut);

View file

@ -654,34 +654,20 @@ void SIC_Destroy(void)
*/ */
static int SIC_LoadOverlayIcon(int icon_idx) static int SIC_LoadOverlayIcon(int icon_idx)
{ {
WCHAR buffer[1024], wszIdx[8]; WCHAR buffer[1024];
HKEY hKeyShellIcons; LPWSTR iconPath;
LPCWSTR iconPath;
int iconIdx; int iconIdx;
iconPath = swShell32Name; /* default: load icon from shell32.dll */ iconPath = swShell32Name; /* default: load icon from shell32.dll */
iconIdx = icon_idx; iconIdx = icon_idx;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons", if (HLM_GetIconW(icon_idx, buffer, _countof(buffer), &iconIdx))
0, KEY_READ, &hKeyShellIcons) == ERROR_SUCCESS)
{ {
DWORD count = sizeof(buffer);
swprintf(wszIdx, L"%d", icon_idx);
/* read icon path and index */
if (RegQueryValueExW(hKeyShellIcons, wszIdx, NULL, NULL, (LPBYTE)buffer, &count) == ERROR_SUCCESS)
{
LPWSTR p = wcschr(buffer, ',');
if (p)
*p++ = 0;
iconPath = buffer; iconPath = buffer;
iconIdx = _wtoi(p);
} }
else
RegCloseKey(hKeyShellIcons); {
WARN("Failed to load icon with index %d, using default one\n", icon_idx);
} }
if (!sic_hdpa) if (!sic_hdpa)

View file

@ -34,6 +34,9 @@
#include <shlwapi.h> #include <shlwapi.h>
#include <wine/debug.h> #include <wine/debug.h>
#include <wine/unicode.h> #include <wine/unicode.h>
#ifdef __REACTOS__
#include <strsafe.h>
#endif
#include "pidl.h" #include "pidl.h"
#include "shell32_main.h" #include "shell32_main.h"
@ -334,6 +337,60 @@ BOOL HCR_GetIconA(LPCSTR szClass, LPSTR szDest, LPCSTR szName, DWORD len, int* p
return ret; return ret;
} }
#ifdef __REACTOS__
BOOL HCU_GetIconW(LPCWSTR szClass, LPWSTR szDest, LPCWSTR szName, DWORD len, int* picon_idx)
{
HKEY hkey;
WCHAR sTemp[MAX_PATH];
BOOL ret = FALSE;
TRACE("%s\n", debugstr_w(szClass));
StringCchPrintfW(sTemp, _countof(sTemp), L"%s\\DefaultIcon", szClass);
if (!RegOpenKeyExW(HKEY_CURRENT_USER, sTemp, 0, KEY_READ, &hkey))
{
ret = HCR_RegGetIconW(hkey, szDest, szName, len, picon_idx);
RegCloseKey(hkey);
}
if (ret)
TRACE("-- %s %i\n", debugstr_w(szDest), *picon_idx);
else
TRACE("-- not found\n");
return ret;
}
BOOL HLM_GetIconW(int reg_idx, LPWSTR szDest, DWORD len, int* picon_idx)
{
HKEY hkey;
WCHAR sTemp[5];
BOOL ret = FALSE;
TRACE("%d\n", reg_idx);
StringCchPrintfW(sTemp, _countof(sTemp), L"%d", reg_idx);
if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons",
0,
KEY_READ,
&hkey))
{
ret = HCR_RegGetIconW(hkey, szDest, sTemp, len, picon_idx);
RegCloseKey(hkey);
}
if (ret)
TRACE("-- %s %i\n", debugstr_w(szDest), *picon_idx);
else
TRACE("-- not found\n");
return ret;
}
#endif
/*************************************************************************************** /***************************************************************************************
* HCR_GetClassName [internal] * HCR_GetClassName [internal]
* *

View file

@ -50,6 +50,14 @@ BOOL HCR_GetExecuteCommandW( HKEY hkeyClass, LPCWSTR szClass, LPCWSTR szVerb, LP
BOOL HCR_GetIconW(LPCWSTR szClass, LPWSTR szDest, LPCWSTR szName, DWORD len, int* picon_idx); BOOL HCR_GetIconW(LPCWSTR szClass, LPWSTR szDest, LPCWSTR szName, DWORD len, int* picon_idx);
BOOL HCR_GetClassNameW(REFIID riid, LPWSTR szDest, DWORD len) DECLSPEC_HIDDEN; BOOL HCR_GetClassNameW(REFIID riid, LPWSTR szDest, DWORD len) DECLSPEC_HIDDEN;
#ifdef __REACTOS__
/* Current User */
BOOL HCU_GetIconW(LPCWSTR szClass, LPWSTR szDest, LPCWSTR szName, DWORD len, int* picon_idx) DECLSPEC_HIDDEN;
/* Local Machine */
BOOL HLM_GetIconW(int reg_idx, LPWSTR szDest, DWORD len, int* picon_idx) DECLSPEC_HIDDEN;
#endif
/* ANSI versions of above functions, supposed to go away as soon as they are not used anymore */ /* ANSI versions of above functions, supposed to go away as soon as they are not used anymore */
BOOL HCR_MapTypeToValueA(LPCSTR szExtension, LPSTR szFileType, LONG len, BOOL bPrependDot) DECLSPEC_HIDDEN; BOOL HCR_MapTypeToValueA(LPCSTR szExtension, LPSTR szFileType, LONG len, BOOL bPrependDot) DECLSPEC_HIDDEN;
BOOL HCR_GetIconA(LPCSTR szClass, LPSTR szDest, LPCSTR sName, DWORD len, int* picon_idx); BOOL HCR_GetIconA(LPCSTR szClass, LPSTR szDest, LPCSTR sName, DWORD len, int* picon_idx);