1 /* 2 * PROJECT: ReactOS Shell 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Implement shell light-weight utility functions 5 * COPYRIGHT: Copyright 2023-2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #define _ATL_NO_EXCEPTIONS 9 10 /* 11 * HACK! These functions are conflicting with <shobjidl.h> inline functions... 12 */ 13 #define IShellFolder_GetDisplayNameOf _disabled_IShellFolder_GetDisplayNameOf_ 14 #define IShellFolder_ParseDisplayName _disabled_IShellFolder_ParseDisplayName_ 15 #define IShellFolder_CompareIDs _disabled_IShellFolder_CompareIDs_ 16 17 #include "precomp.h" 18 #include <shellapi.h> 19 #include <shlwapi.h> 20 #include <shlobj_undoc.h> 21 #include <shlguid_undoc.h> 22 #include <atlstr.h> 23 24 /* 25 * HACK! 26 */ 27 #undef IShellFolder_GetDisplayNameOf 28 #undef IShellFolder_ParseDisplayName 29 #undef IShellFolder_CompareIDs 30 31 #define SHLWAPI_ISHELLFOLDER_HELPERS /* HACK! */ 32 #include <shlwapi_undoc.h> 33 34 #include <strsafe.h> 35 36 WINE_DEFAULT_DEBUG_CHANNEL(shell); 37 38 /************************************************************************* 39 * IContextMenu_Invoke [SHLWAPI.207] 40 * 41 * Used by Win:SHELL32!CISFBand::_TrySimpleInvoke. 42 */ 43 EXTERN_C 44 BOOL WINAPI 45 IContextMenu_Invoke( 46 _In_ IContextMenu *pContextMenu, 47 _In_ HWND hwnd, 48 _In_ LPCSTR lpVerb, 49 _In_ UINT uFlags) 50 { 51 CMINVOKECOMMANDINFO info; 52 BOOL ret = FALSE; 53 INT iDefItem = 0; 54 HMENU hMenu = NULL; 55 HCURSOR hOldCursor; 56 57 TRACE("(%p, %p, %s, %u)\n", pContextMenu, hwnd, debugstr_a(lpVerb), uFlags); 58 59 if (!pContextMenu) 60 return FALSE; 61 62 hOldCursor = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT)); 63 64 ZeroMemory(&info, sizeof(info)); 65 info.cbSize = sizeof(info); 66 info.hwnd = hwnd; 67 info.nShow = SW_NORMAL; 68 info.lpVerb = lpVerb; 69 70 if (IS_INTRESOURCE(lpVerb)) 71 { 72 hMenu = CreatePopupMenu(); 73 if (hMenu) 74 { 75 pContextMenu->QueryContextMenu(hMenu, 0, 1, MAXSHORT, uFlags | CMF_DEFAULTONLY); 76 iDefItem = GetMenuDefaultItem(hMenu, 0, 0); 77 if (iDefItem != -1) 78 info.lpVerb = MAKEINTRESOURCEA(iDefItem - 1); 79 } 80 } 81 82 if (iDefItem != -1 || info.lpVerb) 83 { 84 if (!hwnd) 85 info.fMask |= CMIC_MASK_FLAG_NO_UI; 86 ret = SUCCEEDED(pContextMenu->InvokeCommand(&info)); 87 } 88 89 /* Invoking itself doesn't need the menu object, but getting the command info 90 needs the menu. */ 91 if (hMenu) 92 DestroyMenu(hMenu); 93 94 SetCursor(hOldCursor); 95 96 return ret; 97 } 98 99 /************************************************************************* 100 * PathFileExistsDefExtAndAttributesW [SHLWAPI.511] 101 * 102 * @param pszPath The path string. 103 * @param dwWhich The WHICH_... flags. 104 * @param pdwFileAttributes A pointer to the file attributes. Optional. 105 * @return TRUE if successful. 106 */ 107 BOOL WINAPI 108 PathFileExistsDefExtAndAttributesW( 109 _Inout_ LPWSTR pszPath, 110 _In_ DWORD dwWhich, 111 _Out_opt_ LPDWORD pdwFileAttributes) 112 { 113 TRACE("(%s, 0x%lX, %p)\n", debugstr_w(pszPath), dwWhich, pdwFileAttributes); 114 115 if (pdwFileAttributes) 116 *pdwFileAttributes = INVALID_FILE_ATTRIBUTES; 117 118 if (!pszPath) 119 return FALSE; 120 121 if (!dwWhich || (*PathFindExtensionW(pszPath) && (dwWhich & WHICH_OPTIONAL))) 122 return PathFileExistsAndAttributesW(pszPath, pdwFileAttributes); 123 124 if (!PathFileExistsDefExtW(pszPath, dwWhich)) 125 { 126 if (pdwFileAttributes) 127 *pdwFileAttributes = INVALID_FILE_ATTRIBUTES; 128 return FALSE; 129 } 130 131 if (pdwFileAttributes) 132 *pdwFileAttributes = GetFileAttributesW(pszPath); 133 134 return TRUE; 135 } 136 137 static inline BOOL 138 SHLWAPI_IsBogusHRESULT(HRESULT hr) 139 { 140 return (hr == E_FAIL || hr == E_INVALIDARG || hr == E_NOTIMPL); 141 } 142 143 // Used for IShellFolder_GetDisplayNameOf 144 struct RETRY_DATA 145 { 146 SHGDNF uRemove; 147 SHGDNF uAdd; 148 DWORD dwRetryFlags; 149 }; 150 static const RETRY_DATA g_RetryData[] = 151 { 152 { SHGDN_FOREDITING, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, 153 { SHGDN_FORADDRESSBAR, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, 154 { SHGDN_NORMAL, SHGDN_FORPARSING, SFGDNO_RETRYALWAYS }, 155 { SHGDN_FORPARSING, SHGDN_NORMAL, SFGDNO_RETRYWITHFORPARSING }, 156 { SHGDN_INFOLDER, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, 157 }; 158 159 /************************************************************************* 160 * IShellFolder_GetDisplayNameOf [SHLWAPI.316] 161 * 162 * @note Don't confuse with <shobjidl.h> inline function of the same name. 163 * If the original call fails with the given uFlags, this function will 164 * retry with other flags to attempt retrieving any meaningful description. 165 */ 166 EXTERN_C HRESULT WINAPI 167 IShellFolder_GetDisplayNameOf( 168 _In_ IShellFolder *psf, 169 _In_ LPCITEMIDLIST pidl, 170 _In_ SHGDNF uFlags, 171 _Out_ LPSTRRET lpName, 172 _In_ DWORD dwRetryFlags) // dwRetryFlags is an additional parameter 173 { 174 HRESULT hr; 175 176 TRACE("(%p)->(%p, 0x%lX, %p, 0x%lX)\n", psf, pidl, uFlags, lpName, dwRetryFlags); 177 178 hr = psf->GetDisplayNameOf(pidl, uFlags, lpName); 179 if (!SHLWAPI_IsBogusHRESULT(hr)) 180 return hr; 181 182 dwRetryFlags |= SFGDNO_RETRYALWAYS; 183 184 if ((uFlags & SHGDN_FORPARSING) == 0) 185 dwRetryFlags |= SFGDNO_RETRYWITHFORPARSING; 186 187 // Retry with other flags to get successful results 188 for (SIZE_T iEntry = 0; iEntry < _countof(g_RetryData); ++iEntry) 189 { 190 const RETRY_DATA *pData = &g_RetryData[iEntry]; 191 if (!(dwRetryFlags & pData->dwRetryFlags)) 192 continue; 193 194 SHGDNF uNewFlags = ((uFlags & ~pData->uRemove) | pData->uAdd); 195 if (uNewFlags == uFlags) 196 continue; 197 198 hr = psf->GetDisplayNameOf(pidl, uNewFlags, lpName); 199 if (!SHLWAPI_IsBogusHRESULT(hr)) 200 break; 201 202 uFlags = uNewFlags; // Update flags every time 203 } 204 205 return hr; 206 } 207 208 /************************************************************************* 209 * IShellFolder_ParseDisplayName [SHLWAPI.317] 210 * 211 * @note Don't confuse with <shobjidl.h> inline function of the same name. 212 * This function is safer than IShellFolder::ParseDisplayName. 213 */ 214 EXTERN_C HRESULT WINAPI 215 IShellFolder_ParseDisplayName( 216 _In_ IShellFolder *psf, 217 _In_opt_ HWND hwndOwner, 218 _In_opt_ LPBC pbcReserved, 219 _In_ LPOLESTR lpszDisplayName, 220 _Out_opt_ ULONG *pchEaten, 221 _Out_opt_ PIDLIST_RELATIVE *ppidl, 222 _Out_opt_ ULONG *pdwAttributes) 223 { 224 ULONG dummy1, dummy2; 225 226 TRACE("(%p)->(%p, %p, %s, %p, %p, %p)\n", psf, hwndOwner, pbcReserved, 227 debugstr_w(lpszDisplayName), pchEaten, ppidl, pdwAttributes); 228 229 if (!pdwAttributes) 230 { 231 dummy1 = 0; 232 pdwAttributes = &dummy1; 233 } 234 235 if (!pchEaten) 236 { 237 dummy2 = 0; 238 pchEaten = &dummy2; 239 } 240 241 if (ppidl) 242 *ppidl = NULL; 243 244 return psf->ParseDisplayName(hwndOwner, pbcReserved, lpszDisplayName, pchEaten, 245 ppidl, pdwAttributes); 246 } 247 248 /************************************************************************* 249 * IShellFolder_CompareIDs [SHLWAPI.551] 250 * 251 * @note Don't confuse with <shobjidl.h> inline function of the same name. 252 * This function tries IShellFolder2 if possible. 253 */ 254 EXTERN_C HRESULT WINAPI 255 IShellFolder_CompareIDs( 256 _In_ IShellFolder *psf, 257 _In_ LPARAM lParam, 258 _In_ PCUIDLIST_RELATIVE pidl1, 259 _In_ PCUIDLIST_RELATIVE pidl2) 260 { 261 TRACE("(%p, %p, %p, %p)\n", psf, lParam, pidl1, pidl2); 262 263 if (lParam & ~(SIZE_T)SHCIDS_COLUMNMASK) 264 { 265 /* Try as IShellFolder2 if possible */ 266 HRESULT hr = psf->QueryInterface(IID_IShellFolder2, (void **)&psf); 267 if (FAILED(hr)) 268 lParam &= SHCIDS_COLUMNMASK; 269 else 270 psf->Release(); 271 } 272 273 return psf->CompareIDs(lParam, pidl1, pidl2); 274 } 275