https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8107ff86368259fd53582…
commit 8107ff86368259fd5358287d8e39c5e7180946e1
Author:     Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Sun Nov 17 20:13:05 2024 +0100
Commit:     GitHub <noreply(a)github.com>
CommitDate: Sun Nov 17 20:13:05 2024 +0100
    [SHELL32][SHELL32_APITEST] Consolidate file type description handling (#7485)
    - Fixes the case where an extension exists in HKCR but has no ProgId (half of
CORE-19355)
    - Fixes some cases where Wine hardcoded "File and "... file" strings
are used instead of localized strings in SHGetFileInfo.
---
 dll/win32/shell32/CQueryAssociations.cpp         |  50 ++++++++
 dll/win32/shell32/dialogs/filetypes.cpp          |  13 --
 dll/win32/shell32/folders/CFSFolder.cpp          |  35 +++++-
 dll/win32/shell32/wine/classes.c                 |  51 +++++---
 dll/win32/shell32/wine/pidl.c                    |  61 ++-------
 dll/win32/shell32/wine/pidl.h                    |   2 +-
 dll/win32/shell32/wine/shell32_main.c            | 150 +++++++++++------------
 dll/win32/shell32/wine/shell32_main.h            |   7 ++
 modules/rostests/apitests/shell32/CMakeLists.txt |   1 +
 modules/rostests/apitests/shell32/ItemIDList.cpp |   6 +
 modules/rostests/apitests/shell32/ShellInfo.cpp  |  76 ++++++++++++
 modules/rostests/apitests/shell32/testlist.c     |   2 +
 12 files changed, 288 insertions(+), 166 deletions(-)
diff --git a/dll/win32/shell32/CQueryAssociations.cpp
b/dll/win32/shell32/CQueryAssociations.cpp
index a21e49625f8..14a618b9611 100644
--- a/dll/win32/shell32/CQueryAssociations.cpp
+++ b/dll/win32/shell32/CQueryAssociations.cpp
@@ -22,6 +22,56 @@
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
+EXTERN_C HRESULT SHELL32_AssocGetFSDirectoryDescription(PWSTR Buf, UINT cchBuf)
+{
+    static WCHAR cache[33] = {};
+    if (!*cache)
+        LoadStringW(shell32_hInstance, IDS_DIRECTORY, cache, _countof(cache));
+    return StringCchCopyW(Buf, cchBuf, cache);
+}
+
+static HRESULT GetExtensionDefaultDescription(PCWSTR DotExt, PWSTR Buf, UINT cchBuf)
+{
+    static WCHAR fmt[33] = {};
+    if (!*fmt)
+        LoadStringW(shell32_hInstance, IDS_ANY_FILE, fmt, _countof(fmt));
+    return StringCchPrintfW(Buf, cchBuf, fmt, DotExt);
+}
+
+static HRESULT SHELL32_AssocGetExtensionDescription(PCWSTR DotExt, PWSTR Buf, UINT
cchBuf)
+{
+    HRESULT hr;
+    if (!DotExt[0] || (!DotExt[1] && DotExt[0] == '.'))
+    {
+        if (SUCCEEDED(hr = GetExtensionDefaultDescription(L"", Buf, cchBuf)))
+            StrTrimW(Buf, L" -"); // Remove the empty %s so we are left with
"File"
+        return hr;
+    }
+    HKEY hKey;
+    if (SUCCEEDED(hr = HCR_GetProgIdKeyOfExtension(DotExt, &hKey, TRUE)))
+    {
+        DWORD err = RegLoadMUIStringW(hKey, L"FriendlyTypeName", Buf, cchBuf,
NULL, 0, NULL);
+        if (err && hr == S_OK) // ProgId default value fallback (but not if we
only have a .ext key)
+        {
+            DWORD cb = cchBuf * sizeof(*Buf);
+            err = RegGetValueW(hKey, NULL, NULL, RRF_RT_REG_SZ, NULL, Buf, &cb);
+        }
+        RegCloseKey(hKey);
+        if (!err)
+            return err;
+    }
+    // No information in the registry, default to "UPPERCASEEXT File"
+    WCHAR ext[MAX_PATH + 33];
+    if (LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, ++DotExt, -1, ext,
_countof(ext)))
+        DotExt = ext;
+    return GetExtensionDefaultDescription(DotExt, Buf, cchBuf);
+}
+
+EXTERN_C HRESULT SHELL32_AssocGetFileDescription(PCWSTR Name, PWSTR Buf, UINT cchBuf)
+{
+    return SHELL32_AssocGetExtensionDescription(PathFindExtensionW(Name), Buf, cchBuf);
+}
+
 /**************************************************************************
  *  IQueryAssociations
  *
diff --git a/dll/win32/shell32/dialogs/filetypes.cpp
b/dll/win32/shell32/dialogs/filetypes.cpp
index c10c62573eb..a2206ad9fe9 100644
--- a/dll/win32/shell32/dialogs/filetypes.cpp
+++ b/dll/win32/shell32/dialogs/filetypes.cpp
@@ -89,7 +89,6 @@ typedef struct _FILE_TYPE_GLOBALS
     HICON hDefExtIconSmall;
     HBITMAP hOpenWithImage;
     HANDLE hHeap;
-    WCHAR DefExtTypeNameFmt[TYPENAME_CCHMAX];
     WCHAR NoneString[42];
     INT8 SortCol, SortReverse;
     UINT Restricted;
@@ -298,12 +297,6 @@ GetTypeName(PFILE_TYPE_ENTRY Entry, PFILE_TYPE_GLOBALS pG)
             {
                 StringCchCopyW(Entry->FileDescription,
_countof(Entry->FileDescription), fi.szTypeName);
             }
-            else
-            {
-                // FIXME: Remove this hack when SHGetFileInfo is able to handle
extensions without a ProgId (.ASM etc)
-                StringCchPrintfW(Entry->FileDescription,
_countof(Entry->FileDescription),
-                                 pG->DefExtTypeNameFmt,
&Entry->FileExtension[1]);
-            }
         }
         else
         {
@@ -1773,12 +1766,6 @@ FileTypesDlg_Initialize(HWND hwndDlg)
     pG->himlSmall = ImageList_Create(pG->IconSize, pG->IconSize, ILC_COLOR32 |
ILC_MASK, 256, 20);
     pG->hHeap = GetProcessHeap();
-    if (!LoadStringW(shell32_hInstance, IDS_ANY_FILE,
-                     pG->DefExtTypeNameFmt, _countof(pG->DefExtTypeNameFmt)))
-    {
-        LPCWSTR fallback = L"%s File"; // Default to English
-        StringCchCopyW(pG->DefExtTypeNameFmt, _countof(pG->DefExtTypeNameFmt),
fallback);
-    }
     pG->NoneString[0] = UNICODE_NULL;
     LoadStringW(shell32_hInstance, IDS_NONE, pG->NoneString,
_countof(pG->NoneString));
diff --git a/dll/win32/shell32/folders/CFSFolder.cpp
b/dll/win32/shell32/folders/CFSFolder.cpp
index 8ff94898d81..bb73ec85407 100644
--- a/dll/win32/shell32/folders/CFSFolder.cpp
+++ b/dll/win32/shell32/folders/CFSFolder.cpp
@@ -14,6 +14,13 @@ WINE_DEFAULT_DEBUG_CHANNEL (shell);
 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID*
pclsidFolder);
+static BOOL ItemIsFolder(PCUITEMID_CHILD pidl)
+{
+    const BYTE mask = PT_FS | PT_FS_FOLDER_FLAG | PT_FS_FILE_FLAG;
+    const BYTE type = _ILGetType(pidl);
+    return (type & mask) == (PT_FS | PT_FS_FOLDER_FLAG) || (type == PT_FS &&
ILGetNext(pidl));
+}
+
 static LPCWSTR GetItemFileName(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
 {
     FileStructW* pDataW = _ILGetFileStructW(pidl);
@@ -34,6 +41,23 @@ static BOOL IsRealItem(const ITEMIDLIST &idl)
     return fsitem.dwFileSize | fsitem.uFileDate;
 }
+static void GetItemDescription(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
+{
+    HRESULT hr = E_FAIL;
+    if (ItemIsFolder(pidl))
+    {
+        hr = SHELL32_AssocGetFSDirectoryDescription(Buf, cchMax);
+    }
+    else
+    {
+        WCHAR temp[MAX_PATH];
+        LPCWSTR name = GetItemFileName(pidl, temp, _countof(temp));
+        hr = SHELL32_AssocGetFileDescription(name ? name : L"", Buf, cchMax);
+    }
+    if (FAILED(hr) && cchMax)
+        Buf[0] = UNICODE_NULL;
+}
+
 static HKEY OpenKeyFromFileType(LPCWSTR pExtension, LPCWSTR KeyName)
 {
     HKEY hkey;
@@ -1640,18 +1664,23 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
     {
         hr = S_OK;
         psd->str.uType = STRRET_WSTR;
-        psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
+        if (iColumn != SHFSF_COL_NAME)
+        {
+            psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
+            if (!psd->str.pOleStr)
+                return E_OUTOFMEMORY;
+        }
         /* the data from the pidl */
         switch (iColumn)
         {
             case SHFSF_COL_NAME:
-                hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER,
&psd->str);
+                hr = GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER,
&psd->str);
                 break;
             case SHFSF_COL_SIZE:
                 _ILGetFileSize(pidl, psd->str.pOleStr, MAX_PATH);
                 break;
             case SHFSF_COL_TYPE:
