xref: /reactos/dll/win32/shell32/utils.cpp (revision f06c4dcc)
1 /*
2  * PROJECT:     shell32
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     Utility functions
5  * COPYRIGHT:   Copyright 2023-2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "precomp.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(shell);
11 
12 HWND
13 CStubWindow32::FindStubWindow(UINT Type, LPCWSTR Path)
14 {
15     for (HWND hWnd, hWndAfter = NULL;;)
16     {
17         hWnd = hWndAfter = FindWindowExW(NULL, hWndAfter, CSTUBWINDOW32_CLASSNAME, Path);
18         if (!hWnd || !Path)
19             return NULL;
20         if (GetPropW(hWnd, GetTypePropName()) == ULongToHandle(Type))
21             return hWnd;
22     }
23 }
24 
25 HRESULT
26 CStubWindow32::CreateStub(UINT Type, LPCWSTR Path, const POINT *pPt)
27 {
28     if (HWND hWnd = FindStubWindow(Type, Path))
29     {
30         ::SwitchToThisWindow(::GetLastActivePopup(hWnd), TRUE);
31         return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
32     }
33     RECT rcPosition = { pPt ? pPt->x : CW_USEDEFAULT, pPt ? pPt->y : CW_USEDEFAULT, 0, 0 };
34     DWORD Style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION;
35     DWORD ExStyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
36     if (!Create(NULL, rcPosition, Path, Style, ExStyle))
37     {
38         ERR("StubWindow32 creation failed\n");
39         return E_FAIL;
40     }
41     ::SetPropW(*this, GetTypePropName(), ULongToHandle(Type));
42     return S_OK;
43 }
44 
45 HRESULT
46 SHILClone(
47     _In_opt_ LPCITEMIDLIST pidl,
48     _Outptr_ LPITEMIDLIST *ppidl)
49 {
50     if (!pidl)
51     {
52         *ppidl = NULL;
53         return S_OK;
54     }
55     *ppidl = ILClone(pidl);
56     return (*ppidl ? S_OK : E_OUTOFMEMORY);
57 }
58 
59 BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath)
60 {
61     if (pszPath[0] != L'.')
62         return FALSE;
63     return !pszPath[1] || (pszPath[1] == L'.' && !pszPath[2]);
64 }
65 
66 #define PATH_VALID_ELEMENT ( \
67     PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \
68     PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID \
69 )
70 
71 BOOL PathIsValidElement(_In_ LPCWSTR pszPath)
72 {
73     if (!*pszPath || PathIsDotOrDotDotW(pszPath))
74         return FALSE;
75 
76     for (LPCWSTR pch = pszPath; *pch; ++pch)
77     {
78         if (!PathIsValidCharW(*pch, PATH_VALID_ELEMENT))
79             return FALSE;
80     }
81 
82     return TRUE;
83 }
84 
85 BOOL PathIsDosDevice(_In_ LPCWSTR pszName)
86 {
87     WCHAR szPath[MAX_PATH];
88     StringCchCopyW(szPath, _countof(szPath), pszName);
89     PathRemoveExtensionW(szPath);
90 
91     if (lstrcmpiW(szPath, L"NUL") == 0 || lstrcmpiW(szPath, L"PRN") == 0 ||
92         lstrcmpiW(szPath, L"CON") == 0 || lstrcmpiW(szPath, L"AUX") == 0)
93     {
94         return TRUE;
95     }
96 
97     if (_wcsnicmp(szPath, L"LPT", 3) == 0 || _wcsnicmp(szPath, L"COM", 3) == 0)
98     {
99         if ((L'0' <= szPath[3] && szPath[3] <= L'9') && szPath[4] == UNICODE_NULL)
100             return TRUE;
101     }
102 
103     return FALSE;
104 }
105 
106 HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl)
107 {
108     LPITEMIDLIST pidlOld = *ppidl;
109     if (!pidlOld)
110     {
111         *ppidl = pidl;
112         return S_OK;
113     }
114 
115     HRESULT hr = SHILCombine(*ppidl, pidl, ppidl);
116     ILFree(pidlOld);
117     ILFree(pidl);
118     return hr;
119 }
120 
121 static BOOL
122 OpenEffectiveToken(
123     _In_ DWORD DesiredAccess,
124     _Out_ HANDLE *phToken)
125 {
126     BOOL ret;
127 
128     if (phToken == NULL)
129     {
130         SetLastError(ERROR_INVALID_PARAMETER);
131         return FALSE;
132     }
133 
134     *phToken = NULL;
135 
136     ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken);
137     if (!ret && GetLastError() == ERROR_NO_TOKEN)
138         ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken);
139 
140     return ret;
141 }
142 
143 HRESULT
144 Shell_TranslateIDListAlias(
145     _In_ LPCITEMIDLIST pidl,
146     _In_ HANDLE hToken,
147     _Out_ LPITEMIDLIST *ppidlAlias,
148     _In_ DWORD dwFlags)
149 {
150     return E_FAIL; //FIXME
151 }
152 
153 BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName)
154 {
155     CComPtr<IUnknown> punk;
156     if (!pBindCtx || FAILED(pBindCtx->GetObjectParam(const_cast<LPWSTR>(pszName), &punk)))
157         return FALSE;
158     return TRUE;
159 }
160 
161 DWORD BindCtx_GetMode(_In_ IBindCtx *pbc, _In_ DWORD dwDefault)
162 {
163     if (!pbc)
164         return dwDefault;
165 
166     BIND_OPTS BindOpts = { sizeof(BindOpts) };
167     HRESULT hr = pbc->GetBindOptions(&BindOpts);
168     if (FAILED(hr))
169         return dwDefault;
170 
171     return BindOpts.grfMode;
172 }
173 
174 BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid)
175 {
176     if (!pbc)
177         return FALSE;
178 
179     BIND_OPTS BindOps = { sizeof(BindOps) };
180     if (SUCCEEDED(pbc->GetBindOptions(&BindOps)) && BindOps.grfFlags == OLECONTF_LINKS)
181         return TRUE;
182 
183     return pclsid && SHSkipJunction(pbc, pclsid);
184 }
185 
186 HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *pFindData)
187 {
188     CComPtr<IUnknown> punk;
189     CComPtr<IFileSystemBindData> pBindData;
190 
191     if (!pBindCtx || FAILED(pBindCtx->GetObjectParam((LPWSTR)STR_FILE_SYS_BIND_DATA, &punk)))
192         return S_FALSE;
193 
194     if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData))))
195         return S_FALSE;
196 
197     if (pFindData)
198         pBindData->GetFindData(pFindData);
199 
200     return S_OK;
201 }
202 
203 BOOL Shell_FailForceReturn(_In_ HRESULT hr)
204 {
205     DWORD code = HRESULT_CODE(hr);
206 
207     switch (code)
208     {
209         case ERROR_BAD_NETPATH:
210         case ERROR_BAD_NET_NAME:
211         case ERROR_CANCELLED:
212             return TRUE;
213 
214         default:
215             return (ERROR_FILE_NOT_FOUND <= code && code <= ERROR_PATH_NOT_FOUND);
216     }
217 }
218 
219 HRESULT
220 SHBindToObjectEx(
221     _In_opt_ IShellFolder *pShellFolder,
222     _In_ LPCITEMIDLIST pidl,
223     _In_opt_ IBindCtx *pBindCtx,
224     _In_ REFIID riid,
225     _Out_ void **ppvObj)
226 {
227     CComPtr<IShellFolder> psfDesktop;
228 
229     *ppvObj = NULL;
230 
231     if (!pShellFolder)
232     {
233         SHGetDesktopFolder(&psfDesktop);
234         if (!psfDesktop)
235             return E_FAIL;
236 
237         pShellFolder = psfDesktop;
238     }
239 
240     HRESULT hr;
241     if (_ILIsDesktop(pidl))
242         hr = pShellFolder->QueryInterface(riid, ppvObj);
243     else
244         hr = pShellFolder->BindToObject(pidl, pBindCtx, riid, ppvObj);
245 
246     if (SUCCEEDED(hr) && !*ppvObj)
247         hr = E_FAIL;
248 
249     return hr;
250 }
251 
252 EXTERN_C
253 HRESULT SHBindToObject(
254     _In_opt_ IShellFolder *psf,
255     _In_ LPCITEMIDLIST pidl,
256     _In_ REFIID riid,
257     _Out_ void **ppvObj)
258 {
259     return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj);
260 }
261 
262 EXTERN_C HRESULT
263 SHELL_GetUIObjectOfAbsoluteItem(
264     _In_opt_ HWND hWnd,
265     _In_ PCIDLIST_ABSOLUTE pidl,
266     _In_ REFIID riid, _Out_ void **ppvObj)
267 {
268     if (!ppvObj)
269         return E_INVALIDARG;
270     *ppvObj = NULL;
271     IShellFolder *psf;
272     PCUITEMID_CHILD pidlChild;
273     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
274     if (SUCCEEDED(hr))
275     {
276         hr = psf->GetUIObjectOf(hWnd, 1, &pidlChild, riid, NULL, ppvObj);
277         psf->Release();
278         if (SUCCEEDED(hr))
279         {
280             if (*ppvObj)
281                 return hr;
282             hr = E_FAIL;
283         }
284     }
285     return hr;
286 }
287 
288 HRESULT
289 Shell_DisplayNameOf(
290     _In_ IShellFolder *psf,
291     _In_ LPCITEMIDLIST pidl,
292     _In_ DWORD dwFlags,
293     _Out_ LPWSTR pszBuf,
294     _In_ UINT cchBuf)
295 {
296     *pszBuf = UNICODE_NULL;
297     STRRET sr;
298     HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr);
299     if (FAILED(hr))
300         return hr;
301     return StrRetToBufW(&sr, pidl, pszBuf, cchBuf);
302 }
303 
304 DWORD
305 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes)
306 {
307     LPCITEMIDLIST pidlLast = pidl;
308     IShellFolder *release = NULL;
309 
310     if (!psf)
311     {
312         SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
313         if (!psf)
314             return 0;
315         release = psf;
316     }
317 
318     DWORD oldAttrs = dwAttributes;
319     if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes)))
320         dwAttributes = 0;
321     else
322         dwAttributes &= oldAttrs;
323 
324     if ((dwAttributes & SFGAO_FOLDER) &&
325         (dwAttributes & SFGAO_STREAM) &&
326         !(dwAttributes & SFGAO_STORAGEANCESTOR) &&
327         (oldAttrs & SFGAO_STORAGEANCESTOR) &&
328         (SHGetObjectCompatFlags(psf, NULL) & 0x200))
329     {
330         dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR);
331         dwAttributes |= SFGAO_STORAGEANCESTOR;
332     }
333 
334     if (release)
335         release->Release();
336     return dwAttributes;
337 }
338 
339 HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl)
340 {
341     IShellLink *pSL;
342     HRESULT hr = SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellLink, &pSL));
343     if (SUCCEEDED(hr))
344     {
345         hr = pSL->GetIDList(ppidl); // Note: Returns S_FALSE if no target pidl
346         pSL->Release();
347     }
348     return hr;
349 }
350 
351 HRESULT SHCoInitializeAnyApartment(VOID)
352 {
353     HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
354     if (FAILED(hr))
355         hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE);
356     return hr;
357 }
358 
359 HRESULT
360 SHGetNameAndFlagsW(
361     _In_ LPCITEMIDLIST pidl,
362     _In_ DWORD dwFlags,
363     _Out_opt_ LPWSTR pszText,
364     _In_ UINT cchBuf,
365     _Inout_opt_ DWORD *pdwAttributes)
366 {
367     if (pszText)
368         *pszText = UNICODE_NULL;
369 
370     HRESULT hrCoInit = SHCoInitializeAnyApartment();
371 
372     CComPtr<IShellFolder> psfFolder;
373     LPCITEMIDLIST ppidlLast;
374     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast);
375     if (SUCCEEDED(hr))
376     {
377         if (pszText)
378             hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf);
379 
380         if (SUCCEEDED(hr))
381         {
382             if (pdwAttributes)
383                 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes);
384         }
385     }
386 
387     if (SUCCEEDED(hrCoInit))
388         CoUninitialize();
389 
390     return hr;
391 }
392 
393 EXTERN_C HWND
394 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx)
395 {
396     HWND hWnd = NULL;
397 
398     CComPtr<IUnknown> punk;
399     if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk)))
400         IUnknown_GetWindow(punk, &hWnd);
401 
402     return hWnd;
403 }
404 
405 class CDummyOleWindow : public IOleWindow
406 {
407 protected:
408     LONG m_cRefs;
409     HWND m_hWnd;
410 
411 public:
412     CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { }
413     virtual ~CDummyOleWindow() { }
414 
415     // IUnknown methods
416     STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override
417     {
418         static const QITAB c_tab[] =
419         {
420             QITABENT(CDummyOleWindow, IOleWindow),
421             { NULL }
422         };
423         return ::QISearch(this, c_tab, riid, ppvObj);
424     }
425     STDMETHODIMP_(ULONG) AddRef() override
426     {
427         return ++m_cRefs;
428     }
429     STDMETHODIMP_(ULONG) Release() override
430     {
431         if (--m_cRefs == 0)
432         {
433             delete this;
434             return 0;
435         }
436         return m_cRefs;
437     }
438 
439     // IOleWindow methods
440     STDMETHODIMP GetWindow(HWND *phWnd) override
441     {
442         *phWnd = m_hWnd;
443         if (!m_hWnd)
444             return E_NOTIMPL;
445         return S_OK;
446     }
447     STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override
448     {
449         return E_NOTIMPL;
450     }
451 };
452 
453 EXTERN_C HRESULT
454 BindCtx_RegisterObjectParam(
455     _In_ IBindCtx *pBindCtx,
456     _In_ LPOLESTR pszKey,
457     _In_opt_ IUnknown *punk,
458     _Out_ LPBC *ppbc)
459 {
460     HRESULT hr = S_OK;
461     CDummyOleWindow *pUnknown = NULL;
462 
463     *ppbc = pBindCtx;
464 
465     if (pBindCtx)
466     {
467         pBindCtx->AddRef();
468     }
469     else
470     {
471         hr = CreateBindCtx(0, ppbc);
472         if (FAILED(hr))
473             return hr;
474     }
475 
476     if (!punk)
477         punk = pUnknown = new CDummyOleWindow();
478 
479     hr = (*ppbc)->RegisterObjectParam(pszKey, punk);
480 
481     if (pUnknown)
482         pUnknown->Release();
483 
484     if (FAILED(hr))
485     {
486         (*ppbc)->Release();
487         *ppbc = NULL;
488     }
489 
490     return hr;
491 }
492 
493 /*************************************************************************
494  *                SHSetFolderPathA (SHELL32.231)
495  *
496  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha
497  */
498 EXTERN_C
499 HRESULT WINAPI
500 SHSetFolderPathA(
501     _In_ INT csidl,
502     _In_ HANDLE hToken,
503     _In_ DWORD dwFlags,
504     _In_ LPCSTR pszPath)
505 {
506     TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath));
507     CStringW strPathW(pszPath);
508     return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW);
509 }
510 
511 /*************************************************************************
512  *                PathIsSlowA (SHELL32.240)
513  *
514  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa
515  */
516 EXTERN_C
517 BOOL WINAPI
518 PathIsSlowA(
519     _In_ LPCSTR pszFile,
520     _In_ DWORD dwAttr)
521 {
522     TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr);
523     CStringW strFileW(pszFile);
524     return PathIsSlowW(strFileW, dwAttr);
525 }
526 
527 /*************************************************************************
528  *                ExtractIconResInfoA (SHELL32.221)
529  */
530 EXTERN_C
531 WORD WINAPI
532 ExtractIconResInfoA(
533     _In_ HANDLE hHandle,
534     _In_ LPCSTR lpFileName,
535     _In_ WORD wIndex,
536     _Out_ LPWORD lpSize,
537     _Out_ LPHANDLE lpIcon)
538 {
539     TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon);
540 
541     if (!lpFileName)
542         return 0;
543 
544     CStringW strFileNameW(lpFileName);
545     return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon);
546 }
547 
548 /*************************************************************************
549  *                ShortSizeFormatW (SHELL32.204)
550  */
551 EXTERN_C
552 LPWSTR WINAPI
553 ShortSizeFormatW(
554     _In_ DWORD dwNumber,
555     _Out_writes_(0x8FFF) LPWSTR pszBuffer)
556 {
557     TRACE("(%lu, %p)\n", dwNumber, pszBuffer);
558     return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF);
559 }
560 
561 /*************************************************************************
562  *                SHOpenEffectiveToken (SHELL32.235)
563  */
564 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken)
565 {
566     TRACE("%p\n", phToken);
567     return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken);
568 }
569 
570 /*************************************************************************
571  *                SHGetUserSessionId (SHELL32.248)
572  */
573 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken)
574 {
575     DWORD dwSessionId, dwLength;
576     BOOL bOpenToken = FALSE;
577 
578     TRACE("%p\n", hToken);
579 
580     if (!hToken)
581         bOpenToken = SHOpenEffectiveToken(&hToken);
582 
583     if (!hToken ||
584         !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength))
585     {
586         dwSessionId = 0;
587     }
588 
589     if (bOpenToken)
590         CloseHandle(hToken);
591 
592     return dwSessionId;
593 }
594 
595 /*************************************************************************
596  *                SHInvokePrivilegedFunctionW (SHELL32.246)
597  */
598 EXTERN_C
599 HRESULT WINAPI
600 SHInvokePrivilegedFunctionW(
601     _In_ LPCWSTR pszName,
602     _In_ PRIVILEGED_FUNCTION fn,
603     _In_opt_ LPARAM lParam)
604 {
605     TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam);
606 
607     if (!pszName || !fn)
608         return E_INVALIDARG;
609 
610     HANDLE hToken = NULL;
611     TOKEN_PRIVILEGES NewPriv, PrevPriv;
612     BOOL bAdjusted = FALSE;
613 
614     if (SHOpenEffectiveToken(&hToken) &&
615         ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid))
616     {
617         NewPriv.PrivilegeCount = 1;
618         NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
619 
620         DWORD dwReturnSize;
621         bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv,
622                                             sizeof(PrevPriv), &PrevPriv, &dwReturnSize);
623     }
624 
625     HRESULT hr = fn(lParam);
626 
627     if (bAdjusted)
628         ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL);
629 
630     if (hToken)
631         ::CloseHandle(hToken);
632 
633     return hr;
634 }
635 
636 /*************************************************************************
637  *                SHTestTokenPrivilegeW (SHELL32.236)
638  *
639  * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php
640  */
641 EXTERN_C
642 BOOL WINAPI
643 SHTestTokenPrivilegeW(
644     _In_opt_ HANDLE hToken,
645     _In_ LPCWSTR lpName)
646 {
647     LUID Luid;
648     DWORD dwLength;
649     PTOKEN_PRIVILEGES pTokenPriv;
650     HANDLE hNewToken = NULL;
651     BOOL ret = FALSE;
652 
653     TRACE("(%p, %s)\n", hToken, debugstr_w(lpName));
654 
655     if (!lpName)
656         return FALSE;
657 
658     if (!hToken)
659     {
660         if (!SHOpenEffectiveToken(&hNewToken))
661             goto Quit;
662 
663         if (!hNewToken)
664             return FALSE;
665 
666         hToken = hNewToken;
667     }
668 
669     if (!LookupPrivilegeValueW(NULL, lpName, &Luid))
670         return FALSE;
671 
672     dwLength = 0;
673     if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength))
674         goto Quit;
675 
676     pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength);
677     if (!pTokenPriv)
678         goto Quit;
679 
680     if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength))
681     {
682         UINT iPriv, cPrivs;
683         cPrivs = pTokenPriv->PrivilegeCount;
684         for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv)
685         {
686             ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid);
687         }
688     }
689 
690     LocalFree(pTokenPriv);
691 
692 Quit:
693     if (hToken == hNewToken)
694         CloseHandle(hNewToken);
695 
696     return ret;
697 }
698 
699 BOOL IsShutdownAllowed(VOID)
700 {
701     return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME);
702 }
703 
704 /*************************************************************************
705  *                IsSuspendAllowed (SHELL32.53)
706  */
707 BOOL WINAPI IsSuspendAllowed(VOID)
708 {
709     TRACE("()\n");
710     return IsShutdownAllowed() && IsPwrSuspendAllowed();
711 }
712 
713 /*************************************************************************
714  *                SHGetShellStyleHInstance (SHELL32.749)
715  */
716 EXTERN_C HINSTANCE
717 WINAPI
718 SHGetShellStyleHInstance(VOID)
719 {
720     HINSTANCE hInst = NULL;
721     WCHAR szPath[MAX_PATH], szColorName[100];
722     HRESULT hr;
723     CStringW strShellStyle;
724 
725     TRACE("SHGetShellStyleHInstance called\n");
726 
727     /* First, attempt to load the shellstyle dll from the current active theme */
728     hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0);
729     if (FAILED(hr))
730         goto DoDefault;
731 
732     /* Strip the theme filename */
733     PathRemoveFileSpecW(szPath);
734 
735     strShellStyle = szPath;
736     strShellStyle += L"\\Shell\\";
737     strShellStyle += szColorName;
738     strShellStyle += L"\\ShellStyle.dll";
739 
740     hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE);
741     if (hInst)
742         return hInst;
743 
744     /* Otherwise, use the version stored in the System32 directory */
745 DoDefault:
746     if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll",
747                                    szPath, _countof(szPath)))
748     {
749         ERR("Expand failed\n");
750         return NULL;
751     }
752     return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
753 }
754 
755 /*************************************************************************
756  *                SHCreatePropertyBag (SHELL32.715)
757  */
758 EXTERN_C HRESULT
759 WINAPI
760 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj)
761 {
762     return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj);
763 }
764 
765 /*************************************************************************
766  *                SheRemoveQuotesA (SHELL32.@)
767  */
768 EXTERN_C LPSTR
769 WINAPI
770 SheRemoveQuotesA(LPSTR psz)
771 {
772     PCHAR pch;
773 
774     if (*psz == '"')
775     {
776         for (pch = psz + 1; *pch && *pch != '"'; ++pch)
777         {
778             *(pch - 1) = *pch;
779         }
780 
781         if (*pch == '"')
782             *(pch - 1) = ANSI_NULL;
783     }
784 
785     return psz;
786 }
787 
788 /*************************************************************************
789  *                SheRemoveQuotesW (SHELL32.@)
790  *
791  * ExtractAssociatedIconExW uses this function.
792  */
793 EXTERN_C LPWSTR
794 WINAPI
795 SheRemoveQuotesW(LPWSTR psz)
796 {
797     PWCHAR pch;
798 
799     if (*psz == L'"')
800     {
801         for (pch = psz + 1; *pch && *pch != L'"'; ++pch)
802         {
803             *(pch - 1) = *pch;
804         }
805 
806         if (*pch == L'"')
807             *(pch - 1) = UNICODE_NULL;
808     }
809 
810     return psz;
811 }
812 
813 /*************************************************************************
814  *  SHFindComputer [SHELL32.91]
815  *
816  * Invokes the shell search in My Computer. Used in SHFindFiles.
817  * Two parameters are ignored.
818  */
819 EXTERN_C BOOL
820 WINAPI
821 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch)
822 {
823     UNREFERENCED_PARAMETER(pidlRoot);
824     UNREFERENCED_PARAMETER(pidlSavedSearch);
825 
826     TRACE("%p %p\n", pidlRoot, pidlSavedSearch);
827 
828     IContextMenu *pCM;
829     HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER,
830                                   IID_IContextMenu, (void **)&pCM);
831     if (FAILED(hr))
832     {
833         ERR("0x%08X\n", hr);
834         return hr;
835     }
836 
837     CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) };
838     InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}";
839     InvokeInfo.nShow = SW_SHOWNORMAL;
840     hr = pCM->InvokeCommand(&InvokeInfo);
841     pCM->Release();
842 
843     return SUCCEEDED(hr);
844 }
845 
846 static HRESULT
847 Int64ToStr(
848     _In_ LONGLONG llValue,
849     _Out_writes_(cchValue) LPWSTR pszValue,
850     _In_ UINT cchValue)
851 {
852     WCHAR szBuff[40];
853     UINT ich = 0, ichValue;
854 #if (WINVER >= _WIN32_WINNT_VISTA)
855     BOOL bMinus = (llValue < 0);
856 
857     if (bMinus)
858         llValue = -llValue;
859 #endif
860 
861     if (cchValue <= 0)
862         return E_FAIL;
863 
864     do
865     {
866         szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10));
867         llValue /= 10;
868     } while (llValue != 0 && ich < _countof(szBuff) - 1);
869 
870 #if (WINVER >= _WIN32_WINNT_VISTA)
871     if (bMinus && ich < _countof(szBuff))
872         szBuff[ich++] = '-';
873 #endif
874 
875     for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue)
876     {
877         --ich;
878         pszValue[ichValue] = szBuff[ich];
879     }
880 
881     if (ichValue >= cchValue)
882     {
883         pszValue[cchValue - 1] = UNICODE_NULL;
884         return E_FAIL;
885     }
886 
887     pszValue[ichValue] = UNICODE_NULL;
888     return S_OK;
889 }
890 
891 static VOID
892 Int64GetNumFormat(
893     _Out_ NUMBERFMTW *pDest,
894     _In_opt_ const NUMBERFMTW *pSrc,
895     _In_ DWORD dwNumberFlags,
896     _Out_writes_(cchDecimal) LPWSTR pszDecimal,
897     _In_ INT cchDecimal,
898     _Out_writes_(cchThousand) LPWSTR pszThousand,
899     _In_ INT cchThousand)
900 {
901     WCHAR szBuff[20];
902 
903     if (pSrc)
904         *pDest = *pSrc;
905     else
906         dwNumberFlags = 0;
907 
908     if (!(dwNumberFlags & FMT_USE_NUMDIGITS))
909     {
910         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff));
911         pDest->NumDigits = StrToIntW(szBuff);
912     }
913 
914     if (!(dwNumberFlags & FMT_USE_LEADZERO))
915     {
916         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff));
917         pDest->LeadingZero = StrToIntW(szBuff);
918     }
919 
920     if (!(dwNumberFlags & FMT_USE_GROUPING))
921     {
922         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff));
923         pDest->Grouping = StrToIntW(szBuff);
924     }
925 
926     if (!(dwNumberFlags & FMT_USE_DECIMAL))
927     {
928         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal);
929         pDest->lpDecimalSep = pszDecimal;
930     }
931 
932     if (!(dwNumberFlags & FMT_USE_THOUSAND))
933     {
934         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand);
935         pDest->lpThousandSep = pszThousand;
936     }
937 
938     if (!(dwNumberFlags & FMT_USE_NEGNUMBER))
939     {
940         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff));
941         pDest->NegativeOrder = StrToIntW(szBuff);
942     }
943 }
944 
945 /*************************************************************************
946  *  Int64ToString [SHELL32.209]
947  *
948  * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php
949  */
950 EXTERN_C
951 INT WINAPI
952 Int64ToString(
953     _In_ LONGLONG llValue,
954     _Out_writes_(cchOut) LPWSTR pszOut,
955     _In_ UINT cchOut,
956     _In_ BOOL bUseFormat,
957     _In_opt_ const NUMBERFMTW *pNumberFormat,
958     _In_ DWORD dwNumberFlags)
959 {
960     INT ret;
961     NUMBERFMTW NumFormat;
962     WCHAR szValue[80], szDecimalSep[6], szThousandSep[6];
963 
964     Int64ToStr(llValue, szValue, _countof(szValue));
965 
966     if (bUseFormat)
967     {
968         Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags,
969                           szDecimalSep, _countof(szDecimalSep),
970                           szThousandSep, _countof(szThousandSep));
971         ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut);
972         if (ret)
973             --ret;
974         return ret;
975     }
976 
977     if (FAILED(StringCchCopyW(pszOut, cchOut, szValue)))
978         return 0;
979 
980     return lstrlenW(pszOut);
981 }
982 
983 /*************************************************************************
984  *  LargeIntegerToString [SHELL32.210]
985  *
986  * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php
987  */
988 EXTERN_C
989 INT WINAPI
990 LargeIntegerToString(
991     _In_ const LARGE_INTEGER *pLargeInt,
992     _Out_writes_(cchOut) LPWSTR pszOut,
993     _In_ UINT cchOut,
994     _In_ BOOL bUseFormat,
995     _In_opt_ const NUMBERFMTW *pNumberFormat,
996     _In_ DWORD dwNumberFlags)
997 {
998     return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat,
999                          pNumberFormat, dwNumberFlags);
1000 }
1001 
1002 /*************************************************************************
1003  *  CopyStreamUI [SHELL32.726]
1004  *
1005  * Copy a stream to another stream with optional progress display.
1006  */
1007 EXTERN_C
1008 HRESULT WINAPI
1009 CopyStreamUI(
1010     _In_ IStream *pSrc,
1011     _Out_ IStream *pDst,
1012     _Inout_opt_ IProgressDialog *pProgress,
1013     _In_opt_ DWORDLONG dwlSize)
1014 {
1015     HRESULT hr = E_FAIL;
1016     DWORD cbBuff, cbRead, dwSizeToWrite;
1017     DWORDLONG cbDone;
1018     LPVOID pBuff;
1019     CComHeapPtr<BYTE> pHeapPtr;
1020     STATSTG Stat;
1021     BYTE abBuff[1024];
1022 
1023     TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize);
1024 
1025     if (dwlSize == 0) // Invalid size?
1026     {
1027         // Get the stream size
1028         ZeroMemory(&Stat, sizeof(Stat));
1029         if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME)))
1030             pProgress = NULL; // No size info. Disable progress
1031         else
1032             dwlSize = Stat.cbSize.QuadPart;
1033     }
1034 
1035     if (!pProgress) // Progress is disabled?
1036     {
1037         ULARGE_INTEGER uliSize;
1038 
1039         if (dwlSize > 0)
1040             uliSize.QuadPart = dwlSize;
1041         else
1042             uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE;
1043 
1044         return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch
1045     }
1046 
1047     // Allocate the buffer if necessary
1048     if (dwlSize > 0 && dwlSize <= sizeof(abBuff))
1049     {
1050         cbBuff = sizeof(abBuff);
1051         pBuff = abBuff;
1052     }
1053     else
1054     {
1055 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000
1056         cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE;
1057         if (pHeapPtr.AllocateBytes(cbBuff))
1058         {
1059             pBuff = pHeapPtr;
1060         }
1061         else // Low memory?
1062         {
1063             cbBuff = sizeof(abBuff);
1064             pBuff = abBuff;
1065         }
1066 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE
1067     }
1068 
1069     // Start reading
1070     LARGE_INTEGER zero;
1071     zero.QuadPart = 0;
1072     pSrc->Seek(zero, 0, NULL);
1073     pDst->Seek(zero, 0, NULL);
1074     cbDone = 0;
1075     pProgress->SetProgress64(cbDone, dwlSize);
1076 
1077     // Repeat reading and writing until goal
1078     for (;;)
1079     {
1080         hr = pSrc->Read(pBuff, cbBuff, &cbRead);
1081         if (FAILED(hr))
1082             break;
1083 
1084         // Calculate the size to write
1085         if (dwlSize > 0)
1086             dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead);
1087         else
1088             dwSizeToWrite = cbRead;
1089 
1090         if (dwSizeToWrite == 0) // No need to write?
1091         {
1092             hr = S_OK;
1093             break;
1094         }
1095 
1096         hr = pDst->Write(pBuff, dwSizeToWrite, NULL);
1097         if (hr != S_OK)
1098             break;
1099 
1100         cbDone += dwSizeToWrite;
1101 
1102         if (pProgress->HasUserCancelled()) // Cancelled?
1103         {
1104             hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
1105             break;
1106         }
1107         pProgress->SetProgress64(cbDone, dwlSize);
1108 
1109         if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal?
1110         {
1111             hr = S_OK;
1112             break;
1113         }
1114     }
1115 
1116     return hr;
1117 }
1118 
1119 /*************************************************************************
1120  *  SHOpenPropSheetA [SHELL32.707]
1121  *
1122  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta
1123  */
1124 EXTERN_C
1125 BOOL WINAPI
1126 SHOpenPropSheetA(
1127     _In_opt_ LPCSTR pszCaption,
1128     _In_opt_ HKEY *ahKeys,
1129     _In_ UINT cKeys,
1130     _In_ const CLSID *pclsidDefault,
1131     _In_ IDataObject *pDataObject,
1132     _In_opt_ IShellBrowser *pShellBrowser,
1133     _In_opt_ LPCSTR pszStartPage)
1134 {
1135     CStringW strStartPageW, strCaptionW;
1136     LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL;
1137 
1138     TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault,
1139           pDataObject, pShellBrowser, debugstr_a(pszStartPage));
1140 
1141     if (pszCaption)
1142     {
1143         strStartPageW = pszCaption;
1144         pszCaptionW = strCaptionW;
1145     }
1146 
1147     if (pszStartPage)
1148     {
1149         strStartPageW = pszStartPage;
1150         pszStartPageW = strStartPageW;
1151     }
1152 
1153     return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault,
1154                             pDataObject, pShellBrowser, pszStartPageW);
1155 }
1156 
1157 /*************************************************************************
1158  *  Activate_RunDLL [SHELL32.105]
1159  *
1160  * Unlocks the foreground window and allows the shell window to become the
1161  * foreground window. Every parameter is unused.
1162  */
1163 EXTERN_C
1164 BOOL WINAPI
1165 Activate_RunDLL(
1166     _In_ HWND hwnd,
1167     _In_ HINSTANCE hinst,
1168     _In_ LPCWSTR cmdline,
1169     _In_ INT cmdshow)
1170 {
1171     DWORD dwProcessID;
1172 
1173     UNREFERENCED_PARAMETER(hwnd);
1174     UNREFERENCED_PARAMETER(hinst);
1175     UNREFERENCED_PARAMETER(cmdline);
1176     UNREFERENCED_PARAMETER(cmdshow);
1177 
1178     TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline);
1179 
1180     GetWindowThreadProcessId(GetShellWindow(), &dwProcessID);
1181     return AllowSetForegroundWindow(dwProcessID);
1182 }
1183 
1184 /*************************************************************************
1185  *                SHStartNetConnectionDialogA (SHELL32.12)
1186  *
1187  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga
1188  */
1189 EXTERN_C
1190 HRESULT WINAPI
1191 SHStartNetConnectionDialogA(
1192     _In_ HWND hwnd,
1193     _In_ LPCSTR pszRemoteName,
1194     _In_ DWORD dwType)
1195 {
1196     LPCWSTR pszRemoteNameW = NULL;
1197     CStringW strRemoteNameW;
1198 
1199     TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType);
1200 
1201     if (pszRemoteName)
1202     {
1203         strRemoteNameW = pszRemoteName;
1204         pszRemoteNameW = strRemoteNameW;
1205     }
1206 
1207     return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType);
1208 }
1209 
1210 /*************************************************************************
1211  * Helper functions for PathIsEqualOrSubFolder
1212  */
1213 
1214 static INT
1215 DynamicPathCommonPrefixW(
1216     _In_ LPCWSTR lpszPath1,
1217     _In_ LPCWSTR lpszPath2,
1218     _Out_ CStringW& strPath)
1219 {
1220     SIZE_T cchPath1 = wcslen(lpszPath1);
1221     SIZE_T cchPath2 = wcslen(lpszPath2);
1222     LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16);
1223     INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath);
1224     strPath.ReleaseBuffer();
1225     return ret;
1226 }
1227 
1228 EXTERN_C HRESULT WINAPI
1229 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax);
1230 
1231 static HRESULT
1232 DynamicSHGetPathFromIDListW(
1233     _In_ LPCITEMIDLIST pidl,
1234     _Out_ CStringW& strPath)
1235 {
1236     HRESULT hr;
1237 
1238     for (UINT cchPath = MAX_PATH;; cchPath *= 2)
1239     {
1240         LPWSTR lpszPath = strPath.GetBuffer(cchPath);
1241         if (!lpszPath)
1242             return E_OUTOFMEMORY;
1243 
1244         hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath);
1245         strPath.ReleaseBuffer();
1246 
1247         if (hr != E_NOT_SUFFICIENT_BUFFER)
1248             break;
1249 
1250         if (cchPath >= MAXUINT / 2)
1251         {
1252             hr = E_FAIL;
1253             break;
1254         }
1255     }
1256 
1257     if (FAILED(hr))
1258         strPath.Empty();
1259 
1260     return hr;
1261 }
1262 
1263 static HRESULT
1264 DynamicSHGetSpecialFolderPathW(
1265     _In_ HWND hwndOwner,
1266     _Out_ CStringW& strPath,
1267     _In_ INT nCSIDL,
1268     _In_ BOOL bCreate)
1269 {
1270     LPITEMIDLIST pidl;
1271     HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl);
1272     if (SUCCEEDED(hr))
1273     {
1274         hr = DynamicSHGetPathFromIDListW(pidl, strPath);
1275         CoTaskMemFree(pidl);
1276     }
1277 
1278     if (FAILED(hr))
1279         strPath.Empty();
1280     else if (bCreate)
1281         CreateDirectoryW(strPath, NULL);
1282 
1283     return hr;
1284 }
1285 
1286 static VOID
1287 DynamicPathRemoveBackslashW(
1288     _Out_ CStringW& strPath)
1289 {
1290     INT nLength = strPath.GetLength();
1291     if (nLength > 0 && strPath[nLength - 1] == L'\\')
1292         strPath = strPath.Left(nLength - 1);
1293 }
1294 
1295 /*************************************************************************
1296  *                PathIsEqualOrSubFolder (SHELL32.755)
1297  */
1298 EXTERN_C
1299 BOOL WINAPI
1300 PathIsEqualOrSubFolder(
1301     _In_ LPCWSTR pszPath1OrCSIDL,
1302     _In_ LPCWSTR pszPath2)
1303 {
1304     CStringW strCommon, strPath1;
1305 
1306     TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2));
1307 
1308     if (IS_INTRESOURCE(pszPath1OrCSIDL))
1309     {
1310         DynamicSHGetSpecialFolderPathW(
1311             NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE);
1312     }
1313     else
1314     {
1315         strPath1 = pszPath1OrCSIDL;
1316     }
1317 
1318     DynamicPathRemoveBackslashW(strPath1);
1319 
1320     if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon))
1321         return FALSE;
1322 
1323     return strPath1.CompareNoCase(strCommon) == 0;
1324 }
1325 
1326 /*************************************************************************
1327  *  SHGetRealIDL [SHELL32.98]
1328  */
1329 EXTERN_C
1330 HRESULT WINAPI
1331 SHGetRealIDL(
1332     _In_ IShellFolder *psf,
1333     _In_ PCUITEMID_CHILD pidlSimple,
1334     _Outptr_ PITEMID_CHILD *ppidlReal)
1335 {
1336     HRESULT hr;
1337     STRRET strret;
1338     WCHAR szPath[MAX_PATH];
1339     SFGAOF attrs;
1340 
1341     *ppidlReal = NULL;
1342 
1343     hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_INFOLDER | SHGDN_FORPARSING,
1344                                        &strret, 0);
1345     if (FAILED_UNEXPECTEDLY(hr))
1346         return hr;
1347 
1348     hr = StrRetToBufW(&strret, pidlSimple, szPath, _countof(szPath));
1349     if (FAILED_UNEXPECTEDLY(hr))
1350         return hr;
1351 
1352     attrs = SFGAO_FILESYSTEM;
1353     hr = psf->GetAttributesOf(1, &pidlSimple, &attrs);
1354     if (SUCCEEDED(hr) && !(attrs & SFGAO_FILESYSTEM))
1355         return SHILClone(pidlSimple, ppidlReal);
1356 
1357     hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL);
1358     if (hr == E_INVALIDARG || hr == E_NOTIMPL)
1359         return SHILClone(pidlSimple, ppidlReal);
1360 
1361     return hr;
1362 }
1363 
1364 EXTERN_C HRESULT
1365 IUnknown_InitializeCommand(
1366     _In_ IUnknown *pUnk,
1367     _In_ PCWSTR pszCommandName,
1368     _In_opt_ IPropertyBag *pPB)
1369 {
1370     HRESULT hr;
1371     CComPtr<IInitializeCommand> pIC;
1372     if (SUCCEEDED(hr = pUnk->QueryInterface(IID_PPV_ARG(IInitializeCommand, &pIC))))
1373         hr = pIC->Initialize(pszCommandName, pPB);
1374     return hr;
1375 }
1376 
1377 EXTERN_C HRESULT
1378 InvokeIExecuteCommand(
1379     _In_ IExecuteCommand *pEC,
1380     _In_ PCWSTR pszCommandName,
1381     _In_opt_ IPropertyBag *pPB,
1382     _In_opt_ IShellItemArray *pSIA,
1383     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1384     _In_opt_ IUnknown *pSite)
1385 {
1386     if (!pEC)
1387         return E_INVALIDARG;
1388 
1389     if (pSite)
1390         IUnknown_SetSite(pEC, pSite);
1391     IUnknown_InitializeCommand(pEC, pszCommandName, pPB);
1392 
1393     CComPtr<IObjectWithSelection> pOWS;
1394     if (pSIA && SUCCEEDED(pEC->QueryInterface(IID_PPV_ARG(IObjectWithSelection, &pOWS))))
1395         pOWS->SetSelection(pSIA);
1396 
1397     DWORD dwKeyState = 0, fMask = pICI ? pICI->fMask : 0;
1398     pEC->SetNoShowUI((fMask & CMIC_MASK_FLAG_NO_UI) != 0);
1399     pEC->SetShowWindow(pICI ? pICI->nShow : SW_SHOW);
1400     if (fMask & CMIC_MASK_SHIFT_DOWN)
1401         dwKeyState |= MK_SHIFT;
1402     if (fMask & CMIC_MASK_CONTROL_DOWN)
1403         dwKeyState |= MK_CONTROL;
1404     pEC->SetKeyState(dwKeyState);
1405     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpDirectoryW)
1406         pEC->SetDirectory(pICI->lpDirectoryW);
1407     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpParametersW)
1408         pEC->SetParameters(pICI->lpParametersW);
1409     if (fMask & CMIC_MASK_PTINVOKE)
1410         pEC->SetPosition(pICI->ptInvoke);
1411 
1412     HRESULT hr = pEC->Execute();
1413     if (pSite)
1414         IUnknown_SetSite(pEC, NULL);
1415     return hr;
1416 }
1417 
1418 EXTERN_C HRESULT
1419 InvokeIExecuteCommandWithDataObject(
1420     _In_ IExecuteCommand *pEC,
1421     _In_ PCWSTR pszCommandName,
1422     _In_opt_ IPropertyBag *pPB,
1423     _In_ IDataObject *pDO,
1424     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1425     _In_opt_ IUnknown *pSite)
1426 {
1427     CComPtr<IShellItemArray> pSIA;
1428     HRESULT hr = SHCreateShellItemArrayFromDataObject(pDO, IID_PPV_ARG(IShellItemArray, &pSIA));
1429     return SUCCEEDED(hr) ? InvokeIExecuteCommand(pEC, pszCommandName, pPB, pSIA, pICI, pSite) : hr;
1430 }
1431 
1432 static HRESULT
1433 GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax)
1434 {
1435     HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax);
1436     if (FAILED(hr))
1437     {
1438         WCHAR buf[MAX_PATH];
1439         hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf));
1440         if (SUCCEEDED(hr))
1441             hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL;
1442     }
1443     return hr;
1444 }
1445 
1446 UINT
1447 GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba)
1448 {
1449     CHAR buf[MAX_PATH];
1450     if (IS_INTRESOURCE(verba))
1451     {
1452         if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf))))
1453             return 0;
1454         verba = buf;
1455     }
1456     return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0
1457 }
1458 
1459 HRESULT
1460 SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap)
1461 {
1462     LPCSTR pVerbA = pICI->lpVerb;
1463     CHAR buf[MAX_PATH];
1464     LPCMINVOKECOMMANDINFOEX pICIX = (LPCMINVOKECOMMANDINFOEX)pICI;
1465     if (IsUnicode(*pICIX) && !IS_INTRESOURCE(pICIX->lpVerbW))
1466     {
1467         if (SHUnicodeToAnsi(pICIX->lpVerbW, buf, _countof(buf)))
1468             pVerbA = buf;
1469     }
1470 
1471     if (IS_INTRESOURCE(pVerbA))
1472         return LOWORD(pVerbA);
1473     for (SIZE_T i = 0; pMap[i].Verb; ++i)
1474     {
1475         assert(SUCCEEDED((int)(pMap[i].CmdId))); // The id must be >= 0 and ideally in the 0..0x7fff range
1476         if (!lstrcmpiA(pMap[i].Verb, pVerbA) && pVerbA[0])
1477             return pMap[i].CmdId;
1478     }
1479     return E_FAIL;
1480 }
1481 
1482 static const CMVERBMAP*
1483 FindVerbMapEntry(UINT_PTR CmdId, const CMVERBMAP *pMap)
1484 {
1485     for (SIZE_T i = 0; pMap[i].Verb; ++i)
1486     {
1487         if (pMap[i].CmdId == CmdId)
1488             return &pMap[i];
1489     }
1490     return NULL;
1491 }
1492 
1493 HRESULT
1494 SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const CMVERBMAP *pMap)
1495 {
1496     const CMVERBMAP* pEntry;
1497     switch (uFlags | GCS_UNICODE)
1498     {
1499         case GCS_VALIDATEW:
1500         case GCS_VERBW:
1501             pEntry = FindVerbMapEntry(CmdId, pMap);
1502             if ((uFlags | GCS_UNICODE) == GCS_VERBW)
1503             {
1504                 if (!pEntry)
1505                     return E_INVALIDARG;
1506                 else if (uFlags & GCS_UNICODE)
1507                     return SHAnsiToUnicode(pEntry->Verb, (LPWSTR)Buf, cchBuf) ? S_OK : E_FAIL;
1508                 else
1509                     return StringCchCopyA(Buf, cchBuf, pEntry->Verb);
1510             }
1511             return pEntry ? S_OK : S_FALSE; // GCS_VALIDATE
1512     }
1513     return E_NOTIMPL;
1514 }
1515 
1516 HRESULT
1517 SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut)
1518 {
1519     CComPtr<IDefaultExtractIconInit> initIcon;
1520     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
1521     if (FAILED_UNEXPECTEDLY(hr))
1522         return hr;
1523     initIcon->SetNormalIcon(swShell32Name, IconIndex);
1524     return initIcon->QueryInterface(riid, ppvOut);
1525 }
1526