Reactos
at master 2191 lines 58 kB view raw
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}