-                _ILGetFileType(pidl, psd->str.pOleStr, MAX_PATH);
+                GetItemDescription(pidl, psd->str.pOleStr, MAX_PATH);
                 break;
             case SHFSF_COL_MDATE:
                 _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH);
diff --git a/dll/win32/shell32/wine/classes.c b/dll/win32/shell32/wine/classes.c
index 12571e14c58..6138fdac267 100644
--- a/dll/win32/shell32/wine/classes.c
+++ b/dll/win32/shell32/wine/classes.c
@@ -44,7 +44,40 @@
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
-#define MAX_EXTENSION_LENGTH 20
+#define MAX_EXTENSION_LENGTH 20 // FIXME: The limit is 254?
+
+static LONG GetRegString(HKEY hKey, PCWSTR SubKey, PCWSTR Name, PWSTR Buffer, UINT
cchBuf)
+{
+    DWORD cb = sizeof(*Buffer) * cchBuf;
+    return RegGetValueW(hKey, SubKey, Name, RRF_RT_REG_SZ, NULL, Buffer, &cb);
+}
+
+HRESULT HCR_GetProgIdKeyOfExtension(PCWSTR szExtension, PHKEY phKey, BOOL AllowFallback)
+{
+    LONG err;
+    WCHAR ext[max(1 + MAX_EXTENSION_LENGTH + 1, MAX_PATH)];
+    WCHAR progid[MAX_PATH];
+    if (szExtension[0] != '.')
+    {
+        ext[0] = '.';
+        lstrcpynW(ext + 1, szExtension, _countof(ext) - 1);
+        szExtension = ext;
+    }
+    err = GetRegString(HKEY_CLASSES_ROOT, szExtension, NULL, progid, _countof(progid));
+    if (!err && progid[0] != UNICODE_NULL)
+    {
+        err = RegOpenKeyExW(HKEY_CLASSES_ROOT, progid, 0, KEY_READ, phKey);
+        if (!err)
+            return err; /* A real ProgId key, return S_OK */
+    }
+    if (AllowFallback)
+    {
+        err = RegOpenKeyExW(HKEY_CLASSES_ROOT, szExtension, 0, KEY_READ, phKey);
+        if (!err)
+            return S_FALSE;
+    }
+    return HRESULT_FROM_WIN32(err);
+}
 BOOL HCR_MapTypeToValueW(LPCWSTR szExtension, LPWSTR szFileType, LONG len, BOOL
bPrependDot)
 {
@@ -67,14 +100,6 @@ BOOL HCR_MapTypeToValueW(LPCWSTR szExtension, LPWSTR szFileType, LONG
len, BOOL
          return FALSE;
        }
-#ifdef __REACTOS__
-        if (!RegLoadMUIStringW(hkey, L"FriendlyTypeName", szFileType, len,
NULL, 0, NULL))
-        {
-            RegCloseKey(hkey);
-            return TRUE;
-        }
-#endif
-
        if (RegQueryValueW(hkey, NULL, szFileType, &len))
        {
          RegCloseKey(hkey);
@@ -109,14 +134,6 @@ BOOL HCR_MapTypeToValueA(LPCSTR szExtension, LPSTR szFileType, LONG
len, BOOL bP
          return FALSE;
        }
-#ifdef __REACTOS__
-        if (!RegLoadMUIStringA(hkey, "FriendlyTypeName", szFileType, len, NULL,
0, NULL))
-        {
-            RegCloseKey(hkey);
-            return TRUE;
-        }
-#endif
-
        if (RegQueryValueA(hkey, NULL, szFileType, &len))
        {
          RegCloseKey(hkey);
diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c
index f6058d04ee5..de484982b15 100644
--- a/dll/win32/shell32/wine/pidl.c
+++ b/dll/win32/shell32/wine/pidl.c
@@ -1183,6 +1183,14 @@ static HRESULT _ILParsePathW(LPCWSTR path, LPWIN32_FIND_DATAW
lpFindFile,
     return ret;
 }
+LPITEMIDLIST SHELL32_CreateSimpleIDListFromPath(LPCWSTR pszPath, DWORD dwAttributes)
+{
+    WIN32_FIND_DATAW data = { dwAttributes };
+    LPITEMIDLIST pidl = NULL;
+    _ILParsePathW(pszPath, &data, TRUE, &pidl, NULL);
+    return pidl;
+}
+
 /*************************************************************************
  * SHSimpleIDListFromPath    [SHELL32.162]
  *
@@ -2583,59 +2591,6 @@ BOOL _ILGetExtension(LPCITEMIDLIST pidl, LPWSTR pOut, UINT
uOutSize)
     return TRUE;
 }
-/*************************************************************************
- * _ILGetFileType
- *
- * Given the ItemIdList, get the file type description
- *
- * PARAMS
- *      pidl        [I] The ItemIDList (simple)
- *      pOut        [I] The buffer to save the result
- *      uOutsize    [I] The size of the buffer
- *
- * RETURNS
- *    nothing
- *
- * NOTES
- *    This function copies as much as possible into the buffer.
- */
-void _ILGetFileType(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
-{
-    WCHAR sType[64], sTemp[64];
-
-    if(_ILIsValue(pidl))
-    {
-        if(uOutSize > 0)
-            pOut[0] = 0;
-        if (_ILGetExtension(pidl, sType, _countof(sType)))
-        {
-            if (HCR_MapTypeToValueW(sType, sTemp, _countof(sTemp), TRUE))
-            {
-                /* retrieve description */
-                if (HCR_MapTypeToValueW(sTemp, pOut, uOutSize, FALSE))
-                    return;
-            }
-
-            /* display Ext-file as description */
-            CharUpperW(sType);
-            /* load localized file string */
-            sTemp[0] = UNICODE_NULL;
-            if (LoadStringW(shell32_hInstance, IDS_ANY_FILE, sTemp, _countof(sTemp)))
-            {
-                sTemp[_countof(sTemp) - 1] = UNICODE_NULL;
-                StringCchPrintfW(pOut, uOutSize, sTemp, sType);
-            }
-        }
-    }
-    else
-    {
-        pOut[0] = UNICODE_NULL;
-        LoadStringW(shell32_hInstance, IDS_DIRECTORY, pOut, uOutSize);
-        /* make sure its null terminated */
-        pOut[uOutSize - 1] = UNICODE_NULL;
-    }
-}
-
 /*************************************************************************
  * _ILGetFileAttributes
  *
diff --git a/dll/win32/shell32/wine/pidl.h b/dll/win32/shell32/wine/pidl.h
index ef0002941f7..28fff42feb4 100644
--- a/dll/win32/shell32/wine/pidl.h
+++ b/dll/win32/shell32/wine/pidl.h
@@ -246,7 +246,6 @@ DWORD   _ILSimpleGetTextW   (LPCITEMIDLIST pidl, LPWSTR pOut, UINT
uOutSize) DEC
 BOOL    _ILGetFileDate      (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
 DWORD   _ILGetFileSize      (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
 BOOL    _ILGetExtension     (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
-void    _ILGetFileType      (LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
 DWORD   _ILGetFileAttributes(LPCITEMIDLIST pidl, LPWSTR pOut, UINT uOutSize)
DECLSPEC_HIDDEN;
 BOOL    _ILGetFileDateTime  (LPCITEMIDLIST pidl, FILETIME *ft) DECLSPEC_HIDDEN;
 DWORD   _ILGetDrive         (LPCITEMIDLIST, LPWSTR, UINT) DECLSPEC_HIDDEN;
@@ -291,6 +290,7 @@ LPITEMIDLIST        _ILCreateGuidFromStrW(LPCWSTR szGUID)
DECLSPEC_HIDDEN;
 LPITEMIDLIST   _ILCreateDesktop        (void) DECLSPEC_HIDDEN;
 LPITEMIDLIST   _ILCreateFromFindDataW(const WIN32_FIND_DATAW *stffile) DECLSPEC_HIDDEN;
 HRESULT                _ILCreateFromPathW      (LPCWSTR szPath, LPITEMIDLIST* ppidl)
DECLSPEC_HIDDEN;
+LPITEMIDLIST SHELL32_CreateSimpleIDListFromPath(LPCWSTR pszPath, DWORD dwAttributes);
 /* Other helpers */
 LPITEMIDLIST   _ILCreateMyComputer     (void) DECLSPEC_HIDDEN;
diff --git a/dll/win32/shell32/wine/shell32_main.c b/dll/win32/shell32/wine/shell32_main.c
index e6dc9810c79..04967da66d1 100644
--- a/dll/win32/shell32/wine/shell32_main.c
+++ b/dll/win32/shell32/wine/shell32_main.c
@@ -310,6 +310,28 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
     return argv;
 }
+static HRESULT SHELL_GetDetailsOfToBuffer(IShellFolder *psf, PCUITEMID_CHILD pidl,
+                                          UINT col, LPWSTR Buf, UINT cchBuf)
+{
+    IShellFolder2 *psf2;
+    IShellDetails *psd;
+    SHELLDETAILS details;
+    HRESULT hr = IShellFolder_QueryInterface(psf, &IID_IShellFolder2,
(void**)&psf2);
+    if (SUCCEEDED(hr))
+    {
+        hr = IShellFolder2_GetDetailsOf(psf2, pidl, col, &details);
+        IShellFolder2_Release(psf2);
+    }
+    else if (SUCCEEDED(hr = IShellFolder_QueryInterface(psf, &IID_IShellDetails,
(void**)&psd)))
+    {
+        hr = IShellDetails_GetDetailsOf(psd, pidl, col, &details);
+        IShellDetails_Release(psd);
+    }
+    if (SUCCEEDED(hr))
+        hr = StrRetToStrNW(Buf, cchBuf, &details.str, pidl) ? S_OK : E_FAIL;
+    return hr;
+}
+
 static DWORD shgfi_get_exe_type(LPCWSTR szFullPath)
 {
     BOOL status = FALSE;
@@ -386,17 +408,11 @@ BOOL SHELL_IsShortcut(LPCITEMIDLIST pidlLast)
     BOOL ret = FALSE;
     if (_ILGetExtension(pidlLast, szTemp, _countof(szTemp)) &&
-        HCR_MapTypeToValueW(szTemp, szTemp, _countof(szTemp), TRUE))
+        SUCCEEDED(HCR_GetProgIdKeyOfExtension(szTemp, &keyCls, FALSE)))
     {
-        if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CLASSES_ROOT, szTemp, 0, KEY_QUERY_VALUE,
&keyCls))
-        {
-            if (ERROR_SUCCESS == RegQueryValueExW(keyCls, L"IsShortcut", NULL,
NULL, NULL, NULL))
-                ret = TRUE;
-
-            RegCloseKey(keyCls);
-        }
+        ret = RegQueryValueExW(keyCls, L"IsShortcut", NULL, NULL, NULL, NULL)
== ERROR_SUCCESS;
+        RegCloseKey(keyCls);
     }
-
     return ret;
 }
