xref: /reactos/dll/win32/shell32/utils.cpp (revision 84df40a1)
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 #include <lmcons.h>
10 #include <lmapibuf.h>
11 #include <lmaccess.h>
12 #include <lmserver.h>
13 #include <secext.h>
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(shell);
16 
StrEndNA(_In_ PCSTR psz,_In_ INT_PTR cch)17 static PCSTR StrEndNA(_In_ PCSTR psz, _In_ INT_PTR cch)
18 {
19     PCSTR pch, pchEnd = &psz[cch];
20     for (pch = psz; *pch && pch < pchEnd; pch = CharNextA(pch))
21         ;
22     if (pchEnd < pch) // A double-byte character detected at last?
23         pch -= 2; // The width of a double-byte character is 2
24     return pch;
25 }
26 
StrEndNW(_In_ PCWSTR psz,_In_ INT_PTR cch)27 static PCWSTR StrEndNW(_In_ PCWSTR psz, _In_ INT_PTR cch)
28 {
29     PCWSTR pch, pchEnd = &psz[cch];
30     for (pch = psz; *pch && pch < pchEnd; ++pch)
31         ;
32     return pch;
33 }
34 
35 /*************************************************************************
36  *  StrRStrA [SHELL32.389]
37  */
38 EXTERN_C
39 PSTR WINAPI
StrRStrA(_In_ PCSTR pszSrc,_In_opt_ PCSTR pszLast,_In_ PCSTR pszSearch)40 StrRStrA(
41     _In_ PCSTR pszSrc,
42     _In_opt_ PCSTR pszLast,
43     _In_ PCSTR pszSearch)
44 {
45     INT cchSearch = lstrlenA(pszSearch);
46 
47     PCSTR pchEnd = pszLast ? pszLast : &pszSrc[lstrlenA(pszSrc)];
48     if (pchEnd == pszSrc)
49         return NULL;
50 
51     INT_PTR cchEnd = pchEnd - pszSrc;
52     for (;;)
53     {
54         --pchEnd;
55         --cchEnd;
56         if (!pchEnd)
57             break;
58         if (!StrCmpNA(pchEnd, pszSearch, cchSearch) && pchEnd == StrEndNA(pszSrc, cchEnd))
59             break;
60         if (pchEnd == pszSrc)
61             return NULL;
62     }
63 
64     return const_cast<PSTR>(pchEnd);
65 }
66 
67 /*************************************************************************
68  *  StrRStrW [SHELL32.392]
69  */
70 EXTERN_C
71 PWSTR WINAPI
StrRStrW(_In_ PCWSTR pszSrc,_In_opt_ PCWSTR pszLast,_In_ PCWSTR pszSearch)72 StrRStrW(
73     _In_ PCWSTR pszSrc,
74     _In_opt_ PCWSTR pszLast,
75     _In_ PCWSTR pszSearch)
76 {
77     INT cchSearch = lstrlenW(pszSearch);
78 
79     PCWSTR pchEnd = pszLast ? pszLast : &pszSrc[lstrlenW(pszSrc)];
80     if (pchEnd == pszSrc)
81         return NULL;
82 
83     INT_PTR cchEnd = pchEnd - pszSrc;
84     for (;;)
85     {
86         --pchEnd;
87         --cchEnd;
88         if (!pchEnd)
89             break;
90         if (!StrCmpNW(pchEnd, pszSearch, cchSearch) && pchEnd == StrEndNW(pszSrc, cchEnd))
91             break;
92         if (pchEnd == pszSrc)
93             return NULL;
94     }
95 
96     return const_cast<PWSTR>(pchEnd);
97 }
98 
99 HWND
FindStubWindow(UINT Type,LPCWSTR Path)100 CStubWindow32::FindStubWindow(UINT Type, LPCWSTR Path)
101 {
102     for (HWND hWnd, hWndAfter = NULL;;)
103     {
104         hWnd = hWndAfter = FindWindowExW(NULL, hWndAfter, CSTUBWINDOW32_CLASSNAME, Path);
105         if (!hWnd || !Path)
106             return NULL;
107         if (GetPropW(hWnd, GetTypePropName()) == ULongToHandle(Type))
108             return hWnd;
109     }
110 }
111 
112 HRESULT
CreateStub(UINT Type,LPCWSTR Path,const POINT * pPt)113 CStubWindow32::CreateStub(UINT Type, LPCWSTR Path, const POINT *pPt)
114 {
115     if (HWND hWnd = FindStubWindow(Type, Path))
116     {
117         ::SwitchToThisWindow(::GetLastActivePopup(hWnd), TRUE);
118         return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
119     }
120     RECT rcPosition = { pPt ? pPt->x : CW_USEDEFAULT, pPt ? pPt->y : CW_USEDEFAULT, 0, 0 };
121     DWORD Style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION;
122     DWORD ExStyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW;
123     if (!Create(NULL, rcPosition, Path, Style, ExStyle))
124     {
125         ERR("StubWindow32 creation failed\n");
126         return E_FAIL;
127     }
128     ::SetPropW(*this, GetTypePropName(), ULongToHandle(Type));
129     return S_OK;
130 }
131 
132 HRESULT
SHILClone(_In_opt_ LPCITEMIDLIST pidl,_Outptr_ LPITEMIDLIST * ppidl)133 SHILClone(
134     _In_opt_ LPCITEMIDLIST pidl,
135     _Outptr_ LPITEMIDLIST *ppidl)
136 {
137     if (!pidl)
138     {
139         *ppidl = NULL;
140         return S_OK;
141     }
142     *ppidl = ILClone(pidl);
143     return (*ppidl ? S_OK : E_OUTOFMEMORY);
144 }
145 
PathIsDotOrDotDotW(_In_ LPCWSTR pszPath)146 BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath)
147 {
148     if (pszPath[0] != L'.')
149         return FALSE;
150     return !pszPath[1] || (pszPath[1] == L'.' && !pszPath[2]);
151 }
152 
153 #define PATH_VALID_ELEMENT ( \
154     PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \
155     PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID \
156 )
157 
PathIsValidElement(_In_ LPCWSTR pszPath)158 BOOL PathIsValidElement(_In_ LPCWSTR pszPath)
159 {
160     if (!*pszPath || PathIsDotOrDotDotW(pszPath))
161         return FALSE;
162 
163     for (LPCWSTR pch = pszPath; *pch; ++pch)
164     {
165         if (!PathIsValidCharW(*pch, PATH_VALID_ELEMENT))
166             return FALSE;
167     }
168 
169     return TRUE;
170 }
171 
PathIsDosDevice(_In_ LPCWSTR pszName)172 BOOL PathIsDosDevice(_In_ LPCWSTR pszName)
173 {
174     WCHAR szPath[MAX_PATH];
175     StringCchCopyW(szPath, _countof(szPath), pszName);
176     PathRemoveExtensionW(szPath);
177 
178     if (lstrcmpiW(szPath, L"NUL") == 0 || lstrcmpiW(szPath, L"PRN") == 0 ||
179         lstrcmpiW(szPath, L"CON") == 0 || lstrcmpiW(szPath, L"AUX") == 0)
180     {
181         return TRUE;
182     }
183 
184     if (_wcsnicmp(szPath, L"LPT", 3) == 0 || _wcsnicmp(szPath, L"COM", 3) == 0)
185     {
186         if ((L'0' <= szPath[3] && szPath[3] <= L'9') && szPath[4] == UNICODE_NULL)
187             return TRUE;
188     }
189 
190     return FALSE;
191 }
192 
SHILAppend(_Inout_ LPITEMIDLIST pidl,_Inout_ LPITEMIDLIST * ppidl)193 HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl)
194 {
195     LPITEMIDLIST pidlOld = *ppidl;
196     if (!pidlOld)
197     {
198         *ppidl = pidl;
199         return S_OK;
200     }
201 
202     HRESULT hr = SHILCombine(*ppidl, pidl, ppidl);
203     ILFree(pidlOld);
204     ILFree(pidl);
205     return hr;
206 }
207 
208 /*************************************************************************
209  *  SHShouldShowWizards [SHELL32.237]
210  *
211  * Used by printer and network features.
212  * @see https://undoc.airesoft.co.uk/shell32.dll/SHShouldShowWizards.php
213  */
214 EXTERN_C
215 HRESULT WINAPI
SHShouldShowWizards(_In_ IUnknown * pUnknown)216 SHShouldShowWizards(_In_ IUnknown *pUnknown)
217 {
218     HRESULT hr;
219     IShellBrowser *pBrowser;
220 
221     hr = IUnknown_QueryService(pUnknown, SID_STopWindow, IID_PPV_ARG(IShellBrowser, &pBrowser));
222     if (FAILED(hr))
223         return hr;
224 
225     SHELLSTATE state;
226     SHGetSetSettings(&state, SSF_WEBVIEW, FALSE);
227     if (state.fWebView &&
228         !SHRegGetBoolUSValueW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
229                               L"ShowWizardsTEST", FALSE, FALSE))
230     {
231         hr = S_FALSE;
232     }
233 
234     pBrowser->Release();
235     return hr;
236 }
237 
238 static BOOL
OpenEffectiveToken(_In_ DWORD DesiredAccess,_Out_ HANDLE * phToken)239 OpenEffectiveToken(
240     _In_ DWORD DesiredAccess,
241     _Out_ HANDLE *phToken)
242 {
243     BOOL ret;
244 
245     if (phToken == NULL)
246     {
247         SetLastError(ERROR_INVALID_PARAMETER);
248         return FALSE;
249     }
250 
251     *phToken = NULL;
252 
253     ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken);
254     if (!ret && GetLastError() == ERROR_NO_TOKEN)
255         ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken);
256 
257     return ret;
258 }
259 
260 HRESULT
Shell_TranslateIDListAlias(_In_ LPCITEMIDLIST pidl,_In_ HANDLE hToken,_Out_ LPITEMIDLIST * ppidlAlias,_In_ DWORD dwFlags)261 Shell_TranslateIDListAlias(
262     _In_ LPCITEMIDLIST pidl,
263     _In_ HANDLE hToken,
264     _Out_ LPITEMIDLIST *ppidlAlias,
265     _In_ DWORD dwFlags)
266 {
267     return E_FAIL; //FIXME
268 }
269 
BindCtx_ContainsObject(_In_ IBindCtx * pBindCtx,_In_ LPCWSTR pszName)270 BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName)
271 {
272     CComPtr<IUnknown> punk;
273     if (!pBindCtx || FAILED(pBindCtx->GetObjectParam(const_cast<LPWSTR>(pszName), &punk)))
274         return FALSE;
275     return TRUE;
276 }
277 
BindCtx_GetMode(_In_ IBindCtx * pbc,_In_ DWORD dwDefault)278 DWORD BindCtx_GetMode(_In_ IBindCtx *pbc, _In_ DWORD dwDefault)
279 {
280     if (!pbc)
281         return dwDefault;
282 
283     BIND_OPTS BindOpts = { sizeof(BindOpts) };
284     HRESULT hr = pbc->GetBindOptions(&BindOpts);
285     if (FAILED(hr))
286         return dwDefault;
287 
288     return BindOpts.grfMode;
289 }
290 
SHSkipJunctionBinding(_In_ IBindCtx * pbc,_In_ CLSID * pclsid)291 BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid)
292 {
293     if (!pbc)
294         return FALSE;
295 
296     BIND_OPTS BindOps = { sizeof(BindOps) };
297     if (SUCCEEDED(pbc->GetBindOptions(&BindOps)) && BindOps.grfFlags == OLECONTF_LINKS)
298         return TRUE;
299 
300     return pclsid && SHSkipJunction(pbc, pclsid);
301 }
302 
SHIsFileSysBindCtx(_In_ IBindCtx * pBindCtx,_Out_opt_ WIN32_FIND_DATAW * pFindData)303 HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *pFindData)
304 {
305     CComPtr<IUnknown> punk;
306     CComPtr<IFileSystemBindData> pBindData;
307 
308     if (!pBindCtx || FAILED(pBindCtx->GetObjectParam((LPWSTR)STR_FILE_SYS_BIND_DATA, &punk)))
309         return S_FALSE;
310 
311     if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData))))
312         return S_FALSE;
313 
314     if (pFindData)
315         pBindData->GetFindData(pFindData);
316 
317     return S_OK;
318 }
319 
Shell_FailForceReturn(_In_ HRESULT hr)320 BOOL Shell_FailForceReturn(_In_ HRESULT hr)
321 {
322     DWORD code = HRESULT_CODE(hr);
323 
324     switch (code)
325     {
326         case ERROR_BAD_NETPATH:
327         case ERROR_BAD_NET_NAME:
328         case ERROR_CANCELLED:
329             return TRUE;
330 
331         default:
332             return (ERROR_FILE_NOT_FOUND <= code && code <= ERROR_PATH_NOT_FOUND);
333     }
334 }
335 
336 HRESULT
SHBindToObjectEx(_In_opt_ IShellFolder * pShellFolder,_In_ LPCITEMIDLIST pidl,_In_opt_ IBindCtx * pBindCtx,_In_ REFIID riid,_Out_ void ** ppvObj)337 SHBindToObjectEx(
338     _In_opt_ IShellFolder *pShellFolder,
339     _In_ LPCITEMIDLIST pidl,
340     _In_opt_ IBindCtx *pBindCtx,
341     _In_ REFIID riid,
342     _Out_ void **ppvObj)
343 {
344     CComPtr<IShellFolder> psfDesktop;
345 
346     *ppvObj = NULL;
347 
348     if (!pShellFolder)
349     {
350         SHGetDesktopFolder(&psfDesktop);
351         if (!psfDesktop)
352             return E_FAIL;
353 
354         pShellFolder = psfDesktop;
355     }
356 
357     HRESULT hr;
358     if (_ILIsDesktop(pidl))
359         hr = pShellFolder->QueryInterface(riid, ppvObj);
360     else
361         hr = pShellFolder->BindToObject(pidl, pBindCtx, riid, ppvObj);
362 
363     if (SUCCEEDED(hr) && !*ppvObj)
364         hr = E_FAIL;
365 
366     return hr;
367 }
368 
369 EXTERN_C
SHBindToObject(_In_opt_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ REFIID riid,_Out_ void ** ppvObj)370 HRESULT SHBindToObject(
371     _In_opt_ IShellFolder *psf,
372     _In_ LPCITEMIDLIST pidl,
373     _In_ REFIID riid,
374     _Out_ void **ppvObj)
375 {
376     return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj);
377 }
378 
379 EXTERN_C HRESULT
SHELL_GetUIObjectOfAbsoluteItem(_In_opt_ HWND hWnd,_In_ PCIDLIST_ABSOLUTE pidl,_In_ REFIID riid,_Out_ void ** ppvObj)380 SHELL_GetUIObjectOfAbsoluteItem(
381     _In_opt_ HWND hWnd,
382     _In_ PCIDLIST_ABSOLUTE pidl,
383     _In_ REFIID riid, _Out_ void **ppvObj)
384 {
385     if (!ppvObj)
386         return E_INVALIDARG;
387     *ppvObj = NULL;
388     IShellFolder *psf;
389     PCUITEMID_CHILD pidlChild;
390     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
391     if (SUCCEEDED(hr))
392     {
393         hr = psf->GetUIObjectOf(hWnd, 1, &pidlChild, riid, NULL, ppvObj);
394         psf->Release();
395         if (SUCCEEDED(hr))
396         {
397             if (*ppvObj)
398                 return hr;
399             hr = E_FAIL;
400         }
401     }
402     return hr;
403 }
404 
405 HRESULT
Shell_DisplayNameOf(_In_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ DWORD dwFlags,_Out_ LPWSTR pszBuf,_In_ UINT cchBuf)406 Shell_DisplayNameOf(
407     _In_ IShellFolder *psf,
408     _In_ LPCITEMIDLIST pidl,
409     _In_ DWORD dwFlags,
410     _Out_ LPWSTR pszBuf,
411     _In_ UINT cchBuf)
412 {
413     *pszBuf = UNICODE_NULL;
414     STRRET sr;
415     HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr);
416     if (FAILED(hr))
417         return hr;
418     return StrRetToBufW(&sr, pidl, pszBuf, cchBuf);
419 }
420 
421 DWORD
SHGetAttributes(_In_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ DWORD dwAttributes)422 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes)
423 {
424     LPCITEMIDLIST pidlLast = pidl;
425     IShellFolder *release = NULL;
426 
427     if (!psf)
428     {
429         SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
430         if (!psf)
431             return 0;
432         release = psf;
433     }
434 
435     DWORD oldAttrs = dwAttributes;
436     if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes)))
437         dwAttributes = 0;
438     else
439         dwAttributes &= oldAttrs;
440 
441     if ((dwAttributes & SFGAO_FOLDER) &&
442         (dwAttributes & SFGAO_STREAM) &&
443         !(dwAttributes & SFGAO_STORAGEANCESTOR) &&
444         (oldAttrs & SFGAO_STORAGEANCESTOR) &&
445         (SHGetObjectCompatFlags(psf, NULL) & 0x200))
446     {
447         dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR);
448         dwAttributes |= SFGAO_STORAGEANCESTOR;
449     }
450 
451     if (release)
452         release->Release();
453     return dwAttributes;
454 }
455 
SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl,_Out_ PIDLIST_ABSOLUTE * ppidl)456 HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl)
457 {
458     IShellLink *pSL;
459     HRESULT hr = SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellLink, &pSL));
460     if (SUCCEEDED(hr))
461     {
462         hr = pSL->GetIDList(ppidl); // Note: Returns S_FALSE if no target pidl
463         pSL->Release();
464     }
465     return hr;
466 }
467 
SHCoInitializeAnyApartment(VOID)468 HRESULT SHCoInitializeAnyApartment(VOID)
469 {
470     HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
471     if (FAILED(hr))
472         hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE);
473     return hr;
474 }
475 
476 HRESULT
SHGetNameAndFlagsW(_In_ LPCITEMIDLIST pidl,_In_ DWORD dwFlags,_Out_opt_ LPWSTR pszText,_In_ UINT cchBuf,_Inout_opt_ DWORD * pdwAttributes)477 SHGetNameAndFlagsW(
478     _In_ LPCITEMIDLIST pidl,
479     _In_ DWORD dwFlags,
480     _Out_opt_ LPWSTR pszText,
481     _In_ UINT cchBuf,
482     _Inout_opt_ DWORD *pdwAttributes)
483 {
484     if (pszText)
485         *pszText = UNICODE_NULL;
486 
487     HRESULT hrCoInit = SHCoInitializeAnyApartment();
488 
489     CComPtr<IShellFolder> psfFolder;
490     LPCITEMIDLIST ppidlLast;
491     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast);
492     if (SUCCEEDED(hr))
493     {
494         if (pszText)
495             hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf);
496 
497         if (SUCCEEDED(hr))
498         {
499             if (pdwAttributes)
500                 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes);
501         }
502     }
503 
504     if (SUCCEEDED(hrCoInit))
505         CoUninitialize();
506 
507     return hr;
508 }
509 
510 EXTERN_C HWND
BindCtx_GetUIWindow(_In_ IBindCtx * pBindCtx)511 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx)
512 {
513     HWND hWnd = NULL;
514 
515     CComPtr<IUnknown> punk;
516     if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk)))
517         IUnknown_GetWindow(punk, &hWnd);
518 
519     return hWnd;
520 }
521 
522 class CDummyOleWindow : public IOleWindow
523 {
524 protected:
525     LONG m_cRefs;
526     HWND m_hWnd;
527 
528 public:
CDummyOleWindow()529     CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { }
~CDummyOleWindow()530     virtual ~CDummyOleWindow() { }
531 
532     // IUnknown methods
QueryInterface(REFIID riid,LPVOID * ppvObj)533     STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override
534     {
535         static const QITAB c_tab[] =
536         {
537             QITABENT(CDummyOleWindow, IOleWindow),
538             { NULL }
539         };
540         return ::QISearch(this, c_tab, riid, ppvObj);
541     }
AddRef()542     STDMETHODIMP_(ULONG) AddRef() override
543     {
544         return ++m_cRefs;
545     }
Release()546     STDMETHODIMP_(ULONG) Release() override
547     {
548         if (--m_cRefs == 0)
549         {
550             delete this;
551             return 0;
552         }
553         return m_cRefs;
554     }
555 
556     // IOleWindow methods
GetWindow(HWND * phWnd)557     STDMETHODIMP GetWindow(HWND *phWnd) override
558     {
559         *phWnd = m_hWnd;
560         if (!m_hWnd)
561             return E_NOTIMPL;
562         return S_OK;
563     }
ContextSensitiveHelp(BOOL fEnterMode)564     STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override
565     {
566         return E_NOTIMPL;
567     }
568 };
569 
570 EXTERN_C HRESULT
BindCtx_RegisterObjectParam(_In_ IBindCtx * pBindCtx,_In_ LPOLESTR pszKey,_In_opt_ IUnknown * punk,_Out_ LPBC * ppbc)571 BindCtx_RegisterObjectParam(
572     _In_ IBindCtx *pBindCtx,
573     _In_ LPOLESTR pszKey,
574     _In_opt_ IUnknown *punk,
575     _Out_ LPBC *ppbc)
576 {
577     HRESULT hr = S_OK;
578     CDummyOleWindow *pUnknown = NULL;
579 
580     *ppbc = pBindCtx;
581 
582     if (pBindCtx)
583     {
584         pBindCtx->AddRef();
585     }
586     else
587     {
588         hr = CreateBindCtx(0, ppbc);
589         if (FAILED(hr))
590             return hr;
591     }
592 
593     if (!punk)
594         punk = pUnknown = new CDummyOleWindow();
595 
596     hr = (*ppbc)->RegisterObjectParam(pszKey, punk);
597 
598     if (pUnknown)
599         pUnknown->Release();
600 
601     if (FAILED(hr))
602     {
603         (*ppbc)->Release();
604         *ppbc = NULL;
605     }
606 
607     return hr;
608 }
609 
610 /*************************************************************************
611  *                SHSetFolderPathA (SHELL32.231)
612  *
613  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha
614  */
615 EXTERN_C
616 HRESULT WINAPI
SHSetFolderPathA(_In_ INT csidl,_In_ HANDLE hToken,_In_ DWORD dwFlags,_In_ LPCSTR pszPath)617 SHSetFolderPathA(
618     _In_ INT csidl,
619     _In_ HANDLE hToken,
620     _In_ DWORD dwFlags,
621     _In_ LPCSTR pszPath)
622 {
623     TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath));
624     CStringW strPathW(pszPath);
625     return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW);
626 }
627 
628 /*************************************************************************
629  *                PathIsSlowA (SHELL32.240)
630  *
631  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa
632  */
633 EXTERN_C
634 BOOL WINAPI
PathIsSlowA(_In_ LPCSTR pszFile,_In_ DWORD dwAttr)635 PathIsSlowA(
636     _In_ LPCSTR pszFile,
637     _In_ DWORD dwAttr)
638 {
639     TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr);
640     CStringW strFileW(pszFile);
641     return PathIsSlowW(strFileW, dwAttr);
642 }
643 
644 /*************************************************************************
645  *                ExtractIconResInfoA (SHELL32.221)
646  */
647 EXTERN_C
648 WORD WINAPI
ExtractIconResInfoA(_In_ HANDLE hHandle,_In_ LPCSTR lpFileName,_In_ WORD wIndex,_Out_ LPWORD lpSize,_Out_ LPHANDLE lpIcon)649 ExtractIconResInfoA(
650     _In_ HANDLE hHandle,
651     _In_ LPCSTR lpFileName,
652     _In_ WORD wIndex,
653     _Out_ LPWORD lpSize,
654     _Out_ LPHANDLE lpIcon)
655 {
656     TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon);
657 
658     if (!lpFileName)
659         return 0;
660 
661     CStringW strFileNameW(lpFileName);
662     return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon);
663 }
664 
665 /*************************************************************************
666  *                ShortSizeFormatW (SHELL32.204)
667  */
668 EXTERN_C
669 LPWSTR WINAPI
670 ShortSizeFormatW(
671     _In_ DWORD dwNumber,
672     _Out_writes_(0x8FFF) LPWSTR pszBuffer)
673 {
674     TRACE("(%lu, %p)\n", dwNumber, pszBuffer);
675     return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF);
676 }
677 
678 /*************************************************************************
679  *                SHOpenEffectiveToken (SHELL32.235)
680  */
SHOpenEffectiveToken(_Out_ LPHANDLE phToken)681 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken)
682 {
683     TRACE("%p\n", phToken);
684     return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken);
685 }
686 
687 /*************************************************************************
688  *                SHGetUserSessionId (SHELL32.248)
689  */
SHGetUserSessionId(_In_opt_ HANDLE hToken)690 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken)
691 {
692     DWORD dwSessionId, dwLength;
693     BOOL bOpenToken = FALSE;
694 
695     TRACE("%p\n", hToken);
696 
697     if (!hToken)
698         bOpenToken = SHOpenEffectiveToken(&hToken);
699 
700     if (!hToken ||
701         !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength))
702     {
703         dwSessionId = 0;
704     }
705 
706     if (bOpenToken)
707         CloseHandle(hToken);
708 
709     return dwSessionId;
710 }
711 
712 /*************************************************************************
713  *                SHInvokePrivilegedFunctionW (SHELL32.246)
714  */
715 EXTERN_C
716 HRESULT WINAPI
SHInvokePrivilegedFunctionW(_In_ LPCWSTR pszName,_In_ PRIVILEGED_FUNCTION fn,_In_opt_ LPARAM lParam)717 SHInvokePrivilegedFunctionW(
718     _In_ LPCWSTR pszName,
719     _In_ PRIVILEGED_FUNCTION fn,
720     _In_opt_ LPARAM lParam)
721 {
722     TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam);
723 
724     if (!pszName || !fn)
725         return E_INVALIDARG;
726 
727     HANDLE hToken = NULL;
728     TOKEN_PRIVILEGES NewPriv, PrevPriv;
729     BOOL bAdjusted = FALSE;
730 
731     if (SHOpenEffectiveToken(&hToken) &&
732         ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid))
733     {
734         NewPriv.PrivilegeCount = 1;
735         NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
736 
737         DWORD dwReturnSize;
738         bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv,
739                                             sizeof(PrevPriv), &PrevPriv, &dwReturnSize);
740     }
741 
742     HRESULT hr = fn(lParam);
743 
744     if (bAdjusted)
745         ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL);
746 
747     if (hToken)
748         ::CloseHandle(hToken);
749 
750     return hr;
751 }
752 
753 /*************************************************************************
754  *                SHTestTokenPrivilegeW (SHELL32.236)
755  *
756  * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php
757  */
758 EXTERN_C
759 BOOL WINAPI
SHTestTokenPrivilegeW(_In_opt_ HANDLE hToken,_In_ LPCWSTR lpName)760 SHTestTokenPrivilegeW(
761     _In_opt_ HANDLE hToken,
762     _In_ LPCWSTR lpName)
763 {
764     LUID Luid;
765     DWORD dwLength;
766     PTOKEN_PRIVILEGES pTokenPriv;
767     HANDLE hNewToken = NULL;
768     BOOL ret = FALSE;
769 
770     TRACE("(%p, %s)\n", hToken, debugstr_w(lpName));
771 
772     if (!lpName)
773         return FALSE;
774 
775     if (!hToken)
776     {
777         if (!SHOpenEffectiveToken(&hNewToken))
778             goto Quit;
779 
780         if (!hNewToken)
781             return FALSE;
782 
783         hToken = hNewToken;
784     }
785 
786     if (!LookupPrivilegeValueW(NULL, lpName, &Luid))
787         return FALSE;
788 
789     dwLength = 0;
790     if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength))
791         goto Quit;
792 
793     pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength);
794     if (!pTokenPriv)
795         goto Quit;
796 
797     if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength))
798     {
799         UINT iPriv, cPrivs;
800         cPrivs = pTokenPriv->PrivilegeCount;
801         for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv)
802         {
803             ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid);
804         }
805     }
806 
807     LocalFree(pTokenPriv);
808 
809 Quit:
810     if (hToken == hNewToken)
811         CloseHandle(hNewToken);
812 
813     return ret;
814 }
815 
IsShutdownAllowed(VOID)816 BOOL IsShutdownAllowed(VOID)
817 {
818     return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME);
819 }
820 
821 /*************************************************************************
822  *                IsSuspendAllowed (SHELL32.53)
823  */
IsSuspendAllowed(VOID)824 BOOL WINAPI IsSuspendAllowed(VOID)
825 {
826     TRACE("()\n");
827     return IsShutdownAllowed() && IsPwrSuspendAllowed();
828 }
829 
830 /*************************************************************************
831  *                SHGetShellStyleHInstance (SHELL32.749)
832  */
833 EXTERN_C HINSTANCE
834 WINAPI
SHGetShellStyleHInstance(VOID)835 SHGetShellStyleHInstance(VOID)
836 {
837     HINSTANCE hInst = NULL;
838     WCHAR szPath[MAX_PATH], szColorName[100];
839     HRESULT hr;
840     CStringW strShellStyle;
841 
842     TRACE("SHGetShellStyleHInstance called\n");
843 
844     /* First, attempt to load the shellstyle dll from the current active theme */
845     hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0);
846     if (FAILED(hr))
847         goto DoDefault;
848 
849     /* Strip the theme filename */
850     PathRemoveFileSpecW(szPath);
851 
852     strShellStyle = szPath;
853     strShellStyle += L"\\Shell\\";
854     strShellStyle += szColorName;
855     strShellStyle += L"\\ShellStyle.dll";
856 
857     hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE);
858     if (hInst)
859         return hInst;
860 
861     /* Otherwise, use the version stored in the System32 directory */
862 DoDefault:
863     if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll",
864                                    szPath, _countof(szPath)))
865     {
866         ERR("Expand failed\n");
867         return NULL;
868     }
869     return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
870 }
871 
872 /*************************************************************************
873  *                SHCreatePropertyBag (SHELL32.715)
874  */
875 EXTERN_C HRESULT
876 WINAPI
SHCreatePropertyBag(_In_ REFIID riid,_Out_ void ** ppvObj)877 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj)
878 {
879     return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj);
880 }
881 
882 // The helper function for SHGetUnreadMailCountW
883 static DWORD
SHELL_ReadSingleUnreadMailCount(_In_ HKEY hKey,_Out_opt_ PDWORD pdwCount,_Out_opt_ PFILETIME pFileTime,_Out_writes_opt_ (cchShellExecuteCommand)LPWSTR pszShellExecuteCommand,_In_ INT cchShellExecuteCommand)884 SHELL_ReadSingleUnreadMailCount(
885     _In_ HKEY hKey,
886     _Out_opt_ PDWORD pdwCount,
887     _Out_opt_ PFILETIME pFileTime,
888     _Out_writes_opt_(cchShellExecuteCommand) LPWSTR pszShellExecuteCommand,
889     _In_ INT cchShellExecuteCommand)
890 {
891     DWORD dwType, dwCount, cbSize = sizeof(dwCount);
892     DWORD error = SHQueryValueExW(hKey, L"MessageCount", 0, &dwType, &dwCount, &cbSize);
893     if (error)
894         return error;
895     if (pdwCount && dwType == REG_DWORD)
896         *pdwCount = dwCount;
897 
898     FILETIME FileTime;
899     cbSize = sizeof(FileTime);
900     error = SHQueryValueExW(hKey, L"TimeStamp", 0, &dwType, &FileTime, &cbSize);
901     if (error)
902         return error;
903     if (pFileTime && dwType == REG_BINARY)
904         *pFileTime = FileTime;
905 
906     WCHAR szName[2 * MAX_PATH];
907     cbSize = sizeof(szName);
908     error = SHQueryValueExW(hKey, L"Application", 0, &dwType, szName, &cbSize);
909     if (error)
910         return error;
911 
912     if (pszShellExecuteCommand && dwType == REG_SZ &&
913         FAILED(StringCchCopyW(pszShellExecuteCommand, cchShellExecuteCommand, szName)))
914     {
915         return ERROR_INSUFFICIENT_BUFFER;
916     }
917 
918     return ERROR_SUCCESS;
919 }
920 
921 /*************************************************************************
922  *  SHGetUnreadMailCountW [SHELL32.320]
923  *
924  * @see https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetunreadmailcountw
925  */
926 EXTERN_C
927 HRESULT WINAPI
SHGetUnreadMailCountW(_In_opt_ HKEY hKeyUser,_In_opt_ PCWSTR pszMailAddress,_Out_opt_ PDWORD pdwCount,_Inout_opt_ PFILETIME pFileTime,_Out_writes_opt_ (cchShellExecuteCommand)PWSTR pszShellExecuteCommand,_In_ INT cchShellExecuteCommand)928 SHGetUnreadMailCountW(
929     _In_opt_ HKEY hKeyUser,
930     _In_opt_ PCWSTR pszMailAddress,
931     _Out_opt_ PDWORD pdwCount,
932     _Inout_opt_ PFILETIME pFileTime,
933     _Out_writes_opt_(cchShellExecuteCommand) PWSTR pszShellExecuteCommand,
934     _In_ INT cchShellExecuteCommand)
935 {
936     LSTATUS error;
937     HKEY hKey;
938 
939     if (!hKeyUser)
940         hKeyUser = HKEY_CURRENT_USER;
941 
942     if (pszMailAddress)
943     {
944         CStringW strKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail";
945         strKey += L'\\';
946         strKey += pszMailAddress;
947 
948         error = RegOpenKeyExW(hKeyUser, strKey, 0, KEY_QUERY_VALUE, &hKey);
949         if (error)
950             return HRESULT_FROM_WIN32(error);
951 
952         error = SHELL_ReadSingleUnreadMailCount(hKey, pdwCount, pFileTime,
953                                                 pszShellExecuteCommand, cchShellExecuteCommand);
954     }
955     else
956     {
957         if (pszShellExecuteCommand || cchShellExecuteCommand)
958             return E_INVALIDARG;
959 
960         *pdwCount = 0;
961 
962         error = RegOpenKeyExW(hKeyUser, L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail",
963                               0, KEY_ENUMERATE_SUB_KEYS, &hKey);
964         if (error)
965             return HRESULT_FROM_WIN32(error);
966 
967         for (DWORD dwIndex = 0; !error; ++dwIndex)
968         {
969             WCHAR Name[2 * MAX_PATH];
970             DWORD cchName = _countof(Name);
971             FILETIME LastWritten;
972             error = RegEnumKeyExW(hKey, dwIndex, Name, &cchName, NULL, NULL, NULL, &LastWritten);
973             if (error)
974                 break;
975 
976             HKEY hSubKey;
977             error = RegOpenKeyExW(hKey, Name, 0, KEY_QUERY_VALUE, &hSubKey);
978             if (error)
979                 break;
980 
981             FILETIME FileTime;
982             DWORD dwCount;
983             error = SHELL_ReadSingleUnreadMailCount(hSubKey, &dwCount, &FileTime, NULL, 0);
984             if (!error && (!pFileTime || CompareFileTime(&FileTime, pFileTime) >= 0))
985                 *pdwCount += dwCount;
986 
987             RegCloseKey(hSubKey);
988         }
989 
990         if (error == ERROR_NO_MORE_ITEMS)
991             error = ERROR_SUCCESS;
992     }
993 
994     RegCloseKey(hKey);
995 
996     return error ? HRESULT_FROM_WIN32(error) : S_OK;
997 }
998 
999 /*************************************************************************
1000  *  SHSetUnreadMailCountW [SHELL32.336]
1001  *
1002  * @see https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shsetunreadmailcountw
1003  */
1004 EXTERN_C
1005 HRESULT WINAPI
SHSetUnreadMailCountW(_In_ PCWSTR pszMailAddress,_In_ DWORD dwCount,_In_ PCWSTR pszShellExecuteCommand)1006 SHSetUnreadMailCountW(
1007     _In_ PCWSTR pszMailAddress,
1008     _In_ DWORD dwCount,
1009     _In_ PCWSTR pszShellExecuteCommand)
1010 {
1011     CString strKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\";
1012     strKey += pszMailAddress;
1013 
1014     HKEY hKey;
1015     DWORD dwDisposition;
1016     LSTATUS error = RegCreateKeyExW(HKEY_CURRENT_USER, strKey, 0, NULL, 0, KEY_SET_VALUE, NULL,
1017                                     &hKey, &dwDisposition);
1018     if (error)
1019         return HRESULT_FROM_WIN32(error);
1020 
1021     error = RegSetValueExW(hKey, L"MessageCount", 0, REG_DWORD, (PBYTE)&dwCount, sizeof(dwCount));
1022     if (error)
1023     {
1024         RegCloseKey(hKey);
1025         return HRESULT_FROM_WIN32(error);
1026     }
1027 
1028     FILETIME FileTime;
1029     GetSystemTimeAsFileTime(&FileTime);
1030 
1031     error = RegSetValueExW(hKey, L"TimeStamp", 0, REG_BINARY, (PBYTE)&FileTime, sizeof(FileTime));
1032     if (error)
1033     {
1034         RegCloseKey(hKey);
1035         return HRESULT_FROM_WIN32(error);
1036     }
1037 
1038     WCHAR szBuff[2 * MAX_PATH];
1039     if (!PathUnExpandEnvStringsW(pszShellExecuteCommand, szBuff, _countof(szBuff)))
1040     {
1041         HRESULT hr = StringCchCopyW(szBuff, _countof(szBuff), pszShellExecuteCommand);
1042         if (FAILED_UNEXPECTEDLY(hr))
1043         {
1044             RegCloseKey(hKey);
1045             return hr;
1046         }
1047     }
1048 
1049     DWORD cbValue = (lstrlenW(szBuff) + 1) * sizeof(WCHAR);
1050     error = RegSetValueExW(hKey, L"Application", 0, REG_SZ, (PBYTE)szBuff, cbValue);
1051 
1052     RegCloseKey(hKey);
1053     return (error ? HRESULT_FROM_WIN32(error) : S_OK);
1054 }
1055 
1056 /*************************************************************************
1057  *                SheRemoveQuotesA (SHELL32.@)
1058  */
1059 EXTERN_C LPSTR
1060 WINAPI
SheRemoveQuotesA(LPSTR psz)1061 SheRemoveQuotesA(LPSTR psz)
1062 {
1063     PCHAR pch;
1064 
1065     if (*psz == '"')
1066     {
1067         for (pch = psz + 1; *pch && *pch != '"'; ++pch)
1068         {
1069             *(pch - 1) = *pch;
1070         }
1071 
1072         if (*pch == '"')
1073             *(pch - 1) = ANSI_NULL;
1074     }
1075 
1076     return psz;
1077 }
1078 
1079 /*************************************************************************
1080  *                SheRemoveQuotesW (SHELL32.@)
1081  *
1082  * ExtractAssociatedIconExW uses this function.
1083  */
1084 EXTERN_C LPWSTR
1085 WINAPI
SheRemoveQuotesW(LPWSTR psz)1086 SheRemoveQuotesW(LPWSTR psz)
1087 {
1088     PWCHAR pch;
1089 
1090     if (*psz == L'"')
1091     {
1092         for (pch = psz + 1; *pch && *pch != L'"'; ++pch)
1093         {
1094             *(pch - 1) = *pch;
1095         }
1096 
1097         if (*pch == L'"')
1098             *(pch - 1) = UNICODE_NULL;
1099     }
1100 
1101     return psz;
1102 }
1103 
1104 /*************************************************************************
1105  *  SHEnumerateUnreadMailAccountsW [SHELL32.287]
1106  *
1107  * @see https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shenumerateunreadmailaccountsw
1108  */
1109 EXTERN_C
1110 HRESULT WINAPI
SHEnumerateUnreadMailAccountsW(_In_opt_ HKEY hKeyUser,_In_ DWORD dwIndex,_Out_writes_ (cchMailAddress)PWSTR pszMailAddress,_In_ INT cchMailAddress)1111 SHEnumerateUnreadMailAccountsW(
1112     _In_opt_ HKEY hKeyUser,
1113     _In_ DWORD dwIndex,
1114     _Out_writes_(cchMailAddress) PWSTR pszMailAddress,
1115     _In_ INT cchMailAddress)
1116 {
1117     if (!hKeyUser)
1118         hKeyUser = HKEY_CURRENT_USER;
1119 
1120     HKEY hKey;
1121     LSTATUS error = RegOpenKeyExW(hKeyUser,
1122                                   L"Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail",
1123                                   0, KEY_ENUMERATE_SUB_KEYS, &hKey);
1124     if (error)
1125         return HRESULT_FROM_WIN32(error);
1126 
1127     FILETIME FileTime;
1128     error = RegEnumKeyExW(hKey, dwIndex, pszMailAddress, (PDWORD)&cchMailAddress, NULL, NULL,
1129                           NULL, &FileTime);
1130     if (error)
1131         *pszMailAddress = UNICODE_NULL;
1132 
1133     RegCloseKey(hKey);
1134     return error ? HRESULT_FROM_WIN32(error) : S_OK;
1135 }
1136 
1137 /*************************************************************************
1138  *  SHFindComputer [SHELL32.91]
1139  *
1140  * Invokes the shell search in My Computer. Used in SHFindFiles.
1141  * Two parameters are ignored.
1142  */
1143 EXTERN_C BOOL
1144 WINAPI
SHFindComputer(LPCITEMIDLIST pidlRoot,LPCITEMIDLIST pidlSavedSearch)1145 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch)
1146 {
1147     UNREFERENCED_PARAMETER(pidlRoot);
1148     UNREFERENCED_PARAMETER(pidlSavedSearch);
1149 
1150     TRACE("%p %p\n", pidlRoot, pidlSavedSearch);
1151 
1152     IContextMenu *pCM;
1153     HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER,
1154                                   IID_IContextMenu, (void **)&pCM);
1155     if (FAILED(hr))
1156     {
1157         ERR("0x%08X\n", hr);
1158         return hr;
1159     }
1160 
1161     CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) };
1162     InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}";
1163     InvokeInfo.nShow = SW_SHOWNORMAL;
1164     hr = pCM->InvokeCommand(&InvokeInfo);
1165     pCM->Release();
1166 
1167     return SUCCEEDED(hr);
1168 }
1169 
1170 static HRESULT
Int64ToStr(_In_ LONGLONG llValue,_Out_writes_ (cchValue)LPWSTR pszValue,_In_ UINT cchValue)1171 Int64ToStr(
1172     _In_ LONGLONG llValue,
1173     _Out_writes_(cchValue) LPWSTR pszValue,
1174     _In_ UINT cchValue)
1175 {
1176     WCHAR szBuff[40];
1177     UINT ich = 0, ichValue;
1178 #if (WINVER >= _WIN32_WINNT_VISTA)
1179     BOOL bMinus = (llValue < 0);
1180 
1181     if (bMinus)
1182         llValue = -llValue;
1183 #endif
1184 
1185     if (cchValue <= 0)
1186         return E_FAIL;
1187 
1188     do
1189     {
1190         szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10));
1191         llValue /= 10;
1192     } while (llValue != 0 && ich < _countof(szBuff) - 1);
1193 
1194 #if (WINVER >= _WIN32_WINNT_VISTA)
1195     if (bMinus && ich < _countof(szBuff))
1196         szBuff[ich++] = '-';
1197 #endif
1198 
1199     for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue)
1200     {
1201         --ich;
1202         pszValue[ichValue] = szBuff[ich];
1203     }
1204 
1205     if (ichValue >= cchValue)
1206     {
1207         pszValue[cchValue - 1] = UNICODE_NULL;
1208         return E_FAIL;
1209     }
1210 
1211     pszValue[ichValue] = UNICODE_NULL;
1212     return S_OK;
1213 }
1214 
1215 static VOID
Int64GetNumFormat(_Out_ NUMBERFMTW * pDest,_In_opt_ const NUMBERFMTW * pSrc,_In_ DWORD dwNumberFlags,_Out_writes_ (cchDecimal)LPWSTR pszDecimal,_In_ INT cchDecimal,_Out_writes_ (cchThousand)LPWSTR pszThousand,_In_ INT cchThousand)1216 Int64GetNumFormat(
1217     _Out_ NUMBERFMTW *pDest,
1218     _In_opt_ const NUMBERFMTW *pSrc,
1219     _In_ DWORD dwNumberFlags,
1220     _Out_writes_(cchDecimal) LPWSTR pszDecimal,
1221     _In_ INT cchDecimal,
1222     _Out_writes_(cchThousand) LPWSTR pszThousand,
1223     _In_ INT cchThousand)
1224 {
1225     WCHAR szBuff[20];
1226 
1227     if (pSrc)
1228         *pDest = *pSrc;
1229     else
1230         dwNumberFlags = 0;
1231 
1232     if (!(dwNumberFlags & FMT_USE_NUMDIGITS))
1233     {
1234         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff));
1235         pDest->NumDigits = StrToIntW(szBuff);
1236     }
1237 
1238     if (!(dwNumberFlags & FMT_USE_LEADZERO))
1239     {
1240         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff));
1241         pDest->LeadingZero = StrToIntW(szBuff);
1242     }
1243 
1244     if (!(dwNumberFlags & FMT_USE_GROUPING))
1245     {
1246         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff));
1247         pDest->Grouping = StrToIntW(szBuff);
1248     }
1249 
1250     if (!(dwNumberFlags & FMT_USE_DECIMAL))
1251     {
1252         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal);
1253         pDest->lpDecimalSep = pszDecimal;
1254     }
1255 
1256     if (!(dwNumberFlags & FMT_USE_THOUSAND))
1257     {
1258         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand);
1259         pDest->lpThousandSep = pszThousand;
1260     }
1261 
1262     if (!(dwNumberFlags & FMT_USE_NEGNUMBER))
1263     {
1264         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff));
1265         pDest->NegativeOrder = StrToIntW(szBuff);
1266     }
1267 }
1268 
1269 /*************************************************************************
1270  *  Int64ToString [SHELL32.209]
1271  *
1272  * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php
1273  */
1274 EXTERN_C
1275 INT WINAPI
Int64ToString(_In_ LONGLONG llValue,_Out_writes_ (cchOut)LPWSTR pszOut,_In_ UINT cchOut,_In_ BOOL bUseFormat,_In_opt_ const NUMBERFMTW * pNumberFormat,_In_ DWORD dwNumberFlags)1276 Int64ToString(
1277     _In_ LONGLONG llValue,
1278     _Out_writes_(cchOut) LPWSTR pszOut,
1279     _In_ UINT cchOut,
1280     _In_ BOOL bUseFormat,
1281     _In_opt_ const NUMBERFMTW *pNumberFormat,
1282     _In_ DWORD dwNumberFlags)
1283 {
1284     INT ret;
1285     NUMBERFMTW NumFormat;
1286     WCHAR szValue[80], szDecimalSep[6], szThousandSep[6];
1287 
1288     Int64ToStr(llValue, szValue, _countof(szValue));
1289 
1290     if (bUseFormat)
1291     {
1292         Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags,
1293                           szDecimalSep, _countof(szDecimalSep),
1294                           szThousandSep, _countof(szThousandSep));
1295         ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut);
1296         if (ret)
1297             --ret;
1298         return ret;
1299     }
1300 
1301     if (FAILED(StringCchCopyW(pszOut, cchOut, szValue)))
1302         return 0;
1303 
1304     return lstrlenW(pszOut);
1305 }
1306 
1307 /*************************************************************************
1308  *  LargeIntegerToString [SHELL32.210]
1309  *
1310  * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php
1311  */
1312 EXTERN_C
1313 INT WINAPI
LargeIntegerToString(_In_ const LARGE_INTEGER * pLargeInt,_Out_writes_ (cchOut)LPWSTR pszOut,_In_ UINT cchOut,_In_ BOOL bUseFormat,_In_opt_ const NUMBERFMTW * pNumberFormat,_In_ DWORD dwNumberFlags)1314 LargeIntegerToString(
1315     _In_ const LARGE_INTEGER *pLargeInt,
1316     _Out_writes_(cchOut) LPWSTR pszOut,
1317     _In_ UINT cchOut,
1318     _In_ BOOL bUseFormat,
1319     _In_opt_ const NUMBERFMTW *pNumberFormat,
1320     _In_ DWORD dwNumberFlags)
1321 {
1322     return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat,
1323                          pNumberFormat, dwNumberFlags);
1324 }
1325 
1326 /*************************************************************************
1327  *  CopyStreamUI [SHELL32.726]
1328  *
1329  * Copy a stream to another stream with optional progress display.
1330  */
1331 EXTERN_C
1332 HRESULT WINAPI
CopyStreamUI(_In_ IStream * pSrc,_Out_ IStream * pDst,_Inout_opt_ IProgressDialog * pProgress,_In_opt_ DWORDLONG dwlSize)1333 CopyStreamUI(
1334     _In_ IStream *pSrc,
1335     _Out_ IStream *pDst,
1336     _Inout_opt_ IProgressDialog *pProgress,
1337     _In_opt_ DWORDLONG dwlSize)
1338 {
1339     HRESULT hr = E_FAIL;
1340     DWORD cbBuff, cbRead, dwSizeToWrite;
1341     DWORDLONG cbDone;
1342     LPVOID pBuff;
1343     CComHeapPtr<BYTE> pHeapPtr;
1344     STATSTG Stat;
1345     BYTE abBuff[1024];
1346 
1347     TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize);
1348 
1349     if (dwlSize == 0) // Invalid size?
1350     {
1351         // Get the stream size
1352         ZeroMemory(&Stat, sizeof(Stat));
1353         if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME)))
1354             pProgress = NULL; // No size info. Disable progress
1355         else
1356             dwlSize = Stat.cbSize.QuadPart;
1357     }
1358 
1359     if (!pProgress) // Progress is disabled?
1360     {
1361         ULARGE_INTEGER uliSize;
1362 
1363         if (dwlSize > 0)
1364             uliSize.QuadPart = dwlSize;
1365         else
1366             uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE;
1367 
1368         return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch
1369     }
1370 
1371     // Allocate the buffer if necessary
1372     if (dwlSize > 0 && dwlSize <= sizeof(abBuff))
1373     {
1374         cbBuff = sizeof(abBuff);
1375         pBuff = abBuff;
1376     }
1377     else
1378     {
1379 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000
1380         cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE;
1381         if (pHeapPtr.AllocateBytes(cbBuff))
1382         {
1383             pBuff = pHeapPtr;
1384         }
1385         else // Low memory?
1386         {
1387             cbBuff = sizeof(abBuff);
1388             pBuff = abBuff;
1389         }
1390 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE
1391     }
1392 
1393     // Start reading
1394     LARGE_INTEGER zero;
1395     zero.QuadPart = 0;
1396     pSrc->Seek(zero, 0, NULL);
1397     pDst->Seek(zero, 0, NULL);
1398     cbDone = 0;
1399     pProgress->SetProgress64(cbDone, dwlSize);
1400 
1401     // Repeat reading and writing until goal
1402     for (;;)
1403     {
1404         hr = pSrc->Read(pBuff, cbBuff, &cbRead);
1405         if (FAILED(hr))
1406             break;
1407 
1408         // Calculate the size to write
1409         if (dwlSize > 0)
1410             dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead);
1411         else
1412             dwSizeToWrite = cbRead;
1413 
1414         if (dwSizeToWrite == 0) // No need to write?
1415         {
1416             hr = S_OK;
1417             break;
1418         }
1419 
1420         hr = pDst->Write(pBuff, dwSizeToWrite, NULL);
1421         if (hr != S_OK)
1422             break;
1423 
1424         cbDone += dwSizeToWrite;
1425 
1426         if (pProgress->HasUserCancelled()) // Cancelled?
1427         {
1428             hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
1429             break;
1430         }
1431         pProgress->SetProgress64(cbDone, dwlSize);
1432 
1433         if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal?
1434         {
1435             hr = S_OK;
1436             break;
1437         }
1438     }
1439 
1440     return hr;
1441 }
1442 
1443 /*************************************************************************
1444  *  Activate_RunDLL [SHELL32.105]
1445  *
1446  * Unlocks the foreground window and allows the shell window to become the
1447  * foreground window. Every parameter is unused.
1448  */
1449 EXTERN_C
1450 BOOL WINAPI
Activate_RunDLL(_In_ HWND hwnd,_In_ HINSTANCE hinst,_In_ LPCWSTR cmdline,_In_ INT cmdshow)1451 Activate_RunDLL(
1452     _In_ HWND hwnd,
1453     _In_ HINSTANCE hinst,
1454     _In_ LPCWSTR cmdline,
1455     _In_ INT cmdshow)
1456 {
1457     DWORD dwProcessID;
1458 
1459     UNREFERENCED_PARAMETER(hwnd);
1460     UNREFERENCED_PARAMETER(hinst);
1461     UNREFERENCED_PARAMETER(cmdline);
1462     UNREFERENCED_PARAMETER(cmdshow);
1463 
1464     TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline);
1465 
1466     GetWindowThreadProcessId(GetShellWindow(), &dwProcessID);
1467     return AllowSetForegroundWindow(dwProcessID);
1468 }
1469 
1470 /*************************************************************************
1471  *                SHStartNetConnectionDialogA (SHELL32.12)
1472  *
1473  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga
1474  */
1475 EXTERN_C
1476 HRESULT WINAPI
SHStartNetConnectionDialogA(_In_ HWND hwnd,_In_ LPCSTR pszRemoteName,_In_ DWORD dwType)1477 SHStartNetConnectionDialogA(
1478     _In_ HWND hwnd,
1479     _In_ LPCSTR pszRemoteName,
1480     _In_ DWORD dwType)
1481 {
1482     LPCWSTR pszRemoteNameW = NULL;
1483     CStringW strRemoteNameW;
1484 
1485     TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType);
1486 
1487     if (pszRemoteName)
1488     {
1489         strRemoteNameW = pszRemoteName;
1490         pszRemoteNameW = strRemoteNameW;
1491     }
1492 
1493     return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType);
1494 }
1495 
1496 /*************************************************************************
1497  * Helper functions for PathIsEqualOrSubFolder
1498  */
1499 
1500 static INT
DynamicPathCommonPrefixW(_In_ LPCWSTR lpszPath1,_In_ LPCWSTR lpszPath2,_Out_ CStringW & strPath)1501 DynamicPathCommonPrefixW(
1502     _In_ LPCWSTR lpszPath1,
1503     _In_ LPCWSTR lpszPath2,
1504     _Out_ CStringW& strPath)
1505 {
1506     SIZE_T cchPath1 = wcslen(lpszPath1);
1507     SIZE_T cchPath2 = wcslen(lpszPath2);
1508     LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16);
1509     INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath);
1510     strPath.ReleaseBuffer();
1511     return ret;
1512 }
1513 
1514 EXTERN_C HRESULT WINAPI
1515 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax);
1516 
1517 static HRESULT
DynamicSHGetPathFromIDListW(_In_ LPCITEMIDLIST pidl,_Out_ CStringW & strPath)1518 DynamicSHGetPathFromIDListW(
1519     _In_ LPCITEMIDLIST pidl,
1520     _Out_ CStringW& strPath)
1521 {
1522     HRESULT hr;
1523 
1524     for (UINT cchPath = MAX_PATH;; cchPath *= 2)
1525     {
1526         LPWSTR lpszPath = strPath.GetBuffer(cchPath);
1527         if (!lpszPath)
1528             return E_OUTOFMEMORY;
1529 
1530         hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath);
1531         strPath.ReleaseBuffer();
1532 
1533         if (hr != E_NOT_SUFFICIENT_BUFFER)
1534             break;
1535 
1536         if (cchPath >= MAXUINT / 2)
1537         {
1538             hr = E_FAIL;
1539             break;
1540         }
1541     }
1542 
1543     if (FAILED(hr))
1544         strPath.Empty();
1545 
1546     return hr;
1547 }
1548 
1549 static HRESULT
DynamicSHGetSpecialFolderPathW(_In_ HWND hwndOwner,_Out_ CStringW & strPath,_In_ INT nCSIDL,_In_ BOOL bCreate)1550 DynamicSHGetSpecialFolderPathW(
1551     _In_ HWND hwndOwner,
1552     _Out_ CStringW& strPath,
1553     _In_ INT nCSIDL,
1554     _In_ BOOL bCreate)
1555 {
1556     LPITEMIDLIST pidl;
1557     HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl);
1558     if (SUCCEEDED(hr))
1559     {
1560         hr = DynamicSHGetPathFromIDListW(pidl, strPath);
1561         CoTaskMemFree(pidl);
1562     }
1563 
1564     if (FAILED(hr))
1565         strPath.Empty();
1566     else if (bCreate)
1567         CreateDirectoryW(strPath, NULL);
1568 
1569     return hr;
1570 }
1571 
1572 static VOID
DynamicPathRemoveBackslashW(_Out_ CStringW & strPath)1573 DynamicPathRemoveBackslashW(
1574     _Out_ CStringW& strPath)
1575 {
1576     INT nLength = strPath.GetLength();
1577     if (nLength > 0 && strPath[nLength - 1] == L'\\')
1578         strPath = strPath.Left(nLength - 1);
1579 }
1580 
1581 /*************************************************************************
1582  *                PathIsEqualOrSubFolder (SHELL32.755)
1583  */
1584 EXTERN_C
1585 BOOL WINAPI
PathIsEqualOrSubFolder(_In_ LPCWSTR pszPath1OrCSIDL,_In_ LPCWSTR pszPath2)1586 PathIsEqualOrSubFolder(
1587     _In_ LPCWSTR pszPath1OrCSIDL,
1588     _In_ LPCWSTR pszPath2)
1589 {
1590     CStringW strCommon, strPath1;
1591 
1592     TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2));
1593 
1594     if (IS_INTRESOURCE(pszPath1OrCSIDL))
1595     {
1596         DynamicSHGetSpecialFolderPathW(
1597             NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE);
1598     }
1599     else
1600     {
1601         strPath1 = pszPath1OrCSIDL;
1602     }
1603 
1604     DynamicPathRemoveBackslashW(strPath1);
1605 
1606     if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon))
1607         return FALSE;
1608 
1609     return strPath1.CompareNoCase(strCommon) == 0;
1610 }
1611 
1612 /*************************************************************************
1613  *  SHGetRealIDL [SHELL32.98]
1614  */
1615 EXTERN_C
1616 HRESULT WINAPI
SHGetRealIDL(_In_ IShellFolder * psf,_In_ PCUITEMID_CHILD pidlSimple,_Outptr_ PITEMID_CHILD * ppidlReal)1617 SHGetRealIDL(
1618     _In_ IShellFolder *psf,
1619     _In_ PCUITEMID_CHILD pidlSimple,
1620     _Outptr_ PITEMID_CHILD *ppidlReal)
1621 {
1622     HRESULT hr;
1623     STRRET strret;
1624     WCHAR szPath[MAX_PATH];
1625     SFGAOF attrs;
1626 
1627     *ppidlReal = NULL;
1628 
1629     hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_INFOLDER | SHGDN_FORPARSING,
1630                                        &strret, 0);
1631     if (FAILED_UNEXPECTEDLY(hr))
1632         return hr;
1633 
1634     hr = StrRetToBufW(&strret, pidlSimple, szPath, _countof(szPath));
1635     if (FAILED_UNEXPECTEDLY(hr))
1636         return hr;
1637 
1638     attrs = SFGAO_FILESYSTEM;
1639     hr = psf->GetAttributesOf(1, &pidlSimple, &attrs);
1640     if (SUCCEEDED(hr) && !(attrs & SFGAO_FILESYSTEM))
1641         return SHILClone(pidlSimple, ppidlReal);
1642 
1643     hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL);
1644     if (hr == E_INVALIDARG || hr == E_NOTIMPL)
1645         return SHILClone(pidlSimple, ppidlReal);
1646 
1647     return hr;
1648 }
1649 
1650 EXTERN_C HRESULT
IUnknown_InitializeCommand(_In_ IUnknown * pUnk,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB)1651 IUnknown_InitializeCommand(
1652     _In_ IUnknown *pUnk,
1653     _In_ PCWSTR pszCommandName,
1654     _In_opt_ IPropertyBag *pPB)
1655 {
1656     HRESULT hr;
1657     CComPtr<IInitializeCommand> pIC;
1658     if (SUCCEEDED(hr = pUnk->QueryInterface(IID_PPV_ARG(IInitializeCommand, &pIC))))
1659         hr = pIC->Initialize(pszCommandName, pPB);
1660     return hr;
1661 }
1662 
1663 EXTERN_C HRESULT
InvokeIExecuteCommand(_In_ IExecuteCommand * pEC,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB,_In_opt_ IShellItemArray * pSIA,_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,_In_opt_ IUnknown * pSite)1664 InvokeIExecuteCommand(
1665     _In_ IExecuteCommand *pEC,
1666     _In_ PCWSTR pszCommandName,
1667     _In_opt_ IPropertyBag *pPB,
1668     _In_opt_ IShellItemArray *pSIA,
1669     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1670     _In_opt_ IUnknown *pSite)
1671 {
1672     if (!pEC)
1673         return E_INVALIDARG;
1674 
1675     if (pSite)
1676         IUnknown_SetSite(pEC, pSite);
1677     IUnknown_InitializeCommand(pEC, pszCommandName, pPB);
1678 
1679     CComPtr<IObjectWithSelection> pOWS;
1680     if (pSIA && SUCCEEDED(pEC->QueryInterface(IID_PPV_ARG(IObjectWithSelection, &pOWS))))
1681         pOWS->SetSelection(pSIA);
1682 
1683     DWORD dwKeyState = 0, fMask = pICI ? pICI->fMask : 0;
1684     pEC->SetNoShowUI((fMask & CMIC_MASK_FLAG_NO_UI) != 0);
1685     pEC->SetShowWindow(pICI ? pICI->nShow : SW_SHOW);
1686     if (fMask & CMIC_MASK_SHIFT_DOWN)
1687         dwKeyState |= MK_SHIFT;
1688     if (fMask & CMIC_MASK_CONTROL_DOWN)
1689         dwKeyState |= MK_CONTROL;
1690     pEC->SetKeyState(dwKeyState);
1691     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpDirectoryW)
1692         pEC->SetDirectory(pICI->lpDirectoryW);
1693     if ((fMask & CMIC_MASK_UNICODE) && pICI->lpParametersW)
1694         pEC->SetParameters(pICI->lpParametersW);
1695     if (fMask & CMIC_MASK_PTINVOKE)
1696         pEC->SetPosition(pICI->ptInvoke);
1697 
1698     HRESULT hr = pEC->Execute();
1699     if (pSite)
1700         IUnknown_SetSite(pEC, NULL);
1701     return hr;
1702 }
1703 
1704 EXTERN_C HRESULT
InvokeIExecuteCommandWithDataObject(_In_ IExecuteCommand * pEC,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB,_In_ IDataObject * pDO,_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,_In_opt_ IUnknown * pSite)1705 InvokeIExecuteCommandWithDataObject(
1706     _In_ IExecuteCommand *pEC,
1707     _In_ PCWSTR pszCommandName,
1708     _In_opt_ IPropertyBag *pPB,
1709     _In_ IDataObject *pDO,
1710     _In_opt_ LPCMINVOKECOMMANDINFOEX pICI,
1711     _In_opt_ IUnknown *pSite)
1712 {
1713     CComPtr<IShellItemArray> pSIA;
1714     HRESULT hr = SHCreateShellItemArrayFromDataObject(pDO, IID_PPV_ARG(IShellItemArray, &pSIA));
1715     return SUCCEEDED(hr) ? InvokeIExecuteCommand(pEC, pszCommandName, pPB, pSIA, pICI, pSite) : hr;
1716 }
1717 
1718 static HRESULT
GetCommandStringA(_In_ IContextMenu * pCM,_In_ UINT_PTR Id,_In_ UINT GCS,_Out_writes_ (cchMax)LPSTR Buf,_In_ UINT cchMax)1719 GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax)
1720 {
1721     HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax);
1722     if (FAILED(hr))
1723     {
1724         WCHAR buf[MAX_PATH];
1725         hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf));
1726         if (SUCCEEDED(hr))
1727             hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL;
1728     }
1729     return hr;
1730 }
1731 
1732 UINT
GetDfmCmd(_In_ IContextMenu * pCM,_In_ LPCSTR verba)1733 GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba)
1734 {
1735     CHAR buf[MAX_PATH];
1736     if (IS_INTRESOURCE(verba))
1737     {
1738         if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf))))
1739             return 0;
1740         verba = buf;
1741     }
1742     return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0
1743 }
1744 
1745 HRESULT
SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI,const CMVERBMAP * pMap)1746 SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap)
1747 {
1748     LPCSTR pVerbA = pICI->lpVerb;
1749     CHAR buf[MAX_PATH];
1750     LPCMINVOKECOMMANDINFOEX pICIX = (LPCMINVOKECOMMANDINFOEX)pICI;
1751     if (IsUnicode(*pICIX) && !IS_INTRESOURCE(pICIX->lpVerbW))
1752     {
1753         if (SHUnicodeToAnsi(pICIX->lpVerbW, buf, _countof(buf)))
1754             pVerbA = buf;
1755     }
1756 
1757     if (IS_INTRESOURCE(pVerbA))
1758         return LOWORD(pVerbA);
1759     for (SIZE_T i = 0; pMap[i].Verb; ++i)
1760     {
1761         assert(SUCCEEDED((int)(pMap[i].CmdId))); // The id must be >= 0 and ideally in the 0..0x7fff range
1762         if (!lstrcmpiA(pMap[i].Verb, pVerbA) && pVerbA[0])
1763             return pMap[i].CmdId;
1764     }
1765     return E_FAIL;
1766 }
1767 
1768 static const CMVERBMAP*
FindVerbMapEntry(UINT_PTR CmdId,const CMVERBMAP * pMap)1769 FindVerbMapEntry(UINT_PTR CmdId, const CMVERBMAP *pMap)
1770 {
1771     for (SIZE_T i = 0; pMap[i].Verb; ++i)
1772     {
1773         if (pMap[i].CmdId == CmdId)
1774             return &pMap[i];
1775     }
1776     return NULL;
1777 }
1778 
1779 HRESULT
SHELL_GetCommandStringImpl(SIZE_T CmdId,UINT uFlags,LPSTR Buf,UINT cchBuf,const CMVERBMAP * pMap)1780 SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const CMVERBMAP *pMap)
1781 {
1782     const CMVERBMAP* pEntry;
1783     switch (uFlags | GCS_UNICODE)
1784     {
1785         case GCS_VALIDATEW:
1786         case GCS_VERBW:
1787             pEntry = FindVerbMapEntry(CmdId, pMap);
1788             if ((uFlags | GCS_UNICODE) == GCS_VERBW)
1789             {
1790                 if (!pEntry)
1791                     return E_INVALIDARG;
1792                 else if (uFlags & GCS_UNICODE)
1793                     return SHAnsiToUnicode(pEntry->Verb, (LPWSTR)Buf, cchBuf) ? S_OK : E_FAIL;
1794                 else
1795                     return StringCchCopyA(Buf, cchBuf, pEntry->Verb);
1796             }
1797             return pEntry ? S_OK : S_FALSE; // GCS_VALIDATE
1798     }
1799     return E_NOTIMPL;
1800 }
1801 
1802 HRESULT
SHELL_CreateShell32DefaultExtractIcon(int IconIndex,REFIID riid,LPVOID * ppvOut)1803 SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut)
1804 {
1805     CComPtr<IDefaultExtractIconInit> initIcon;
1806     HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon));
1807     if (FAILED_UNEXPECTEDLY(hr))
1808         return hr;
1809     initIcon->SetNormalIcon(swShell32Name, IconIndex);
1810     return initIcon->QueryInterface(riid, ppvOut);
1811 }
1812 
1813 /*************************************************************************
1814  *  SHIsBadInterfacePtr [SHELL32.84]
1815  *
1816  * Retired in 6.0 from Windows Vista and higher.
1817  */
1818 EXTERN_C
1819 BOOL WINAPI
SHIsBadInterfacePtr(_In_ LPCVOID pv,_In_ UINT_PTR ucb)1820 SHIsBadInterfacePtr(
1821     _In_ LPCVOID pv,
1822     _In_ UINT_PTR ucb)
1823 {
1824     struct CUnknownVtbl
1825     {
1826         HRESULT (STDMETHODCALLTYPE *QueryInterface)(REFIID riid, LPVOID *ppvObj);
1827         ULONG (STDMETHODCALLTYPE *AddRef)();
1828         ULONG (STDMETHODCALLTYPE *Release)();
1829     };
1830     struct CUnknown { CUnknownVtbl *lpVtbl; };
1831     const CUnknown *punk = reinterpret_cast<const CUnknown *>(pv);
1832     return !punk || IsBadReadPtr(punk, sizeof(punk->lpVtbl)) ||
1833            IsBadReadPtr(punk->lpVtbl, ucb) ||
1834            IsBadCodePtr((FARPROC)punk->lpVtbl->Release);
1835 }
1836 
1837 /*************************************************************************
1838  *  SHGetUserDisplayName [SHELL32.241]
1839  *
1840  * @see https://undoc.airesoft.co.uk/shell32.dll/SHGetUserDisplayName.php
1841  */
1842 EXTERN_C
1843 HRESULT WINAPI
SHGetUserDisplayName(_Out_writes_to_ (* puSize,* puSize)PWSTR pName,_Inout_ PULONG puSize)1844 SHGetUserDisplayName(
1845     _Out_writes_to_(*puSize, *puSize) PWSTR pName,
1846     _Inout_ PULONG puSize)
1847 {
1848     if (!pName || !puSize)
1849         return E_INVALIDARG;
1850 
1851     if (GetUserNameExW(NameDisplay, pName, puSize))
1852         return S_OK;
1853 
1854     LONG error = GetLastError(); // for ERROR_NONE_MAPPED
1855     HRESULT hr = HRESULT_FROM_WIN32(error);
1856 
1857     WCHAR UserName[MAX_PATH];
1858     DWORD cchUserName = _countof(UserName);
1859     if (!GetUserNameW(UserName, &cchUserName))
1860         return HRESULT_FROM_WIN32(GetLastError());
1861 
1862     // Was the user name not available in the specified format (NameDisplay)?
1863     if (error == ERROR_NONE_MAPPED)
1864     {
1865         // Try to get the user name by using Network API
1866         PUSER_INFO_2 UserInfo;
1867         DWORD NetError = NetUserGetInfo(NULL, UserName, 2, (PBYTE*)&UserInfo);
1868         if (NetError)
1869         {
1870             hr = HRESULT_FROM_WIN32(NetError);
1871         }
1872         else
1873         {
1874             if (UserInfo->usri2_full_name)
1875             {
1876                 hr = StringCchCopyW(pName, *puSize, UserInfo->usri2_full_name);
1877                 if (SUCCEEDED(hr))
1878                 {
1879                     // Include the NUL-terminator
1880                     *puSize = lstrlenW(UserInfo->usri2_full_name) + 1;
1881                 }
1882             }
1883 
1884             NetApiBufferFree(UserInfo);
1885         }
1886     }
1887 
1888     if (FAILED(hr))
1889     {
1890         hr = StringCchCopyW(pName, *puSize, UserName);
1891         if (SUCCEEDED(hr))
1892             *puSize = cchUserName;
1893     }
1894 
1895     return hr;
1896 }
1897 
1898 // Skip leading backslashes
1899 static PCWSTR
SHELL_SkipServerSlashes(_In_ PCWSTR pszPath)1900 SHELL_SkipServerSlashes(
1901     _In_ PCWSTR pszPath)
1902 {
1903     PCWSTR pch;
1904     for (pch = pszPath; *pch == L'\\'; ++pch)
1905         ;
1906     return pch;
1907 }
1908 
1909 // The registry key for server computer descriptions cache
1910 #define COMPUTER_DESCRIPTIONS_KEY \
1911     L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComputerDescriptions"
1912 
1913 // Get server computer description from cache
1914 static HRESULT
SHELL_GetCachedComputerDescription(_Out_writes_z_ (cchDescMax)PWSTR pszDesc,_In_ DWORD cchDescMax,_In_ PCWSTR pszServerName)1915 SHELL_GetCachedComputerDescription(
1916     _Out_writes_z_(cchDescMax) PWSTR pszDesc,
1917     _In_ DWORD cchDescMax,
1918     _In_ PCWSTR pszServerName)
1919 {
1920     cchDescMax *= sizeof(WCHAR);
1921     DWORD error = SHGetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY,
1922                               SHELL_SkipServerSlashes(pszServerName), NULL, pszDesc, &cchDescMax);
1923     return HRESULT_FROM_WIN32(error);
1924 }
1925 
1926 // Do cache a server computer description
1927 static VOID
SHELL_CacheComputerDescription(_In_ PCWSTR pszServerName,_In_ PCWSTR pszDesc)1928 SHELL_CacheComputerDescription(
1929     _In_ PCWSTR pszServerName,
1930     _In_ PCWSTR pszDesc)
1931 {
1932     if (!pszDesc)
1933         return;
1934 
1935     SIZE_T cbDesc = (wcslen(pszDesc) + 1) * sizeof(WCHAR);
1936     SHSetValueW(HKEY_CURRENT_USER, COMPUTER_DESCRIPTIONS_KEY,
1937                 SHELL_SkipServerSlashes(pszServerName), REG_SZ, pszDesc, (DWORD)cbDesc);
1938 }
1939 
1940 // Get real server computer description
1941 static HRESULT
SHELL_GetComputerDescription(_Out_writes_z_ (cchDescMax)PWSTR pszDesc,_In_ SIZE_T cchDescMax,_In_ PWSTR pszServerName)1942 SHELL_GetComputerDescription(
1943     _Out_writes_z_(cchDescMax) PWSTR pszDesc,
1944     _In_ SIZE_T cchDescMax,
1945     _In_ PWSTR pszServerName)
1946 {
1947     PSERVER_INFO_101 bufptr;
1948     NET_API_STATUS error = NetServerGetInfo(pszServerName, 101, (PBYTE*)&bufptr);
1949     HRESULT hr = (error > 0) ? HRESULT_FROM_WIN32(error) : error;
1950     if (FAILED_UNEXPECTEDLY(hr))
1951         return hr;
1952 
1953     PCWSTR comment = bufptr->sv101_comment;
1954     if (comment && comment[0])
1955         StringCchCopyW(pszDesc, cchDescMax, comment);
1956     else
1957         hr = E_FAIL;
1958 
1959     NetApiBufferFree(bufptr);
1960     return hr;
1961 }
1962 
1963 // Build computer display name
1964 static HRESULT
SHELL_BuildDisplayMachineName(_Out_writes_z_ (cchNameMax)PWSTR pszName,_In_ DWORD cchNameMax,_In_ PCWSTR pszServerName,_In_ PCWSTR pszDescription)1965 SHELL_BuildDisplayMachineName(
1966     _Out_writes_z_(cchNameMax) PWSTR pszName,
1967     _In_ DWORD cchNameMax,
1968     _In_ PCWSTR pszServerName,
1969     _In_ PCWSTR pszDescription)
1970 {
1971     if (!pszDescription || !*pszDescription)
1972         return E_FAIL;
1973 
1974     PCWSTR pszFormat = (SHRestricted(REST_ALLOWCOMMENTTOGGLE) ? L"%2 (%1)" : L"%1 (%2)");
1975     PCWSTR args[] = { pszDescription , SHELL_SkipServerSlashes(pszServerName) };
1976     return (FormatMessageW(FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING,
1977                            pszFormat, 0, 0, pszName, cchNameMax, (va_list *)args) ? S_OK : E_FAIL);
1978 }
1979 
1980 /*************************************************************************
1981  *  SHGetComputerDisplayNameW [SHELL32.752]
1982  */
1983 EXTERN_C
1984 HRESULT WINAPI
SHGetComputerDisplayNameW(_In_opt_ PWSTR pszServerName,_In_ DWORD dwFlags,_Out_writes_z_ (cchNameMax)PWSTR pszName,_In_ DWORD cchNameMax)1985 SHGetComputerDisplayNameW(
1986     _In_opt_ PWSTR pszServerName,
1987     _In_ DWORD dwFlags,
1988     _Out_writes_z_(cchNameMax) PWSTR pszName,
1989     _In_ DWORD cchNameMax)
1990 {
1991     WCHAR szDesc[256], szCompName[MAX_COMPUTERNAME_LENGTH + 1];
1992 
1993     // If no server name is specified, retrieve the local computer name
1994     if (!pszServerName)
1995     {
1996         // Use computer name as server name
1997         DWORD cchCompName = _countof(szCompName);
1998         if (!GetComputerNameW(szCompName, &cchCompName))
1999             return E_FAIL;
2000         pszServerName = szCompName;
2001 
2002         // Don't use the cache for the local machine
2003         dwFlags |= SHGCDN_NOCACHE;
2004     }
2005 
2006     // Get computer description from cache if necessary
2007     HRESULT hr = E_FAIL;
2008     if (!(dwFlags & SHGCDN_NOCACHE))
2009         hr = SHELL_GetCachedComputerDescription(szDesc, _countof(szDesc), pszServerName);
2010 
2011     // Actually retrieve the computer description if it is not in the cache
2012     if (FAILED(hr))
2013     {
2014         hr = SHELL_GetComputerDescription(szDesc, _countof(szDesc), pszServerName);
2015         if (FAILED(hr))
2016             szDesc[0] = UNICODE_NULL;
2017 
2018         // Cache the description if necessary
2019         if (!(dwFlags & SHGCDN_NOCACHE))
2020             SHELL_CacheComputerDescription(pszServerName, szDesc);
2021     }
2022 
2023     // If getting the computer description failed, store the server name only
2024     if (FAILED(hr) || !szDesc[0])
2025     {
2026         if (dwFlags & SHGCDN_NOSERVERNAME)
2027             return hr; // Bail out if no server name is requested
2028 
2029         StringCchCopyW(pszName, cchNameMax, SHELL_SkipServerSlashes(pszServerName));
2030         return S_OK;
2031     }
2032 
2033     // If no server name is requested, store the description only
2034     if (dwFlags & SHGCDN_NOSERVERNAME)
2035     {
2036         StringCchCopyW(pszName, cchNameMax, szDesc);
2037         return S_OK;
2038     }
2039 
2040     // Build a string like "Description (SERVERNAME)"
2041     return SHELL_BuildDisplayMachineName(pszName, cchNameMax, pszServerName, szDesc);
2042 }
2043