Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467/* * PROJECT: ReactOS Shell * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Implement shell light-weight utility functions * COPYRIGHT: Copyright 2023-2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#define _ATL_NO_EXCEPTIONS
/* * HACK! These functions are conflicting with <shobjidl.h> inline functions... */#define IShellFolder_GetDisplayNameOf _disabled_IShellFolder_GetDisplayNameOf_#define IShellFolder_ParseDisplayName _disabled_IShellFolder_ParseDisplayName_#define IShellFolder_CompareIDs _disabled_IShellFolder_CompareIDs_
#include "precomp.h"#include <shellapi.h>#include <shlwapi.h>#include <shlobj_undoc.h>#include <shlguid_undoc.h>#include <atlstr.h>
#include <shlwapi_undoc.h>#include <ishellfolder_helpers.h>
#include <strsafe.h>
#ifndef FAILED_UNEXPECTEDLY#define FAILED_UNEXPECTEDLY FAILED /* FIXME: Make shellutils.h usable without ATL */#endif
WINE_DEFAULT_DEBUG_CHANNEL(shell);
EXTERN_C LSTATUS WINAPI RegGetValueW(HKEY, LPCWSTR, LPCWSTR, DWORD, LPDWORD, PVOID, LPDWORD);
static inline WORD GetVersionMajorMinor(){ DWORD version = GetVersion(); return MAKEWORD(HIBYTE(version), LOBYTE(version));}
static HRESULTSHInvokeCommandOnContextMenuInternal( _In_opt_ HWND hWnd, _In_opt_ IUnknown* pUnk, _In_ IContextMenu* pCM, _In_ UINT fCMIC, _In_ UINT fCMF, _In_opt_ LPCSTR pszVerb, _In_opt_ LPCWSTR pwszDir, _In_ bool ForceQCM){ CMINVOKECOMMANDINFOEX info = { sizeof(info), fCMIC, hWnd, pszVerb }; INT iDefItem = 0; HMENU hMenu = NULL; HCURSOR hOldCursor; HRESULT hr = S_OK; WCHAR wideverb[MAX_PATH];
if (!pCM) return E_INVALIDARG;
hOldCursor = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT)); info.nShow = SW_NORMAL; if (pUnk) IUnknown_SetSite(pCM, pUnk);
if (IS_INTRESOURCE(pszVerb)) { hMenu = CreatePopupMenu(); if (hMenu) { hr = pCM->QueryContextMenu(hMenu, 0, 1, MAXSHORT, fCMF | CMF_DEFAULTONLY); iDefItem = GetMenuDefaultItem(hMenu, 0, 0); if (iDefItem != -1) info.lpVerb = MAKEINTRESOURCEA(iDefItem - 1); } info.lpVerbW = MAKEINTRESOURCEW(info.lpVerb); } else { if (GetVersionMajorMinor() >= _WIN32_WINNT_WIN7) { info.fMask |= CMF_OPTIMIZEFORINVOKE; } if (pszVerb && SHAnsiToUnicode(pszVerb, wideverb, _countof(wideverb))) { info.fMask |= CMIC_MASK_UNICODE; info.lpVerbW = wideverb; } if (ForceQCM) { hMenu = CreatePopupMenu(); hr = pCM->QueryContextMenu(hMenu, 0, 1, MAXSHORT, fCMF); } }
SetCursor(hOldCursor);
if (!FAILED_UNEXPECTEDLY(hr) && (iDefItem != -1 || info.lpVerb)) { if (!hWnd) info.fMask |= CMIC_MASK_FLAG_NO_UI;
CHAR dir[MAX_PATH]; if (pwszDir) { info.fMask |= CMIC_MASK_UNICODE; info.lpDirectoryW = pwszDir; if (SHUnicodeToAnsi(pwszDir, dir, _countof(dir))) info.lpDirectory = dir; }
hr = pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&info); if (FAILED_UNEXPECTEDLY(hr)) { /* Diagnostic message */ } }
if (pUnk) IUnknown_SetSite(pCM, NULL); if (hMenu) DestroyMenu(hMenu);
return hr;}
/************************************************************************* * SHInvokeCommandOnContextMenuEx [SHLWAPI.639] */EXTERN_CHRESULT WINAPISHInvokeCommandOnContextMenuEx( _In_opt_ HWND hWnd, _In_opt_ IUnknown* pUnk, _In_ IContextMenu* pCM, _In_ UINT fCMIC, _In_ UINT fCMF, _In_opt_ LPCSTR pszVerb, _In_opt_ LPCWSTR pwszDir){ return SHInvokeCommandOnContextMenuInternal(hWnd, pUnk, pCM, fCMIC, fCMF, pszVerb, pwszDir, true);}
/************************************************************************* * SHInvokeCommandOnContextMenu [SHLWAPI.540] */EXTERN_CHRESULT WINAPISHInvokeCommandOnContextMenu( _In_opt_ HWND hWnd, _In_opt_ IUnknown* pUnk, _In_ IContextMenu* pCM, _In_ UINT fCMIC, _In_opt_ LPCSTR pszVerb){ return SHInvokeCommandOnContextMenuEx(hWnd, pUnk, pCM, fCMIC, CMF_EXTENDEDVERBS, pszVerb, NULL);}
/************************************************************************* * SHInvokeCommandWithFlagsAndSite [SHLWAPI.571] */EXTERN_CHRESULT WINAPISHInvokeCommandWithFlagsAndSite( _In_opt_ HWND hWnd, _In_opt_ IUnknown* pUnk, _In_ IShellFolder* pShellFolder, _In_ LPCITEMIDLIST pidl, _In_ UINT fCMIC, _In_opt_ LPCSTR pszVerb){ HRESULT hr = E_INVALIDARG; if (pShellFolder) { IContextMenu *pCM; hr = pShellFolder->GetUIObjectOf(hWnd, 1, &pidl, IID_IContextMenu, NULL, (void**)&pCM); if (SUCCEEDED(hr)) { fCMIC |= CMIC_MASK_FLAG_LOG_USAGE; hr = SHInvokeCommandOnContextMenuEx(hWnd, pUnk, pCM, fCMIC, 0, pszVerb, NULL); pCM->Release(); } } return hr;}
/************************************************************************* * IContextMenu_Invoke [SHLWAPI.207] * * Used by Win:SHELL32!CISFBand::_TrySimpleInvoke. */EXTERN_CBOOL WINAPIIContextMenu_Invoke( _In_ IContextMenu *pContextMenu, _In_ HWND hwnd, _In_ LPCSTR lpVerb, _In_ UINT uFlags){ TRACE("(%p, %p, %s, %u)\n", pContextMenu, hwnd, debugstr_a(lpVerb), uFlags); HRESULT hr = SHInvokeCommandOnContextMenuInternal(hwnd, NULL, pContextMenu, 0, uFlags, lpVerb, NULL, false); return !FAILED_UNEXPECTEDLY(hr);}
/************************************************************************* * ShellExecuteCommand [INTERNAL] */static HRESULTShellExecuteCommand(_In_opt_ HWND hWnd, _In_ PCWSTR Command, _In_opt_ UINT Flags){ WCHAR szCmd[MAX_PATH * 2]; int len = PathProcessCommand(Command, szCmd, _countof(szCmd), PPCF_ADDARGUMENTS | PPCF_FORCEQUALIFY); if (len <= 0) // Could not resolve the command, just use the input { HRESULT hr = StringCchCopyW(szCmd, _countof(szCmd), Command); if (FAILED(hr)) return hr; } PWSTR pszArgs = PathGetArgsW(szCmd); PathRemoveArgsW(szCmd); PathUnquoteSpacesW(szCmd);
SHELLEXECUTEINFOW sei = { sizeof(sei), Flags, hWnd, NULL, szCmd, pszArgs }; sei.nShow = SW_SHOW; UINT error = ShellExecuteExW(&sei) ? ERROR_SUCCESS : GetLastError(); return HRESULT_FROM_WIN32(error);}
/************************************************************************* * RunRegCommand [SHLWAPI.469] */EXTERN_C HRESULT WINAPIRunRegCommand(_In_opt_ HWND hWnd, _In_ HKEY hKey, _In_opt_ PCWSTR pszSubKey){ WCHAR szCmd[MAX_PATH * 2]; DWORD cb = sizeof(szCmd); DWORD error = RegGetValueW(hKey, pszSubKey, NULL, RRF_RT_REG_SZ, NULL, szCmd, &cb); if (error) return HRESULT_FROM_WIN32(error); return ShellExecuteCommand(hWnd, szCmd, SEE_MASK_FLAG_LOG_USAGE);}
/************************************************************************* * RunIndirectRegCommand [SHLWAPI.468] */EXTERN_C HRESULT WINAPIRunIndirectRegCommand(_In_opt_ HWND hWnd, _In_ HKEY hKey, _In_opt_ PCWSTR pszSubKey, _In_ PCWSTR pszVerb){ WCHAR szKey[MAX_PATH]; HRESULT hr; if (pszSubKey) hr = StringCchPrintfW(szKey, _countof(szKey), L"%s\\shell\\%s\\command", pszSubKey, pszVerb); else hr = StringCchPrintfW(szKey, _countof(szKey), L"shell\\%s\\command", pszVerb); return SUCCEEDED(hr) ? RunRegCommand(hWnd, hKey, szKey) : hr;}
/************************************************************************* * SHRunIndirectRegClientCommand [SHLWAPI.467] */EXTERN_C HRESULT WINAPISHRunIndirectRegClientCommand(_In_opt_ HWND hWnd, _In_ PCWSTR pszClientType){ WCHAR szKey[MAX_PATH], szClient[MAX_PATH]; HRESULT hr = StringCchPrintfW(szKey, _countof(szKey), L"Software\\Clients\\%s", pszClientType); if (FAILED(hr)) return hr;
// Find the default client DWORD error, cb; cb = sizeof(szClient); error = RegGetValueW(HKEY_CURRENT_USER, szKey, NULL, RRF_RT_REG_SZ, NULL, szClient, &cb); if (error) { cb = sizeof(szClient); if (error != ERROR_MORE_DATA && error != ERROR_BUFFER_OVERFLOW) error = RegGetValueW(HKEY_LOCAL_MACHINE, szKey, NULL, RRF_RT_REG_SZ, NULL, szClient, &cb); if (error) return HRESULT_FROM_WIN32(error); }
hr = StringCchPrintfW(szKey, _countof(szKey), L"Software\\Clients\\%s\\%s", pszClientType, szClient); if (SUCCEEDED(hr)) hr = RunIndirectRegCommand(hWnd, HKEY_LOCAL_MACHINE, szKey, L"open"); return hr;}
/************************************************************************* * PathFileExistsDefExtAndAttributesW [SHLWAPI.511] * * @param pszPath The path string. * @param dwWhich The WHICH_... flags. * @param pdwFileAttributes A pointer to the file attributes. Optional. * @return TRUE if successful. */BOOL WINAPIPathFileExistsDefExtAndAttributesW( _Inout_ LPWSTR pszPath, _In_ DWORD dwWhich, _Out_opt_ LPDWORD pdwFileAttributes){ TRACE("(%s, 0x%lX, %p)\n", debugstr_w(pszPath), dwWhich, pdwFileAttributes);
if (pdwFileAttributes) *pdwFileAttributes = INVALID_FILE_ATTRIBUTES;
if (!pszPath) return FALSE;
if (!dwWhich || (*PathFindExtensionW(pszPath) && (dwWhich & WHICH_OPTIONAL))) return PathFileExistsAndAttributesW(pszPath, pdwFileAttributes);
if (!PathFileExistsDefExtW(pszPath, dwWhich)) { if (pdwFileAttributes) *pdwFileAttributes = INVALID_FILE_ATTRIBUTES; return FALSE; }
if (pdwFileAttributes) *pdwFileAttributes = GetFileAttributesW(pszPath);
return TRUE;}
static inline BOOLSHLWAPI_IsBogusHRESULT(HRESULT hr){ return (hr == E_FAIL || hr == E_INVALIDARG || hr == E_NOTIMPL);}
// Used for IShellFolder_GetDisplayNameOfstruct RETRY_DATA{ SHGDNF uRemove; SHGDNF uAdd; DWORD dwRetryFlags;};static const RETRY_DATA g_RetryData[] ={ { SHGDN_FOREDITING, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, { SHGDN_FORADDRESSBAR, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, { SHGDN_NORMAL, SHGDN_FORPARSING, SFGDNO_RETRYALWAYS }, { SHGDN_FORPARSING, SHGDN_NORMAL, SFGDNO_RETRYWITHFORPARSING }, { SHGDN_INFOLDER, SHGDN_NORMAL, SFGDNO_RETRYALWAYS },};
/************************************************************************* * IShellFolder_GetDisplayNameOf [SHLWAPI.316] * * @note Don't confuse with <shobjidl.h> inline function of the same name. * If the original call fails with the given uFlags, this function will * retry with other flags to attempt retrieving any meaningful description. */EXTERN_C HRESULT WINAPIIShellFolder_GetDisplayNameOf( _In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ SHGDNF uFlags, _Out_ LPSTRRET lpName, _In_ DWORD dwRetryFlags) // dwRetryFlags is an additional parameter{ HRESULT hr;
TRACE("(%p)->(%p, 0x%lX, %p, 0x%lX)\n", psf, pidl, uFlags, lpName, dwRetryFlags);
hr = psf->GetDisplayNameOf(pidl, uFlags, lpName); if (!SHLWAPI_IsBogusHRESULT(hr)) return hr;
dwRetryFlags |= SFGDNO_RETRYALWAYS;
if ((uFlags & SHGDN_FORPARSING) == 0) dwRetryFlags |= SFGDNO_RETRYWITHFORPARSING;
// Retry with other flags to get successful results for (SIZE_T iEntry = 0; iEntry < _countof(g_RetryData); ++iEntry) { const RETRY_DATA *pData = &g_RetryData[iEntry]; if (!(dwRetryFlags & pData->dwRetryFlags)) continue;
SHGDNF uNewFlags = ((uFlags & ~pData->uRemove) | pData->uAdd); if (uNewFlags == uFlags) continue;
hr = psf->GetDisplayNameOf(pidl, uNewFlags, lpName); if (!SHLWAPI_IsBogusHRESULT(hr)) break;
uFlags = uNewFlags; // Update flags every time }
return hr;}
/************************************************************************* * IShellFolder_ParseDisplayName [SHLWAPI.317] * * @note Don't confuse with <shobjidl.h> inline function of the same name. * This function is safer than IShellFolder::ParseDisplayName. */EXTERN_C HRESULT WINAPIIShellFolder_ParseDisplayName( _In_ IShellFolder *psf, _In_opt_ HWND hwndOwner, _In_opt_ LPBC pbcReserved, _In_ LPOLESTR lpszDisplayName, _Out_opt_ ULONG *pchEaten, _Out_opt_ PIDLIST_RELATIVE *ppidl, _Out_opt_ ULONG *pdwAttributes){ ULONG dummy1, dummy2;
TRACE("(%p)->(%p, %p, %s, %p, %p, %p)\n", psf, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), pchEaten, ppidl, pdwAttributes);
if (!pdwAttributes) { dummy1 = 0; pdwAttributes = &dummy1; }
if (!pchEaten) { dummy2 = 0; pchEaten = &dummy2; }
if (ppidl) *ppidl = NULL;
return psf->ParseDisplayName(hwndOwner, pbcReserved, lpszDisplayName, pchEaten, ppidl, pdwAttributes);}
/************************************************************************* * IShellFolder_CompareIDs [SHLWAPI.551] * * @note Don't confuse with <shobjidl.h> inline function of the same name. * This function tries IShellFolder2 if possible. */EXTERN_C HRESULT WINAPIIShellFolder_CompareIDs( _In_ IShellFolder *psf, _In_ LPARAM lParam, _In_ PCUIDLIST_RELATIVE pidl1, _In_ PCUIDLIST_RELATIVE pidl2){ TRACE("(%p, %p, %p, %p)\n", psf, lParam, pidl1, pidl2);
if (lParam & ~(SIZE_T)SHCIDS_COLUMNMASK) { /* Try as IShellFolder2 if possible */ HRESULT hr = psf->QueryInterface(IID_IShellFolder2, (void **)&psf); if (FAILED(hr)) lParam &= SHCIDS_COLUMNMASK; else psf->Release(); }
return psf->CompareIDs(lParam, pidl1, pidl2);}