@@ -420,7 +436,7 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
     DWORD dwAttributes = 0;
     IShellFolder * psfParent = NULL;
     IExtractIconW * pei = NULL;
-    LPITEMIDLIST    pidlLast = NULL, pidl = NULL;
+    LPITEMIDLIST pidlLast = NULL, pidl = NULL, pidlFree = NULL;
     HRESULT hr = S_OK;
     BOOL IconNotYetLoaded=TRUE;
     UINT uGilFlags = 0;
@@ -453,6 +469,27 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
         {
             lstrcpynW(szFullPath, path, MAX_PATH);
         }
+
+        if ((flags & SHGFI_TYPENAME) && !PathIsRootW(szFullPath))
+        {
+            HRESULT hr2;
+            if (!(flags & SHGFI_USEFILEATTRIBUTES))
+            {
+                dwFileAttributes = GetFileAttributesW(szFullPath);
+                if (dwFileAttributes == INVALID_FILE_ATTRIBUTES)
+                    dwFileAttributes = 0;
+            }
+            if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                hr2 = SHELL32_AssocGetFSDirectoryDescription(psfi->szTypeName,
_countof(psfi->szTypeName));
+            else
+                hr2 = SHELL32_AssocGetFileDescription(path, psfi->szTypeName,
_countof(psfi->szTypeName));
+            if (SUCCEEDED(hr2))
+            {
+                flags &= ~SHGFI_TYPENAME;
+                if (!(flags & ~SHGFI_USEFILEATTRIBUTES))
+                    return ret; /* Bail out early if this was our only operation */
+            }
+        }
     }
     else
     {
@@ -489,31 +526,26 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
     if (flags & SHGFI_PIDL)
     {
-        pidl = ILClone((LPCITEMIDLIST)path);
-    }
-    else if (!(flags & SHGFI_USEFILEATTRIBUTES))
-    {
-        hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes);
+        pidl = (LPITEMIDLIST)path;
+        hr = pidl ? S_OK : E_FAIL;
     }
