From 8107ff86368259fd5358287d8e39c5e7180946e1 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Sun, 17 Nov 2024 20:13:05 +0100 Subject: [PATCH] [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 + .../rostests/apitests/shell32/CMakeLists.txt | 1 + .../rostests/apitests/shell32/ItemIDList.cpp | 6 + .../rostests/apitests/shell32/ShellInfo.cpp | 76 +++++++++ modules/rostests/apitests/shell32/testlist.c | 2 + 12 files changed, 288 insertions(+), 166 deletions(-) create mode 100644 modules/rostests/apitests/shell32/ShellInfo.cpp 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); + pidl = (LPITEMIDLIST)path; + hr = pidl ? S_OK : E_FAIL; } - else if (!(flags & SHGFI_USEFILEATTRIBUTES)) + else { - hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes); - } - - if ((flags & SHGFI_PIDL) || !(flags & SHGFI_USEFILEATTRIBUTES)) - { - /* 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 + */ + +#include "shelltest.h" +#include + +#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 },