[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;
if (cidl > 0)
{
AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
}
DEFCONTEXTMENU dcm;

View file

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

View file

@ -64,7 +64,7 @@ HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* pp
extern "C"
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);
@ -94,7 +94,7 @@ static __inline int SHELL32_GUIDToStringW (REFGUID guid, LPWSTR str)
void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags);
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

View file

@ -265,59 +265,83 @@ HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST
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)
return;
return ERROR_MORE_DATA;
HKEY hkey;
LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
if (result != ERROR_SUCCESS)
return;
array[*cKeys] = hkey;
*cKeys += 1;
if (result == ERROR_SUCCESS)
{
array[*cKeys] = hkey;
*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))
{
WCHAR buf[MAX_PATH];
PWSTR name;
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)
{
AddClassKeyToArray(extension, array, cKeys);
WCHAR wszClass[MAX_PATH], wszClass2[MAX_PATH];
WCHAR wszClass[MAX_PATH], wszSFA[23 + _countof(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);
AddClassKeyToArray(wszClass2, array, cKeys);
// "Open With" becomes the default when there are no verbs in the above keys
if (cidl == 1)
AddClassKeyToArray(L"Unknown", array, cKeys);
}
swprintf(wszClass2, L"SystemFileAssociations//%s", extension);
AddClassKeyToArray(wszClass2, array, cKeys);
swprintf(wszSFA, L"SystemFileAssociations\\%s", extension);
AddClassKeyToArray(wszSFA, array, cKeys);
dwSize = sizeof(wszClass);
if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
{
swprintf(wszClass2, L"SystemFileAssociations//%s", wszClass);
AddClassKeyToArray(wszClass2, array, cKeys);
swprintf(wszSFA, L"SystemFileAssociations\\%s", wszClass);
AddClassKeyToArray(wszSFA, array, cKeys);
}
}
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
AddClassKeyToArray(L"*", array, cKeys);
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
}
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"Directory", array, cKeys);
AddClassKeyToArray(L"Folder", array, cKeys);
}
else
{