-
-    if ((flags & SHGFI_PIDL) || !(flags & SHGFI_USEFILEATTRIBUTES))
+    else
     {
-        /* get the parent shellfolder */
-        if (pidl)
+        if (flags & SHGFI_USEFILEATTRIBUTES)
         {
-            hr = SHBindToParent( pidl, &IID_IShellFolder, (LPVOID*)&psfParent,
-                                (LPCITEMIDLIST*)&pidlLast );
-            if (SUCCEEDED(hr))
-                pidlLast = ILClone(pidlLast);
-            else
-                hr = S_OK;
-            ILFree(pidl);
+            pidl = SHELL32_CreateSimpleIDListFromPath(szFullPath, dwFileAttributes);
+            hr = pidl ? S_OK : E_FAIL;
         }
         else
         {
-            ERR("pidl is null!\n");
-            return FALSE;
+            hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes);
         }
+        pidlFree = pidl;
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        hr = SHBindToParent(pidl, &IID_IShellFolder, (void**)&psfParent,
(LPCITEMIDLIST*)&pidlLast);
     }
     /* get the attributes of the child */
@@ -523,11 +555,7 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
         {
             psfi->dwAttributes = 0xffffffff;
         }
-        if (psfParent)
-        {
-            IShellFolder_GetAttributesOf(psfParent, 1, (LPCITEMIDLIST*)&pidlLast,
-                                         &(psfi->dwAttributes));
-        }
+        hr = IShellFolder_GetAttributesOf(psfParent, 1, (LPCITEMIDLIST*)&pidlLast,
&psfi->dwAttributes);
     }
     if (flags & SHGFI_USEFILEATTRIBUTES)
