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