[SHELL32] Fix FS folder assoc array class order (#6047)

Fixes the reg class key order for FS items. The existing code was close,
but for some reason used `//` as the path separator for SystemFileAssociations!

- Fixed SystemFileAssociations.

- Swapped the order of `*` and `AllFilesystemObjects`. This is the documented
  order and can also be observed in Process Monitor.
  https://learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray#about-association-arrays

- Removed `(..., L"%s//%s", extension, wszClass)`, this does not seem to be
  a valid thing (`.TestAAExtWeird` in my tests).

- Adds the `Unknown` class when appropriate. Not adding the `openas` verb
  to `Unknown` rgs registration now to mimic Windows, because ROS
  `CDefaultContextMenu` lacks verb de-duplication and the menu would end up
  with two "Open With" entries. This just uses `(cidl == 1)` to simulate
  Windows, while Windows on NT6 uses `MultiSelectModel=Single`, a NT6 feature
  not implemented in ROS.

- The class order for folders was wrong and is still "wrong" in this PR,
  but I chose to use the Windows menu display order until the exact mechanics
  required in `CDefaultContextMenu` can be understood.

- Extracts the extension from ANSI PIDLs.
This commit is contained in:
Whindmar Saksit 2024-03-24 21:37:59 +01:00 committed by GitHub
parent f6cf6954eb
commit a83e40f6d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 50 additions and 26 deletions

View file

@ -624,7 +624,7 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
UINT cKeys = 0; UINT cKeys = 0;
if (cidl > 0) if (cidl > 0)
{ {
AddFSClassKeysToArray(apidl[0], hKeys, &cKeys); AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
} }
DEFCONTEXTMENU dcm; DEFCONTEXTMENU dcm;

View file

@ -1207,7 +1207,7 @@ HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
{ {
HKEY hKeys[16]; HKEY hKeys[16];
UINT cKeys = 0; UINT cKeys = 0;
AddFSClassKeysToArray(apidl[0], hKeys, &cKeys); AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
DEFCONTEXTMENU dcm; DEFCONTEXTMENU dcm;
dcm.hwnd = hwndOwner; dcm.hwnd = hwndOwner;

View file

@ -64,7 +64,7 @@ HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* pp
extern "C" extern "C"
BOOL HCR_RegOpenClassIDKey(REFIID riid, HKEY *hkey); BOOL HCR_RegOpenClassIDKey(REFIID riid, HKEY *hkey);
void AddFSClassKeysToArray(PCUITEMID_CHILD pidl, HKEY* array, UINT* cKeys); void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys);
HRESULT CDefViewBckgrndMenu_CreateInstance(IShellFolder* psf, REFIID riid, void **ppv); HRESULT CDefViewBckgrndMenu_CreateInstance(IShellFolder* psf, REFIID riid, void **ppv);
@ -94,7 +94,7 @@ static __inline int SHELL32_GUIDToStringW (REFGUID guid, LPWSTR str)
void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags); void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags);
BOOL SHELL_FS_HideExtension(LPCWSTR pwszPath); BOOL SHELL_FS_HideExtension(LPCWSTR pwszPath);
void AddClassKeyToArray(const WCHAR * szClass, HKEY* array, UINT* cKeys); LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -265,59 +265,83 @@ HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST
return MAKE_COMPARE_HRESULT(ret); return MAKE_COMPARE_HRESULT(ret);
} }
void AddClassKeyToArray(const WCHAR * szClass, HKEY* array, UINT* cKeys) LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys)
{ {
if (*cKeys >= 16) if (*cKeys >= 16)
return; return ERROR_MORE_DATA;
HKEY hkey; HKEY hkey;
LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey); LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
if (result != ERROR_SUCCESS) if (result == ERROR_SUCCESS)
return; {
array[*cKeys] = hkey;
array[*cKeys] = hkey; *cKeys += 1;
*cKeys += 1; }
return result;
} }
void AddFSClassKeysToArray(PCUITEMID_CHILD pidl, HKEY* array, UINT* cKeys) void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys)
{ {
// This function opens the association array keys in canonical order for filesystem items.
// The order is documented: learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray
ASSERT(cidl >= 1 && apidl);
PCUITEMID_CHILD pidl = apidl[0];
if (_ILIsValue(pidl)) if (_ILIsValue(pidl))
{ {
WCHAR buf[MAX_PATH];
PWSTR name;
FileStructW* pFileData = _ILGetFileStructW(pidl); FileStructW* pFileData = _ILGetFileStructW(pidl);
LPWSTR extension = PathFindExtension(pFileData->wszName); if (pFileData)
{
name = pFileData->wszName;
}
else
{
_ILSimpleGetTextW(pidl, buf, _countof(buf));
name = buf;
}
LPCWSTR extension = PathFindExtension(name);
if (extension) if (extension)
{ {
AddClassKeyToArray(extension, array, cKeys); WCHAR wszClass[MAX_PATH], wszSFA[23 + _countof(wszClass)];
WCHAR wszClass[MAX_PATH], wszClass2[MAX_PATH];
DWORD dwSize = sizeof(wszClass); DWORD dwSize = sizeof(wszClass);
if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS) if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS ||
!*wszClass || AddClassKeyToArray(wszClass, array, cKeys) != ERROR_SUCCESS)
{ {
swprintf(wszClass2, L"%s//%s", extension, wszClass); // Only add the extension key if the ProgId is not valid
AddClassKeyToArray(extension, array, cKeys);
AddClassKeyToArray(wszClass, array, cKeys); // "Open With" becomes the default when there are no verbs in the above keys
AddClassKeyToArray(wszClass2, array, cKeys); if (cidl == 1)
AddClassKeyToArray(L"Unknown", array, cKeys);
} }
swprintf(wszClass2, L"SystemFileAssociations//%s", extension); swprintf(wszSFA, L"SystemFileAssociations\\%s", extension);
AddClassKeyToArray(wszClass2, array, cKeys); AddClassKeyToArray(wszSFA, array, cKeys);
dwSize = sizeof(wszClass);
if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS) if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
{ {
swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass); swprintf(wszSFA, L"SystemFileAssociations\\%s", wszClass);
AddClassKeyToArray(wszClass2, array, cKeys); AddClassKeyToArray(wszSFA, array, cKeys);
} }
} }
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
AddClassKeyToArray(L"*", array, cKeys); AddClassKeyToArray(L"*", array, cKeys);
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
} }
else if (_ILIsFolder(pidl)) else if (_ILIsFolder(pidl))
{ {
// FIXME: Directory > Folder > AFO is the correct order and it's
// the order Windows reports in its undocumented association array
// but it is somehow not the order Windows adds the items to its menu!
// Until the correct algorithm in CDefaultContextMenu can be determined,
// we add the folder keys in "menu order".
AddClassKeyToArray(L"Folder", array, cKeys);
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
AddClassKeyToArray(L"Directory", array, cKeys); AddClassKeyToArray(L"Directory", array, cKeys);
AddClassKeyToArray(L"Folder", array, cKeys);
} }
else else
{ {