@@ -541,55 +569,20 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
     /* get the displayname */
     if (SUCCEEDED(hr) && (flags & SHGFI_DISPLAYNAME))
     {
-        if (flags & SHGFI_USEFILEATTRIBUTES && !(flags & SHGFI_PIDL))
-        {
-            lstrcpyW (psfi->szDisplayName, PathFindFileNameW(szFullPath));
-        }
-        else if (psfParent)
-        {
-            STRRET str;
-            hr = IShellFolder_GetDisplayNameOf( psfParent, pidlLast,
-                                                SHGDN_INFOLDER, &str);
-            StrRetToStrNW (psfi->szDisplayName, MAX_PATH, &str, pidlLast);
-        }
+        STRRET str;
+        psfi->szDisplayName[0] = UNICODE_NULL;
+        hr = IShellFolder_GetDisplayNameOf(psfParent, pidlLast, SHGDN_INFOLDER,
&str);
+        if (SUCCEEDED(hr))
+            StrRetToStrNW(psfi->szDisplayName, _countof(psfi->szDisplayName),
&str, pidlLast);
     }
     /* get the type name */
     if (SUCCEEDED(hr) && (flags & SHGFI_TYPENAME))
     {
-        if (!(flags & SHGFI_USEFILEATTRIBUTES) || (flags & SHGFI_PIDL))
-        {
-            _ILGetFileType(pidlLast, psfi->szTypeName, _countof(psfi->szTypeName));
-        }
-        else
-        {
-            if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-                strcatW (psfi->szTypeName, L"Folder");
-            else
-            {
-                WCHAR sTemp[64];
-
-                lstrcpyW(sTemp,PathFindExtensionW(szFullPath));
-                if (sTemp[0] == 0 || (sTemp[0] == '.' && sTemp[1] == 0))
-                {
-                    /* "name" or "name." => "File" */
-                    lstrcpynW (psfi->szTypeName, L"File", 64);
-                }
-                else if (!( HCR_MapTypeToValueW(sTemp, sTemp, 64, TRUE) &&
-                    HCR_MapTypeToValueW(sTemp, psfi->szTypeName, 80, FALSE )))
-                {
-                    if (sTemp[0])
-                    {
-                        lstrcpynW (psfi->szTypeName, sTemp, 64);
-                        strcatW (psfi->szTypeName, L" file");
-                    }
-                    else
-                    {
-                        lstrcpynW (psfi->szTypeName, L"File", 64);
-                    }
-                }
-            }
-        }
+        /* FIXME: Use IShellFolder2::GetDetailsEx */
+        UINT col = _ILIsDrive(pidlLast) ? 1 : 2; /* SHFSF_COL_TYPE */
+        psfi->szTypeName[0] = UNICODE_NULL;
+        hr = SHELL_GetDetailsOfToBuffer(psfParent, pidlLast, col, psfi->szTypeName,
_countof(psfi->szTypeName));
     }
     /* ### icons ###*/
