xref: /reactos/dll/win32/shell32/utils.cpp (revision 3bd9ddca)
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 HRESULT
263 Shell_DisplayNameOf(
264     _In_ IShellFolder *psf,
265     _In_ LPCITEMIDLIST pidl,
266     _In_ DWORD dwFlags,
267     _Out_ LPWSTR pszBuf,
268     _In_ UINT cchBuf)
269 {
270     *pszBuf = UNICODE_NULL;
271     STRRET sr;
272     HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr);
273     if (FAILED(hr))
274         return hr;
275     return StrRetToBufW(&sr, pidl, pszBuf, cchBuf);
276 }
277 
278 DWORD
279 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes)
280 {
281     LPCITEMIDLIST pidlLast = pidl;
282     IShellFolder *release = NULL;
283 
284     if (!psf)
285     {
286         SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
287         if (!psf)
288             return 0;
289         release = psf;
290     }
291 
292     DWORD oldAttrs = dwAttributes;
293     if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes)))
294         dwAttributes = 0;
295     else
296         dwAttributes &= oldAttrs;
297 
298     if ((dwAttributes & SFGAO_FOLDER) &&
299         (dwAttributes & SFGAO_STREAM) &&
300         !(dwAttributes & SFGAO_STORAGEANCESTOR) &&
301         (oldAttrs & SFGAO_STORAGEANCESTOR) &&
302         (SHGetObjectCompatFlags(psf, NULL) & 0x200))
303     {
304         dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR);
305         dwAttributes |= SFGAO_STORAGEANCESTOR;
306     }
307 
308     if (release)
309         release->Release();
310     return dwAttributes;
311 }
312 
313 HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl)
314 {
315     IShellLink *pSL;
316     HRESULT hr = SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellLink, &pSL));
317     if (SUCCEEDED(hr))
318     {
319         hr = pSL->GetIDList(ppidl); // Note: Returns S_FALSE if no target pidl
320         pSL->Release();
321     }
322     return hr;
323 }
324 
325 HRESULT SHCoInitializeAnyApartment(VOID)
326 {
327     HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
328     if (FAILED(hr))
329         hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE);
330     return hr;
331 }
332 
333 HRESULT
334 SHGetNameAndFlagsW(
335     _In_ LPCITEMIDLIST pidl,
336     _In_ DWORD dwFlags,
337     _Out_opt_ LPWSTR pszText,
338     _In_ UINT cchBuf,
339     _Inout_opt_ DWORD *pdwAttributes)
340 {
341     if (pszText)
342         *pszText = UNICODE_NULL;
343 
344     HRESULT hrCoInit = SHCoInitializeAnyApartment();
345 
346     CComPtr<IShellFolder> psfFolder;
347     LPCITEMIDLIST ppidlLast;
348     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast);
349     if (SUCCEEDED(hr))
350     {
351         if (pszText)
352             hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf);
353 
354         if (SUCCEEDED(hr))
355         {
356             if (pdwAttributes)
357                 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes);
358         }
359     }
360 
361     if (SUCCEEDED(hrCoInit))
362         CoUninitialize();
363 
364     return hr;
365 }
366 
367 EXTERN_C HWND
368 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx)
369 {
370     HWND hWnd = NULL;
371 
372     CComPtr<IUnknown> punk;
373     if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk)))
374         IUnknown_GetWindow(punk, &hWnd);
375 
376     return hWnd;
377 }
378 
379 class CDummyOleWindow : public IOleWindow
380 {
381 protected:
382     LONG m_cRefs;
383     HWND m_hWnd;
384 
385 public:
386     CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { }
387     virtual ~CDummyOleWindow() { }
388 
389     // IUnknown methods
390     STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override
391     {
392         static const QITAB c_tab[] =
393         {
394             QITABENT(CDummyOleWindow, IOleWindow),
395             { NULL }
396         };
397         return ::QISearch(this, c_tab, riid, ppvObj);
398     }
399     STDMETHODIMP_(ULONG) AddRef() override
400     {
401         return ++m_cRefs;
402     }
403     STDMETHODIMP_(ULONG) Release() override
404     {
405         if (--m_cRefs == 0)
406         {
407             delete this;
408             return 0;
409         }
410         return m_cRefs;
411     }
412 
413     // IOleWindow methods
414     STDMETHODIMP GetWindow(HWND *phWnd) override
415     {
416         *phWnd = m_hWnd;
417         if (!m_hWnd)
418             return E_NOTIMPL;
419         return S_OK;
420     }
421     STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override
422     {
423         return E_NOTIMPL;
424     }
425 };
426 
427 EXTERN_C HRESULT
428 BindCtx_RegisterObjectParam(
429     _In_ IBindCtx *pBindCtx,
430     _In_ LPOLESTR pszKey,
431     _In_opt_ IUnknown *punk,
432     _Out_ LPBC *ppbc)
433 {
434     HRESULT hr = S_OK;
435     CDummyOleWindow *pUnknown = NULL;
436 
437     *ppbc = pBindCtx;
438 
439     if (pBindCtx)
440     {
441         pBindCtx->AddRef();
442     }
443     else
444     {
445         hr = CreateBindCtx(0, ppbc);
446         if (FAILED(hr))
447             return hr;
448     }
449 
450     if (!punk)
451         punk = pUnknown = new CDummyOleWindow();
452 
453     hr = (*ppbc)->RegisterObjectParam(pszKey, punk);
454 
455     if (pUnknown)
456         pUnknown->Release();
457 
458     if (FAILED(hr))
459     {
460         (*ppbc)->Release();
461         *ppbc = NULL;
462     }
463 
464     return hr;
465 }
466 
467 /*************************************************************************
468  *                SHSetFolderPathA (SHELL32.231)
469  *
470  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha
471  */
472 EXTERN_C
473 HRESULT WINAPI
474 SHSetFolderPathA(
475     _In_ INT csidl,
476     _In_ HANDLE hToken,
477     _In_ DWORD dwFlags,
478     _In_ LPCSTR pszPath)
479 {
480     TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath));
481     CStringW strPathW(pszPath);
482     return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW);
483 }
484 
485 /*************************************************************************
486  *                PathIsSlowA (SHELL32.240)
487  *
488  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa
489  */
490 EXTERN_C
491 BOOL WINAPI
492 PathIsSlowA(
493     _In_ LPCSTR pszFile,
494     _In_ DWORD dwAttr)
495 {
496     TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr);
497     CStringW strFileW(pszFile);
498     return PathIsSlowW(strFileW, dwAttr);
499 }
500 
501 /*************************************************************************
502  *                ExtractIconResInfoA (SHELL32.221)
503  */
504 EXTERN_C
505 WORD WINAPI
506 ExtractIconResInfoA(
507     _In_ HANDLE hHandle,
508     _In_ LPCSTR lpFileName,
509     _In_ WORD wIndex,
510     _Out_ LPWORD lpSize,
511     _Out_ LPHANDLE lpIcon)
512 {
513     TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon);
514 
515     if (!lpFileName)
516         return 0;
517 
518     CStringW strFileNameW(lpFileName);
519     return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon);
520 }
521 
522 /*************************************************************************
523  *                ShortSizeFormatW (SHELL32.204)
524  */
525 EXTERN_C
526 LPWSTR WINAPI
527 ShortSizeFormatW(
528     _In_ DWORD dwNumber,
529     _Out_writes_(0x8FFF) LPWSTR pszBuffer)
530 {
531     TRACE("(%lu, %p)\n", dwNumber, pszBuffer);
532     return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF);
533 }
534 
535 /*************************************************************************
536  *                SHOpenEffectiveToken (SHELL32.235)
537  */
538 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken)
539 {
540     TRACE("%p\n", phToken);
541     return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken);
542 }
543 
544 /*************************************************************************
545  *                SHGetUserSessionId (SHELL32.248)
546  */
547 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken)
548 {
549     DWORD dwSessionId, dwLength;
550     BOOL bOpenToken = FALSE;
551 
552     TRACE("%p\n", hToken);
553 
554     if (!hToken)
555         bOpenToken = SHOpenEffectiveToken(&hToken);
556 
557     if (!hToken ||
558         !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength))
559     {
560         dwSessionId = 0;
561     }
562 
563     if (bOpenToken)
564         CloseHandle(hToken);
565 
566     return dwSessionId;
567 }
568 
569 /*************************************************************************
570  *                SHInvokePrivilegedFunctionW (SHELL32.246)
571  */
572 EXTERN_C
573 HRESULT WINAPI
574 SHInvokePrivilegedFunctionW(
575     _In_ LPCWSTR pszName,
576     _In_ PRIVILEGED_FUNCTION fn,
577     _In_opt_ LPARAM lParam)
578 {
579     TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam);
580 
581     if (!pszName || !fn)
582         return E_INVALIDARG;
583 
584     HANDLE hToken = NULL;
585     TOKEN_PRIVILEGES NewPriv, PrevPriv;
586     BOOL bAdjusted = FALSE;
587 
588     if (SHOpenEffectiveToken(&hToken) &&
589         ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid))
590     {
591         NewPriv.PrivilegeCount = 1;
592         NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
593 
594         DWORD dwReturnSize;
595         bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv,
596                                             sizeof(PrevPriv), &PrevPriv, &dwReturnSize);
597     }
598 
599     HRESULT hr = fn(lParam);
600 
601     if (bAdjusted)
602         ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL);
603 
604     if (hToken)
605         ::CloseHandle(hToken);
606 
607     return hr;
608 }
609 
610 /*************************************************************************
611  *                SHTestTokenPrivilegeW (SHELL32.236)
612  *
613  * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php
614  */
615 EXTERN_C
616 BOOL WINAPI
617 SHTestTokenPrivilegeW(
618     _In_opt_ HANDLE hToken,
619     _In_ LPCWSTR lpName)
620 {
621     LUID Luid;
622     DWORD dwLength;
623     PTOKEN_PRIVILEGES pTokenPriv;
624     HANDLE hNewToken = NULL;
625     BOOL ret = FALSE;
626 
627     TRACE("(%p, %s)\n", hToken, debugstr_w(lpName));
628 
629     if (!lpName)
630         return FALSE;
631 
632     if (!hToken)
633     {
634         if (!SHOpenEffectiveToken(&hNewToken))
635             goto Quit;
636 
637         if (!hNewToken)
638             return FALSE;
639 
640         hToken = hNewToken;
641     }
642 
643     if (!LookupPrivilegeValueW(NULL, lpName, &Luid))
644         return FALSE;
645 
646     dwLength = 0;
647     if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength))
648         goto Quit;
649 
650     pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength);
651     if (!pTokenPriv)
652         goto Quit;
653 
654     if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength))
655     {
656         UINT iPriv, cPrivs;
657         cPrivs = pTokenPriv->PrivilegeCount;
658         for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv)
659         {
660             ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid);
661         }
662     }
663 
664     LocalFree(pTokenPriv);
665 
666 Quit:
667     if (hToken == hNewToken)
668         CloseHandle(hNewToken);
669 
670     return ret;
671 }
672 
673 BOOL IsShutdownAllowed(VOID)
674 {
675     return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME);
676 }
677 
678 /*************************************************************************
679  *                IsSuspendAllowed (SHELL32.53)
680  */
681 BOOL WINAPI IsSuspendAllowed(VOID)
682 {
683     TRACE("()\n");
684     return IsShutdownAllowed() && IsPwrSuspendAllowed();
685 }
686 
687 /*************************************************************************
688  *                SHGetShellStyleHInstance (SHELL32.749)
689  */
690 EXTERN_C HINSTANCE
691 WINAPI
692 SHGetShellStyleHInstance(VOID)
693 {
694     HINSTANCE hInst = NULL;
695     WCHAR szPath[MAX_PATH], szColorName[100];
696     HRESULT hr;
697     CStringW strShellStyle;
698 
699     TRACE("SHGetShellStyleHInstance called\n");
700 
701     /* First, attempt to load the shellstyle dll from the current active theme */
702     hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0);
703     if (FAILED(hr))
704         goto DoDefault;
705 
706     /* Strip the theme filename */
707     PathRemoveFileSpecW(szPath);
708 
709     strShellStyle = szPath;
710     strShellStyle += L"\\Shell\\";
711     strShellStyle += szColorName;
712     strShellStyle += L"\\ShellStyle.dll";
713 
714     hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE);
715     if (hInst)
716         return hInst;
717 
718     /* Otherwise, use the version stored in the System32 directory */
719 DoDefault:
720     if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll",
721                                    szPath, _countof(szPath)))
722     {
723         ERR("Expand failed\n");
724         return NULL;
725     }
726     return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
727 }
728 
729 /*************************************************************************
730  *                SHCreatePropertyBag (SHELL32.715)
731  */
732 EXTERN_C HRESULT
733 WINAPI
734 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj)
735 {
736     return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj);
737 }
738 
739 /*************************************************************************
740  *                SheRemoveQuotesA (SHELL32.@)
741  */
742 EXTERN_C LPSTR
743 WINAPI
744 SheRemoveQuotesA(LPSTR psz)
745 {
746     PCHAR pch;
747 
748     if (*psz == '"')
749     {
750         for (pch = psz + 1; *pch && *pch != '"'; ++pch)
751         {
752             *(pch - 1) = *pch;
753         }
754 
755         if (*pch == '"')
756             *(pch - 1) = ANSI_NULL;
757     }
758 
759     return psz;
760 }
761 
762 /*************************************************************************
763  *                SheRemoveQuotesW (SHELL32.@)
764  *
765  * ExtractAssociatedIconExW uses this function.
766  */
767 EXTERN_C LPWSTR
768 WINAPI
769 SheRemoveQuotesW(LPWSTR psz)
770 {
771     PWCHAR pch;
772 
773     if (*psz == L'"')
774     {
775         for (pch = psz + 1; *pch && *pch != L'"'; ++pch)
776         {
777             *(pch - 1) = *pch;
778         }
779 
780         if (*pch == L'"')
781             *(pch - 1) = UNICODE_NULL;
782     }
783 
784     return psz;
785 }
786 
787 /*************************************************************************
788  *  SHFindComputer [SHELL32.91]
789  *
790  * Invokes the shell search in My Computer. Used in SHFindFiles.
791  * Two parameters are ignored.
792  */
793 EXTERN_C BOOL
794 WINAPI
795 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch)
796 {
797     UNREFERENCED_PARAMETER(pidlRoot);
798     UNREFERENCED_PARAMETER(pidlSavedSearch);
799 
800     TRACE("%p %p\n", pidlRoot, pidlSavedSearch);
801 
802     IContextMenu *pCM;
803     HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER,
804                                   IID_IContextMenu, (void **)&pCM);
805     if (FAILED(hr))
806     {
807         ERR("0x%08X\n", hr);
808         return hr;
809     }
810 
811     CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) };
812     InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}";
813     InvokeInfo.nShow = SW_SHOWNORMAL;
814     hr = pCM->InvokeCommand(&InvokeInfo);
815     pCM->Release();
816 
817     return SUCCEEDED(hr);
818 }
819 
820 static HRESULT
821 Int64ToStr(
822     _In_ LONGLONG llValue,
823     _Out_writes_(cchValue) LPWSTR pszValue,
824     _In_ UINT cchValue)
825 {
826     WCHAR szBuff[40];
827     UINT ich = 0, ichValue;
828 #if (WINVER >= _WIN32_WINNT_VISTA)
829     BOOL bMinus = (llValue < 0);
830 
831     if (bMinus)
832         llValue = -llValue;
833 #endif
834 
835     if (cchValue <= 0)
836         return E_FAIL;
837 
838     do
839     {
840         szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10));
841         llValue /= 10;
842     } while (llValue != 0 && ich < _countof(szBuff) - 1);
843 
844 #if (WINVER >= _WIN32_WINNT_VISTA)
845     if (bMinus && ich < _countof(szBuff))
846         szBuff[ich++] = '-';
847 #endif
848 
849     for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue)
850     {
851         --ich;
852         pszValue[ichValue] = szBuff[ich];
853     }
854 
855     if (ichValue >= cchValue)
856     {
857         pszValue[cchValue - 1] = UNICODE_NULL;
858         return E_FAIL;
859     }
860 
861     pszValue[ichValue] = UNICODE_NULL;
862     return S_OK;
863 }
864 
865 static VOID
866 Int64GetNumFormat(
867     _Out_ NUMBERFMTW *pDest,
868     _In_opt_ const NUMBERFMTW *pSrc,
869     _In_ DWORD dwNumberFlags,
870     _Out_writes_(cchDecimal) LPWSTR pszDecimal,
871     _In_ INT cchDecimal,
872     _Out_writes_(cchThousand) LPWSTR pszThousand,
873     _In_ INT cchThousand)
874 {
875     WCHAR szBuff[20];
876 
877     if (pSrc)
878         *pDest = *pSrc;
879     else
880         dwNumberFlags = 0;
881 
882     if (!(dwNumberFlags & FMT_USE_NUMDIGITS))
883     {
884         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff));
885         pDest->NumDigits = StrToIntW(szBuff);
886     }
887 
888     if (!(dwNumberFlags & FMT_USE_LEADZERO))
889     {
890         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff));
891         pDest->LeadingZero = StrToIntW(szBuff);
892     }
893 
894     if (!(dwNumberFlags & FMT_USE_GROUPING))
895     {
896         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff));
897         pDest->Grouping = StrToIntW(szBuff);
898     }
899 
900     if (!(dwNumberFlags & FMT_USE_DECIMAL))
901     {
902         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal);
903         pDest->lpDecimalSep = pszDecimal;
904     }
905 
906     if (!(dwNumberFlags & FMT_USE_THOUSAND))
907     {
908         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand);
909         pDest->lpThousandSep = pszThousand;
910     }
911 
912     if (!(dwNumberFlags & FMT_USE_NEGNUMBER))
913     {
914         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff));
915         pDest->NegativeOrder = StrToIntW(szBuff);
916     }
917 }
918 
919 /*************************************************************************
920  *  Int64ToString [SHELL32.209]
921  *
922  * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php
923  */
924 EXTERN_C
925 INT WINAPI
926 Int64ToString(
927     _In_ LONGLONG llValue,
928     _Out_writes_(cchOut) LPWSTR pszOut,
929     _In_ UINT cchOut,
930     _In_ BOOL bUseFormat,
931     _In_opt_ const NUMBERFMTW *pNumberFormat,
932     _In_ DWORD dwNumberFlags)
933 {
934     INT ret;
935     NUMBERFMTW NumFormat;
936     WCHAR szValue[80], szDecimalSep[6], szThousandSep[6];
937 
938     Int64ToStr(llValue, szValue, _countof(szValue));
939 
940     if (bUseFormat)
941     {
942         Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags,
943                           szDecimalSep, _countof(szDecimalSep),
944                           szThousandSep, _countof(szThousandSep));
945         ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut);
946         if (ret)
947             --ret;
948         return ret;
949     }
950 
951     if (FAILED(StringCchCopyW(pszOut, cchOut, szValue)))
952         return 0;
953 
954     return lstrlenW(pszOut);
955 }
956 
957 /*************************************************************************
958  *  LargeIntegerToString [SHELL32.210]
959  *
960  * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php
961  */
962 EXTERN_C
963 INT WINAPI
964 LargeIntegerToString(
965     _In_ const LARGE_INTEGER *pLargeInt,
966     _Out_writes_(cchOut) LPWSTR pszOut,
967     _In_ UINT cchOut,
968     _In_ BOOL bUseFormat,
969     _In_opt_ const NUMBERFMTW *pNumberFormat,
970     _In_ DWORD dwNumberFlags)
971 {
972     return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat,
973                          pNumberFormat, dwNumberFlags);
974 }
975 
976 /*************************************************************************
977  *  CopyStreamUI [SHELL32.726]
978  *
979  * Copy a stream to another stream with optional progress display.
980  */
981 EXTERN_C
982 HRESULT WINAPI
983 CopyStreamUI(
984     _In_ IStream *pSrc,
985     _Out_ IStream *pDst,
986     _Inout_opt_ IProgressDialog *pProgress,
987     _In_opt_ DWORDLONG dwlSize)
988 {
989     HRESULT hr = E_FAIL;
990     DWORD cbBuff, cbRead, dwSizeToWrite;
991     DWORDLONG cbDone;
992     LPVOID pBuff;
993     CComHeapPtr<BYTE> pHeapPtr;
994     STATSTG Stat;
995     BYTE abBuff[1024];
996 
997     TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize);
998 
999     if (dwlSize == 0) // Invalid size?
1000     {
1001         // Get the stream size
1002         ZeroMemory(&Stat, sizeof(Stat));
1003         if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME)))
1004             pProgress = NULL; // No size info. Disable progress
1005         else
1006             dwlSize = Stat.cbSize.QuadPart;
1007     }
1008 
1009     if (!pProgress) // Progress is disabled?
1010     {
1011         ULARGE_INTEGER uliSize;
1012 
1013         if (dwlSize > 0)
1014             uliSize.QuadPart = dwlSize;
1015         else
1016             uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE;
1017 
1018         return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch
1019     }
1020 
1021     // Allocate the buffer if necessary
1022     if (dwlSize > 0 && dwlSize <= sizeof(abBuff))
1023     {
1024         cbBuff = sizeof(abBuff);
1025         pBuff = abBuff;
1026     }
1027     else
1028     {
1029 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000
1030         cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE;
1031         if (pHeapPtr.AllocateBytes(cbBuff))
1032         {
1033             pBuff = pHeapPtr;
1034         }
1035         else // Low memory?
1036         {
1037             cbBuff = sizeof(abBuff);
1038             pBuff = abBuff;
1039         }
1040 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE
1041     }
1042 
1043     // Start reading
1044     LARGE_INTEGER zero;
1045     zero.QuadPart = 0;
1046     pSrc->Seek(zero, 0, NULL);
1047     pDst->Seek(zero, 0, NULL);
1048     cbDone = 0;
1049     pProgress->SetProgress64(cbDone, dwlSize);
1050 
1051     // Repeat reading and writing until goal
1052     for (;;)
1053     {
1054         hr = pSrc->Read(pBuff, cbBuff, &cbRead);
1055         if (FAILED(hr))
1056             break;
1057 
1058         // Calculate the size to write
1059         if (dwlSize > 0)
1060             dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead);
1061         else
1062             dwSizeToWrite = cbRead;
1063 
1064         if (dwSizeToWrite == 0) // No need to write?
1065         {
1066             hr = S_OK;
1067             break;
1068         }
1069 
1070         hr = pDst->Write(pBuff, dwSizeToWrite, NULL);
1071         if (hr != S_OK)
1072             break;
1073 
1074         cbDone += dwSizeToWrite;
1075 
1076         if (pProgress->HasUserCancelled()) // Cancelled?
1077         {
1078             hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
1079             break;
1080         }
1081         pProgress->SetProgress64(cbDone, dwlSize);
1082 
1083         if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal?
1084         {
1085             hr = S_OK;
1086             break;
1087         }
1088     }
1089 
1090     return hr;
1091 }
1092 
1093 /*************************************************************************
1094  *  SHOpenPropSheetA [SHELL32.707]
1095  *
1096  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta
1097  */
1098 EXTERN_C
1099 BOOL WINAPI
1100 SHOpenPropSheetA(
1101     _In_opt_ LPCSTR pszCaption,
1102     _In_opt_ HKEY *ahKeys,
1103     _In_ UINT cKeys,
1104     _In_ const CLSID *pclsidDefault,
1105     _In_ IDataObject *pDataObject,
1106     _In_opt_ IShellBrowser *pShellBrowser,
1107     _In_opt_ LPCSTR pszStartPage)
1108 {
1109     CStringW strStartPageW, strCaptionW;
1110     LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL;
1111 
1112     TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault,
1113           pDataObject, pShellBrowser, debugstr_a(pszStartPage));
1114 
1115     if (pszCaption)
1116     {
1117         strStartPageW = pszCaption;
1118         pszCaptionW = strCaptionW;
1119     }
1120 
1121     if (pszStartPage)
1122     {
1123         strStartPageW = pszStartPage;
1124         pszStartPageW = strStartPageW;
1125     }
1126 
1127     return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault,
1128                             pDataObject, pShellBrowser, pszStartPageW);
1129 }
1130 
1131 /*************************************************************************
1132  *  Activate_RunDLL [SHELL32.105]
1133  *
1134  * Unlocks the foreground window and allows the shell window to become the
1135  * foreground window. Every parameter is unused.
1136  */
1137 EXTERN_C
1138 BOOL WINAPI
1139 Activate_RunDLL(
1140     _In_ HWND hwnd,
1141     _In_ HINSTANCE hinst,
1142     _In_ LPCWSTR cmdline,
1143     _In_ INT cmdshow)
1144 {
1145     DWORD dwProcessID;
1146 
1147     UNREFERENCED_PARAMETER(hwnd);
1148     UNREFERENCED_PARAMETER(hinst);
1149     UNREFERENCED_PARAMETER(cmdline);
1150     UNREFERENCED_PARAMETER(cmdshow);
1151 
1152     TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline);
1153 
1154     GetWindowThreadProcessId(GetShellWindow(), &dwProcessID);
1155     return AllowSetForegroundWindow(dwProcessID);
1156 }
1157 
1158 /*************************************************************************
1159  *                SHStartNetConnectionDialogA (SHELL32.12)
1160  *
1161  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga
1162  */
1163 EXTERN_C
1164 HRESULT WINAPI
1165 SHStartNetConnectionDialogA(
1166     _In_ HWND hwnd,
1167     _In_ LPCSTR pszRemoteName,
1168     _In_ DWORD dwType)
1169 {
1170     LPCWSTR pszRemoteNameW = NULL;
1171     CStringW strRemoteNameW;
1172 
1173     TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType);
1174 
1175     if (pszRemoteName)
1176     {
1177         strRemoteNameW = pszRemoteName;
1178         pszRemoteNameW = strRemoteNameW;
1179     }
1180 
1181     return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType);
1182 }
1183 
1184 /*************************************************************************
1185  * Helper functions for PathIsEqualOrSubFolder
1186  */
1187 
1188 static INT
1189 DynamicPathCommonPrefixW(
1190     _In_ LPCWSTR lpszPath1,
1191     _In_ LPCWSTR lpszPath2,
1192     _Out_ CStringW& strPath)
1193 {
1194     SIZE_T cchPath1 = wcslen(lpszPath1);
1195     SIZE_T cchPath2 = wcslen(lpszPath2);
1196     LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16);
1197     INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath);
1198     strPath.ReleaseBuffer();
1199     return ret;
1200 }
1201 
1202 EXTERN_C HRESULT WINAPI
1203 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax);
1204 
1205 static HRESULT
1206 DynamicSHGetPathFromIDListW(
1207     _In_ LPCITEMIDLIST pidl,
1208     _Out_ CStringW& strPath)
1209 {
1210     HRESULT hr;
1211 
1212     for (UINT cchPath = MAX_PATH;; cchPath *= 2)
1213     {
1214         LPWSTR lpszPath = strPath.GetBuffer(cchPath);
1215         if (!lpszPath)
1216             return E_OUTOFMEMORY;
1217 
1218         hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath);
1219         strPath.ReleaseBuffer();
1220 
1221         if (hr != E_NOT_SUFFICIENT_BUFFER)
1222             break;
1223 
1224         if (cchPath >= MAXUINT / 2)
1225         {
1226             hr = E_FAIL;
1227             break;
1228         }
1229     }
1230 
1231     if (FAILED(hr))
1232         strPath.Empty();
1233 
1234     return hr;
1235 }
1236 
1237 static HRESULT
1238 DynamicSHGetSpecialFolderPathW(
1239     _In_ HWND hwndOwner,
1240     _Out_ CStringW& strPath,
1241     _In_ INT nCSIDL,
1242     _In_ BOOL bCreate)
1243 {
1244     LPITEMIDLIST pidl;
1245     HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl);
1246     if (SUCCEEDED(hr))
1247     {
1248         hr = DynamicSHGetPathFromIDListW(pidl, strPath);
1249         CoTaskMemFree(pidl);
1250     }
1251 
1252     if (FAILED(hr))
1253         strPath.Empty();
1254     else if (bCreate)
1255         CreateDirectoryW(strPath, NULL);
1256 
1257     return hr;
1258 }
1259 
1260 static VOID
1261 DynamicPathRemoveBackslashW(
1262     _Out_ CStringW& strPath)
1263 {
1264     INT nLength = strPath.GetLength();
1265     if (nLength > 0 && strPath[nLength - 1] == L'\\')
1266         strPath = strPath.Left(nLength - 1);
1267 }
1268 
1269 /*************************************************************************
1270  *                PathIsEqualOrSubFolder (SHELL32.755)
1271  */
1272 EXTERN_C
1273 BOOL WINAPI
1274 PathIsEqualOrSubFolder(
1275     _In_ LPCWSTR pszPath1OrCSIDL,
1276     _In_ LPCWSTR pszPath2)
1277 {
1278     CStringW strCommon, strPath1;
1279 
1280     TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2));
1281 
1282     if (IS_INTRESOURCE(pszPath1OrCSIDL))
1283     {
1284         DynamicSHGetSpecialFolderPathW(
1285             NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE);
1286     }
1287     else
1288     {
1289         strPath1 = pszPath1OrCSIDL;
1290     }
1291 
1292     DynamicPathRemoveBackslashW(strPath1);
1293 
1294     if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon))
1295         return FALSE;
1296 
1297     return strPath1.CompareNoCase(strCommon) == 0;
1298 }
1299 
1300 /*************************************************************************
1301  *  SHGetRealIDL [SHELL32.98]
1302  */
1303 EXTERN_C
1304 HRESULT WINAPI
1305 SHGetRealIDL(
1306     _In_ IShellFolder *psf,
1307     _In_ PCUITEMID_CHILD pidlSimple,
1308     _Outptr_ PITEMID_CHILD *ppidlReal)
1309 {
1310     HRESULT hr;
1311     STRRET strret;
1312     WCHAR szPath[MAX_PATH];
1313     SFGAOF attrs;
1314 
1315     *ppidlReal = NULL;
1316 
1317     hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_INFOLDER | SHGDN_FORPARSING,
1318                                        &strret, 0);
1319     if (FAILED_UNEXPECTEDLY(hr))
1320         return hr;
1321 
1322     hr = StrRetToBufW(&strret, pidlSimple, szPath, _countof(szPath));
1323     if (FAILED_UNEXPECTEDLY(hr))
1324         return hr;
1325 
1326     attrs = SFGAO_FILESYSTEM;
1327     hr = psf->GetAttributesOf(1, &pidlSimple, &attrs);
1328     if (SUCCEEDED(hr) && !(attrs & SFGAO_FILESYSTEM))
1329         return SHILClone(pidlSimple, ppidlReal);
1330 
1331     hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL);
1332     if (hr == E_INVALIDARG || hr == E_NOTIMPL)
1333         return SHILClone(pidlSimple, ppidlReal);
1334 
1335     return hr;
1336 }
1337 
1338 EXTERN_C HRESULT
1339 IUnknown_InitializeCommand(
1340     _In_ IUnknown *pUnk,
1341     _In_ PCWSTR pszCommandName,
1342     _In_opt_ IPropertyBag *pPB)
1343 {
1344     HRESULT hr;
1345     CComPtr<IInitializeCommand> pIC;
1346     if (SUCCEEDED(hr = pUnk->QueryInterface(IID_PPV_ARG(IInitializeCommand, &pIC))))
1347         hr = pIC->Initialize(pszCommandName, pPB);
1348     return hr;
1349 }
1350 
1351 EXTERN_C HRESULT
1352 InvokeIExecuteCommand(
1353     _In_ IExecuteCommand *pEC,
1354     _In_ PCWSTR pszCommandName,
1355     _In_opt_ IPropertyBag *pPB,
1356     _In_opt_ IShellItemArray *pSIA,
1357     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1358     _In_opt_ IUnknown *pSite)
1359 {
1360     if (!pEC)
1361         return E_INVALIDARG;
1362 
1363     if (pSite)
1364         IUnknown_SetSite(pEC, pSite);
1365     IUnknown_InitializeCommand(pEC, pszCommandName, pPB);
1366 
1367     CComPtr<IObjectWithSelection> pOWS;
1368     if (pSIA && SUCCEEDED(pEC->QueryInterface(IID_PPV_ARG(IObjectWithSelection, &pOWS))))
1369         pOWS->SetSelection(pSIA);
1370 
1371     DWORD dwKeyState = 0, fMask = pICI ? pICI->fMask : 0;
1372     pEC->SetNoShowUI((fMask & CMIC_MASK_FLAG_NO_UI) != 0);
1373     pEC->SetShowWindow(pICI ? pICI->nShow : SW_SHOW);
1374     if (fMask & CMIC_MASK_SHIFT_DOWN)
1375         dwKeyState |= MK_SHIFT;
1376     if (fMask & CMIC_MASK_CONTROL_DOWN)
1377         dwKeyState |= MK_CONTROL;
1378     pEC->SetKeyState(dwKeyState);
1379     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpDirectoryW)
1380         pEC->SetDirectory(pICI->lpDirectoryW);
1381     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpParametersW)
1382         pEC->SetParameters(pICI->lpParametersW);
1383     if (fMask & CMIC_MASK_PTINVOKE)
1384         pEC->SetPosition(pICI->ptInvoke);
1385 
1386     HRESULT hr = pEC->Execute();
1387     if (pSite)
1388         IUnknown_SetSite(pEC, NULL);
1389     return hr;
1390 }
1391 
1392 EXTERN_C HRESULT
1393 InvokeIExecuteCommandWithDataObject(
1394     _In_ IExecuteCommand *pEC,
1395     _In_ PCWSTR pszCommandName,
1396     _In_opt_ IPropertyBag *pPB,
1397     _In_ IDataObject *pDO,
1398     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1399     _In_opt_ IUnknown *pSite)
1400 {
1401     CComPtr<IShellItemArray> pSIA;
1402     HRESULT hr = SHCreateShellItemArrayFromDataObject(pDO, IID_PPV_ARG(IShellItemArray, &pSIA));
1403     return SUCCEEDED(hr) ? InvokeIExecuteCommand(pEC, pszCommandName, pPB, pSIA, pICI, pSite) : hr;
1404 }
1405 
1406 static HRESULT
1407 GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax)
1408 {
1409     HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax);
1410     if (FAILED(hr))
1411     {
1412         WCHAR buf[MAX_PATH];
1413         hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf));
1414         if (SUCCEEDED(hr))
1415             hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL;
1416     }
1417     return hr;
1418 }
1419 
1420 UINT
1421 GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba)
1422 {
1423     CHAR buf[MAX_PATH];
1424     if (IS_INTRESOURCE(verba))
1425     {
1426         if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf))))
1427             return 0;
1428         verba = buf;
1429     }
1430     return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0
1431 }
1432