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