@@ -757,12 +750,11 @@ DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
     if (psfParent)
         IShellFolder_Release(psfParent);
+    SHFree(pidlFree);
     if (hr != S_OK)
         ret = FALSE;
-    SHFree(pidlLast);
-
     TRACE ("icon=%p index=0x%08x attr=0x%08x name=%s type=%s ret=0x%08lx\n",
            psfi->hIcon, psfi->iIcon, psfi->dwAttributes,
            debugstr_w(psfi->szDisplayName), debugstr_w(psfi->szTypeName), ret);
diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h
index 73bebc71d3b..4392a50659a 100644
--- a/dll/win32/shell32/wine/shell32_main.h
+++ b/dll/win32/shell32/wine/shell32_main.h
@@ -46,6 +46,7 @@ BOOL PidlToSicIndex (IShellFolder * sh, LPCITEMIDLIST pidl, BOOL
bBigIcon, UINT
 INT SIC_GetIconIndex (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags )
DECLSPEC_HIDDEN;
 /* Classes Root */
+HRESULT HCR_GetProgIdKeyOfExtension(PCWSTR szExtension, PHKEY phKey, BOOL AllowFallback);
 BOOL HCR_MapTypeToValueW(LPCWSTR szExtension, LPWSTR szFileType, LONG len, BOOL
bPrependDot) DECLSPEC_HIDDEN;
 BOOL HCR_GetDefaultVerbW( HKEY hkeyClass, LPCWSTR szVerb, LPWSTR szDest, DWORD len )
DECLSPEC_HIDDEN;
 BOOL HCR_GetExecuteCommandW( HKEY hkeyClass, LPCWSTR szClass, LPCWSTR szVerb, LPWSTR
szDest, DWORD len ) DECLSPEC_HIDDEN;
@@ -67,6 +68,12 @@ BOOL HCR_GetClassNameA(REFIID riid, LPSTR szDest, DWORD len)
DECLSPEC_HIDDEN;
 BOOL HCR_GetFolderAttributes(LPCITEMIDLIST pidlFolder, LPDWORD dwAttributes)
DECLSPEC_HIDDEN;
+/* File associations */
+#define SHELL32_AssocGetFolderDescription SHELL32_AssocGetFSDirectoryDescription
+HRESULT SHELL32_AssocGetFSDirectoryDescription(PWSTR Buf, UINT cchBuf);
+HRESULT SHELL32_AssocGetFileDescription(PCWSTR Name, PWSTR Buf, UINT cchBuf);
+
+
 DWORD WINAPI ParseFieldA(LPCSTR src, DWORD nField, LPSTR dst, DWORD len) DECLSPEC_HIDDEN;
 DWORD WINAPI ParseFieldW(LPCWSTR src, DWORD nField, LPWSTR dst, DWORD len)
DECLSPEC_HIDDEN;
diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt
b/modules/rostests/apitests/shell32/CMakeLists.txt
index c0a6686eecf..2a90a0e009d 100644
--- a/modules/rostests/apitests/shell32/CMakeLists.txt
+++ b/modules/rostests/apitests/shell32/CMakeLists.txt
@@ -38,6 +38,7 @@ list(APPEND SOURCE
     ShellExecuteEx.cpp
     ShellExecuteW.cpp
     ShellHook.cpp
+    ShellInfo.cpp
     ShellState.cpp
     SHGetAttributesFromDataObject.cpp
     SHLimitInputEdit.cpp
diff --git a/modules/rostests/apitests/shell32/ItemIDList.cpp
b/modules/rostests/apitests/shell32/ItemIDList.cpp
index 9665dede641..02c4e694bd6 100644
--- a/modules/rostests/apitests/shell32/ItemIDList.cpp
+++ b/modules/rostests/apitests/shell32/ItemIDList.cpp
@@ -148,6 +148,12 @@ START_TEST(SHSimpleIDListFromPath)
         ok_long(item->mkid.abID[0] & 0x70, 0x20); // Something in My Computer
         ok_char(item->mkid.abID[1] | 32, 'x' | 32); // x:
     }
+
+    LPITEMIDLIST pidl;
+    ok_int((pidl = SHSimpleIDListFromPath(L"c:")) != NULL, TRUE);
+    ILFree(pidl);
+    ok_int((pidl = SHSimpleIDListFromPath(L"c:\\")) != NULL, TRUE);
+    ILFree(pidl);
 }
 START_TEST(ILCreateFromPath)
