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