diff --git a/modules/rostests/apitests/shell32/ShellInfo.cpp
b/modules/rostests/apitests/shell32/ShellInfo.cpp
new file mode 100644
index 00000000000..13921611667
--- /dev/null
+++ b/modules/rostests/apitests/shell32/ShellInfo.cpp
@@ -0,0 +1,76 @@
+/*
+ * PROJECT:     ReactOS API tests
+ * LICENSE:     GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Test for SHGetFileInfo
+ * COPYRIGHT:   Copyright 2024 Whindmar Saksit <whindsaks(a)proton.me>
+ */
+
+#include "shelltest.h"
+#include <shellutils.h>
+
+#define UNIQUEEXT L"ABC123XYZ"
+#define my_ok_all_flags(val, flags) ok_eq_hex((val) & (flags), (flags))
+
+static DWORD_PTR SHGFI(PCWSTR Path, SHFILEINFOW &Info, UINT Flags, UINT Attributes =
0)
+{
+    return SHGetFileInfoW(Path, Attributes, &Info, sizeof(Info), Flags);
+}
+
+static DWORD_PTR SHGFI(LPCITEMIDLIST Pidl, SHFILEINFOW &Info, UINT Flags, UINT
Attributes = 0)
+{
+    return SHGFI((PCWSTR)Pidl, Info, Flags | SHGFI_PIDL, Attributes);
+}
+
+START_TEST(SHGetFileInfo)
+{
+    CCoInit ComInit;
+    LPITEMIDLIST pidl;
+    SHFILEINFOW info;
+    UINT flags;
+    WCHAR buf[MAX_PATH];
+
+    ok_int(SHGFI((PCWSTR)NULL, info, 0), FALSE);
+    ok_int(SHGFI((PCWSTR)NULL, info, SHGFI_DISPLAYNAME), FALSE);
+    ok_int(SHGFI((PCWSTR)NULL, info, SHGFI_TYPENAME), FALSE);
+    ok_int(SHGFI((PCWSTR)NULL, info, SHGFI_ICONLOCATION), FALSE);
+    ok_int(SHGFI((PCWSTR)NULL, info, SHGFI_SYSICONINDEX), FALSE);
+    ok_int(SHGFI(UNIQUEEXT, info, SHGFI_USEFILEATTRIBUTES), TRUE); // Success when asking
for no info
+    ok_int(SHGetFileInfoW(UNIQUEEXT, 0, NULL, 0, SHGFI_DISPLAYNAME |
SHGFI_USEFILEATTRIBUTES), FALSE); // NULL pointer
+    ok_int(SHGFI(UNIQUEEXT, info, SHGFI_EXETYPE | SHGFI_USEFILEATTRIBUTES), TRUE); //
Invalid combination, returns TRUE!
+
+    GetModuleFileNameW(NULL, buf, _countof(buf));
+    ok_int(LOWORD(SHGFI(buf, info, SHGFI_EXETYPE)), 0x4550); // 'PE'
+
+    flags = SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES | SHGFI_ATTRIBUTES;
+    ZeroMemory(&info, sizeof(info));
+    info.dwAttributes = ~SFGAO_VALIDATE;
+    ok_int(SHGFI(UNIQUEEXT, info, flags | SHGFI_ATTR_SPECIFIED), TRUE);
+    ok_ptr(StrStrIW(info.szTypeName, UNIQUEEXT), NULL); // A file without extension (not
"EXT File")
+    my_ok_all_flags(info.dwAttributes, SFGAO_FILESYSTEM | SFGAO_STREAM);
+
+    ZeroMemory(&info, sizeof(info));
+    info.dwAttributes = ~SFGAO_VALIDATE;
+    ok_int(SHGFI(UNIQUEEXT, info, flags | SHGFI_ATTR_SPECIFIED,
FILE_ATTRIBUTE_DIRECTORY), TRUE);
+    ok_ptr(StrStrIW(info.szTypeName, UNIQUEEXT), NULL); // A directory (not "EXT
File")
+    my_ok_all_flags(info.dwAttributes, SFGAO_FILESYSTEM | SFGAO_FOLDER);
+
+    ZeroMemory(&info, sizeof(info));
+    info.dwAttributes = ~SFGAO_VALIDATE;
+    ok_int(SHGFI(L"." UNIQUEEXT, info, flags | SHGFI_ATTR_SPECIFIED), TRUE);
+    ok_bool_true(StrStrIW(info.szTypeName, UNIQUEEXT) != NULL, ".ext is treated as
extension");
+    my_ok_all_flags(info.dwAttributes, SFGAO_FILESYSTEM | SFGAO_STREAM);
+
+    info.dwAttributes = ~SFGAO_VALIDATE;
+    ok_int(SHGFI(L"c:", info, flags | SHGFI_ATTR_SPECIFIED), TRUE); // ROS
fails this, a parsing bug in CDrivesFolder?
+    my_ok_all_flags(info.dwAttributes, SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR);
+
+    info.dwAttributes = ~SFGAO_VALIDATE;
+    ok_int(SHGFI(L"c:\\", info, flags | SHGFI_ATTR_SPECIFIED), TRUE);
+    my_ok_all_flags(info.dwAttributes, SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR);
+
+    pidl = SHSimpleIDListFromPath(L"c:\\foo");
+    info.iIcon = -1;
+    ok_int(SHGFI(pidl, info, SHGFI_SYSICONINDEX) > TRUE, TRUE);
+    ok_int(info.iIcon != -1, TRUE);
+    ILFree(pidl);
+}
diff --git a/modules/rostests/apitests/shell32/testlist.c
b/modules/rostests/apitests/shell32/testlist.c
index 4b16a6dd4d5..ff06c5a5dc6 100644
--- a/modules/rostests/apitests/shell32/testlist.c
+++ b/modules/rostests/apitests/shell32/testlist.c
@@ -41,6 +41,7 @@ extern void func_ShellExecuteW(void);
 extern void func_ShellHook(void);
 extern void func_ShellState(void);
 extern void func_SHGetAttributesFromDataObject(void);
+extern void func_SHGetFileInfo(void);
 extern void func_SHLimitInputEdit(void);
 extern void func_SHParseDisplayName(void);
 extern void func_SHSimpleIDListFromPath(void);
@@ -86,6 +87,7 @@ const struct test winetest_testlist[] =
     { "ShellHook", func_ShellHook },
     { "ShellState", func_ShellState },
     { "SHGetAttributesFromDataObject", func_SHGetAttributesFromDataObject },
+    { "SHGetFileInfo", func_SHGetFileInfo },
     { "SHLimitInputEdit", func_SHLimitInputEdit },
     { "SHParseDisplayName", func_SHParseDisplayName },
     { "SHSimpleIDListFromPath", func_SHSimpleIDListFromPath },