1 /* 2 * PROJECT: shell32 3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) 4 * PURPOSE: Utility functions 5 * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "precomp.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(shell); 11 12 static BOOL 13 OpenEffectiveToken( 14 _In_ DWORD DesiredAccess, 15 _Out_ HANDLE *phToken) 16 { 17 BOOL ret; 18 19 if (phToken == NULL) 20 { 21 SetLastError(ERROR_INVALID_PARAMETER); 22 return FALSE; 23 } 24 25 *phToken = NULL; 26 27 ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken); 28 if (!ret && GetLastError() == ERROR_NO_TOKEN) 29 ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken); 30 31 return ret; 32 } 33 34 HRESULT 35 Shell_TranslateIDListAlias( 36 _In_ LPCITEMIDLIST pidl, 37 _In_ HANDLE hToken, 38 _Out_ LPITEMIDLIST *ppidlAlias, 39 _In_ DWORD dwFlags) 40 { 41 return E_FAIL; //FIXME 42 } 43 44 BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName) 45 { 46 CComPtr<IUnknown> punk; 47 if (!pBindCtx || FAILED(pBindCtx->GetObjectParam(const_cast<LPWSTR>(pszName), &punk))) 48 return FALSE; 49 return TRUE; 50 } 51 52 BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid) 53 { 54 if (!pbc) 55 return FALSE; 56 57 BIND_OPTS BindOps = { sizeof(BindOps) }; 58 if (SUCCEEDED(pbc->GetBindOptions(&BindOps)) && BindOps.grfFlags == OLECONTF_LINKS) 59 return TRUE; 60 61 return pclsid && SHSkipJunction(pbc, pclsid); 62 } 63 64 HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW **ppFindData) 65 { 66 CComPtr<IUnknown> punk; 67 CComPtr<IFileSystemBindData> pBindData; 68 69 if (!pBindCtx || FAILED(pBindCtx->GetObjectParam((LPWSTR)STR_FILE_SYS_BIND_DATA, &punk))) 70 return S_FALSE; 71 72 if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData)))) 73 return S_FALSE; 74 75 HRESULT hr = S_OK; 76 if (ppFindData) 77 { 78 *ppFindData = (WIN32_FIND_DATAW*)LocalAlloc(LPTR, sizeof(WIN32_FIND_DATAW)); 79 if (*ppFindData) 80 pBindData->GetFindData(*ppFindData); 81 else 82 hr = E_OUTOFMEMORY; 83 } 84 85 return hr; 86 } 87 88 BOOL Shell_FailForceReturn(_In_ HRESULT hr) 89 { 90 DWORD code = HRESULT_CODE(hr); 91 92 switch (code) 93 { 94 case ERROR_BAD_NETPATH: 95 case ERROR_BAD_NET_NAME: 96 case ERROR_CANCELLED: 97 return TRUE; 98 99 default: 100 return (ERROR_FILE_NOT_FOUND <= code && code <= ERROR_PATH_NOT_FOUND); 101 } 102 } 103 104 HRESULT 105 SHBindToObjectEx( 106 _In_opt_ IShellFolder *pShellFolder, 107 _In_ LPCITEMIDLIST pidl, 108 _In_opt_ IBindCtx *pBindCtx, 109 _In_ REFIID riid, 110 _Out_ void **ppvObj) 111 { 112 CComPtr<IShellFolder> psfDesktop; 113 114 *ppvObj = NULL; 115 116 if (!pShellFolder) 117 { 118 SHGetDesktopFolder(&psfDesktop); 119 if (!psfDesktop) 120 return E_FAIL; 121 122 pShellFolder = psfDesktop; 123 } 124 125 HRESULT hr; 126 if (_ILIsDesktop(pidl)) 127 hr = pShellFolder->QueryInterface(riid, ppvObj); 128 else 129 hr = pShellFolder->BindToObject(pidl, pBindCtx, riid, ppvObj); 130 131 if (SUCCEEDED(hr) && !*ppvObj) 132 hr = E_FAIL; 133 134 return hr; 135 } 136 137 HRESULT SHBindToObject( 138 _In_opt_ IShellFolder *psf, 139 _In_ LPCITEMIDLIST pidl, 140 _In_ REFIID riid, 141 _Out_ void **ppvObj) 142 { 143 return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj); 144 } 145 146 HRESULT 147 Shell_DisplayNameOf( 148 _In_ IShellFolder *psf, 149 _In_ LPCITEMIDLIST pidl, 150 _In_ DWORD dwFlags, 151 _Out_ LPWSTR pszBuf, 152 _In_ UINT cchBuf) 153 { 154 *pszBuf = UNICODE_NULL; 155 STRRET sr; 156 HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr); 157 if (FAILED(hr)) 158 return hr; 159 return StrRetToBufW(&sr, pidl, pszBuf, cchBuf); 160 } 161 162 DWORD 163 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes) 164 { 165 LPCITEMIDLIST pidlLast; 166 167 if (psf) 168 { 169 psf->AddRef(); 170 pidlLast = pidl; 171 } 172 else 173 { 174 SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 175 } 176 177 if (!psf) 178 return 0; 179 180 DWORD oldAttrs = dwAttributes; 181 if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes))) 182 dwAttributes = 0; 183 else 184 dwAttributes &= oldAttrs; 185 186 if ((dwAttributes & SFGAO_FOLDER) && 187 (dwAttributes & SFGAO_STREAM) && 188 !(dwAttributes & SFGAO_STORAGEANCESTOR) && 189 (oldAttrs & SFGAO_STORAGEANCESTOR) && 190 (SHGetObjectCompatFlags(psf, NULL) & 0x200)) 191 { 192 dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR); 193 dwAttributes |= SFGAO_STORAGEANCESTOR; 194 } 195 196 if (psf) 197 psf->Release(); 198 199 return dwAttributes; 200 } 201 202 HRESULT SHCoInitializeAnyApartment(VOID) 203 { 204 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 205 if (FAILED(hr)) 206 hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); 207 return hr; 208 } 209 210 HRESULT 211 SHGetNameAndFlagsW( 212 _In_ LPCITEMIDLIST pidl, 213 _In_ DWORD dwFlags, 214 _Out_opt_ LPWSTR pszText, 215 _In_ UINT cchBuf, 216 _Inout_opt_ DWORD *pdwAttributes) 217 { 218 if (pszText) 219 *pszText = UNICODE_NULL; 220 221 HRESULT hrCoInit = SHCoInitializeAnyApartment(); 222 223 CComPtr<IShellFolder> psfFolder; 224 LPCITEMIDLIST ppidlLast; 225 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast); 226 if (SUCCEEDED(hr)) 227 { 228 if (pszText) 229 hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf); 230 231 if (SUCCEEDED(hr)) 232 { 233 if (pdwAttributes) 234 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes); 235 } 236 } 237 238 if (SUCCEEDED(hrCoInit)) 239 CoUninitialize(); 240 241 return hr; 242 } 243 244 EXTERN_C HWND 245 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx) 246 { 247 HWND hWnd = NULL; 248 249 CComPtr<IUnknown> punk; 250 if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk))) 251 IUnknown_GetWindow(punk, &hWnd); 252 253 return hWnd; 254 } 255 256 class CDummyOleWindow : public IOleWindow 257 { 258 protected: 259 LONG m_cRefs; 260 HWND m_hWnd; 261 262 public: 263 CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { } 264 virtual ~CDummyOleWindow() { } 265 266 // IUnknown methods 267 STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override 268 { 269 static const QITAB c_tab[] = 270 { 271 QITABENT(CDummyOleWindow, IOleWindow), 272 { NULL } 273 }; 274 return ::QISearch(this, c_tab, riid, ppvObj); 275 } 276 STDMETHODIMP_(ULONG) AddRef() override 277 { 278 return ++m_cRefs; 279 } 280 STDMETHODIMP_(ULONG) Release() override 281 { 282 if (--m_cRefs == 0) 283 { 284 delete this; 285 return 0; 286 } 287 return m_cRefs; 288 } 289 290 // IOleWindow methods 291 STDMETHODIMP GetWindow(HWND *phWnd) override 292 { 293 *phWnd = m_hWnd; 294 if (!m_hWnd) 295 return E_NOTIMPL; 296 return S_OK; 297 } 298 STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override 299 { 300 return E_NOTIMPL; 301 } 302 }; 303 304 EXTERN_C HRESULT 305 BindCtx_RegisterObjectParam( 306 _In_ IBindCtx *pBindCtx, 307 _In_ LPOLESTR pszKey, 308 _In_opt_ IUnknown *punk, 309 _Out_ LPBC *ppbc) 310 { 311 HRESULT hr = S_OK; 312 CDummyOleWindow *pUnknown = NULL; 313 314 *ppbc = pBindCtx; 315 316 if (pBindCtx) 317 { 318 pBindCtx->AddRef(); 319 } 320 else 321 { 322 hr = CreateBindCtx(0, ppbc); 323 if (FAILED(hr)) 324 return hr; 325 } 326 327 if (!punk) 328 punk = pUnknown = new CDummyOleWindow(); 329 330 hr = (*ppbc)->RegisterObjectParam(pszKey, punk); 331 332 if (pUnknown) 333 pUnknown->Release(); 334 335 if (FAILED(hr)) 336 { 337 (*ppbc)->Release(); 338 *ppbc = NULL; 339 } 340 341 return hr; 342 } 343 344 /************************************************************************* 345 * SHSetFolderPathA (SHELL32.231) 346 * 347 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha 348 */ 349 EXTERN_C 350 HRESULT WINAPI 351 SHSetFolderPathA( 352 _In_ INT csidl, 353 _In_ HANDLE hToken, 354 _In_ DWORD dwFlags, 355 _In_ LPCSTR pszPath) 356 { 357 TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath)); 358 CStringW strPathW(pszPath); 359 return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW); 360 } 361 362 /************************************************************************* 363 * PathIsSlowA (SHELL32.240) 364 * 365 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa 366 */ 367 EXTERN_C 368 BOOL WINAPI 369 PathIsSlowA( 370 _In_ LPCSTR pszFile, 371 _In_ DWORD dwAttr) 372 { 373 TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr); 374 CStringW strFileW(pszFile); 375 return PathIsSlowW(strFileW, dwAttr); 376 } 377 378 /************************************************************************* 379 * ExtractIconResInfoA (SHELL32.221) 380 */ 381 EXTERN_C 382 WORD WINAPI 383 ExtractIconResInfoA( 384 _In_ HANDLE hHandle, 385 _In_ LPCSTR lpFileName, 386 _In_ WORD wIndex, 387 _Out_ LPWORD lpSize, 388 _Out_ LPHANDLE lpIcon) 389 { 390 TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon); 391 392 if (!lpFileName) 393 return 0; 394 395 CStringW strFileNameW(lpFileName); 396 return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon); 397 } 398 399 /************************************************************************* 400 * ShortSizeFormatW (SHELL32.204) 401 */ 402 EXTERN_C 403 LPWSTR WINAPI 404 ShortSizeFormatW( 405 _In_ DWORD dwNumber, 406 _Out_writes_(0x8FFF) LPWSTR pszBuffer) 407 { 408 TRACE("(%lu, %p)\n", dwNumber, pszBuffer); 409 return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF); 410 } 411 412 /************************************************************************* 413 * SHOpenEffectiveToken (SHELL32.235) 414 */ 415 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken) 416 { 417 TRACE("%p\n", phToken); 418 return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken); 419 } 420 421 /************************************************************************* 422 * SHGetUserSessionId (SHELL32.248) 423 */ 424 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken) 425 { 426 DWORD dwSessionId, dwLength; 427 BOOL bOpenToken = FALSE; 428 429 TRACE("%p\n", hToken); 430 431 if (!hToken) 432 bOpenToken = SHOpenEffectiveToken(&hToken); 433 434 if (!hToken || 435 !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength)) 436 { 437 dwSessionId = 0; 438 } 439 440 if (bOpenToken) 441 CloseHandle(hToken); 442 443 return dwSessionId; 444 } 445 446 /************************************************************************* 447 * SHInvokePrivilegedFunctionW (SHELL32.246) 448 */ 449 EXTERN_C 450 HRESULT WINAPI 451 SHInvokePrivilegedFunctionW( 452 _In_ LPCWSTR pszName, 453 _In_ PRIVILEGED_FUNCTION fn, 454 _In_opt_ LPARAM lParam) 455 { 456 TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam); 457 458 if (!pszName || !fn) 459 return E_INVALIDARG; 460 461 HANDLE hToken = NULL; 462 TOKEN_PRIVILEGES NewPriv, PrevPriv; 463 BOOL bAdjusted = FALSE; 464 465 if (SHOpenEffectiveToken(&hToken) && 466 ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid)) 467 { 468 NewPriv.PrivilegeCount = 1; 469 NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 470 471 DWORD dwReturnSize; 472 bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv, 473 sizeof(PrevPriv), &PrevPriv, &dwReturnSize); 474 } 475 476 HRESULT hr = fn(lParam); 477 478 if (bAdjusted) 479 ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL); 480 481 if (hToken) 482 ::CloseHandle(hToken); 483 484 return hr; 485 } 486 487 /************************************************************************* 488 * SHTestTokenPrivilegeW (SHELL32.236) 489 * 490 * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php 491 */ 492 EXTERN_C 493 BOOL WINAPI 494 SHTestTokenPrivilegeW( 495 _In_opt_ HANDLE hToken, 496 _In_ LPCWSTR lpName) 497 { 498 LUID Luid; 499 DWORD dwLength; 500 PTOKEN_PRIVILEGES pTokenPriv; 501 HANDLE hNewToken = NULL; 502 BOOL ret = FALSE; 503 504 TRACE("(%p, %s)\n", hToken, debugstr_w(lpName)); 505 506 if (!lpName) 507 return FALSE; 508 509 if (!hToken) 510 { 511 if (!SHOpenEffectiveToken(&hNewToken)) 512 goto Quit; 513 514 if (!hNewToken) 515 return FALSE; 516 517 hToken = hNewToken; 518 } 519 520 if (!LookupPrivilegeValueW(NULL, lpName, &Luid)) 521 return FALSE; 522 523 dwLength = 0; 524 if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength)) 525 goto Quit; 526 527 pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength); 528 if (!pTokenPriv) 529 goto Quit; 530 531 if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength)) 532 { 533 UINT iPriv, cPrivs; 534 cPrivs = pTokenPriv->PrivilegeCount; 535 for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv) 536 { 537 ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid); 538 } 539 } 540 541 LocalFree(pTokenPriv); 542 543 Quit: 544 if (hToken == hNewToken) 545 CloseHandle(hNewToken); 546 547 return ret; 548 } 549 550 BOOL IsShutdownAllowed(VOID) 551 { 552 return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME); 553 } 554 555 /************************************************************************* 556 * IsSuspendAllowed (SHELL32.53) 557 */ 558 BOOL WINAPI IsSuspendAllowed(VOID) 559 { 560 TRACE("()\n"); 561 return IsShutdownAllowed() && IsPwrSuspendAllowed(); 562 } 563 564 /************************************************************************* 565 * SHGetShellStyleHInstance (SHELL32.749) 566 */ 567 EXTERN_C HINSTANCE 568 WINAPI 569 SHGetShellStyleHInstance(VOID) 570 { 571 HINSTANCE hInst = NULL; 572 WCHAR szPath[MAX_PATH], szColorName[100]; 573 HRESULT hr; 574 CStringW strShellStyle; 575 576 TRACE("SHGetShellStyleHInstance called\n"); 577 578 /* First, attempt to load the shellstyle dll from the current active theme */ 579 hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0); 580 if (FAILED(hr)) 581 goto DoDefault; 582 583 /* Strip the theme filename */ 584 PathRemoveFileSpecW(szPath); 585 586 strShellStyle = szPath; 587 strShellStyle += L"\\Shell\\"; 588 strShellStyle += szColorName; 589 strShellStyle += L"\\ShellStyle.dll"; 590 591 hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE); 592 if (hInst) 593 return hInst; 594 595 /* Otherwise, use the version stored in the System32 directory */ 596 DoDefault: 597 if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll", 598 szPath, _countof(szPath))) 599 { 600 ERR("Expand failed\n"); 601 return NULL; 602 } 603 return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE); 604 } 605 606 /************************************************************************* 607 * SHCreatePropertyBag (SHELL32.715) 608 */ 609 EXTERN_C HRESULT 610 WINAPI 611 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj) 612 { 613 return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj); 614 } 615 616 /************************************************************************* 617 * SheRemoveQuotesA (SHELL32.@) 618 */ 619 EXTERN_C LPSTR 620 WINAPI 621 SheRemoveQuotesA(LPSTR psz) 622 { 623 PCHAR pch; 624 625 if (*psz == '"') 626 { 627 for (pch = psz + 1; *pch && *pch != '"'; ++pch) 628 { 629 *(pch - 1) = *pch; 630 } 631 632 if (*pch == '"') 633 *(pch - 1) = ANSI_NULL; 634 } 635 636 return psz; 637 } 638 639 /************************************************************************* 640 * SheRemoveQuotesW (SHELL32.@) 641 * 642 * ExtractAssociatedIconExW uses this function. 643 */ 644 EXTERN_C LPWSTR 645 WINAPI 646 SheRemoveQuotesW(LPWSTR psz) 647 { 648 PWCHAR pch; 649 650 if (*psz == L'"') 651 { 652 for (pch = psz + 1; *pch && *pch != L'"'; ++pch) 653 { 654 *(pch - 1) = *pch; 655 } 656 657 if (*pch == L'"') 658 *(pch - 1) = UNICODE_NULL; 659 } 660 661 return psz; 662 } 663 664 /************************************************************************* 665 * SHFindComputer [SHELL32.91] 666 * 667 * Invokes the shell search in My Computer. Used in SHFindFiles. 668 * Two parameters are ignored. 669 */ 670 EXTERN_C BOOL 671 WINAPI 672 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch) 673 { 674 UNREFERENCED_PARAMETER(pidlRoot); 675 UNREFERENCED_PARAMETER(pidlSavedSearch); 676 677 TRACE("%p %p\n", pidlRoot, pidlSavedSearch); 678 679 IContextMenu *pCM; 680 HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, 681 IID_IContextMenu, (void **)&pCM); 682 if (FAILED(hr)) 683 { 684 ERR("0x%08X\n", hr); 685 return hr; 686 } 687 688 CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) }; 689 InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}"; 690 InvokeInfo.nShow = SW_SHOWNORMAL; 691 hr = pCM->InvokeCommand(&InvokeInfo); 692 pCM->Release(); 693 694 return SUCCEEDED(hr); 695 } 696 697 static HRESULT 698 Int64ToStr( 699 _In_ LONGLONG llValue, 700 _Out_writes_(cchValue) LPWSTR pszValue, 701 _In_ UINT cchValue) 702 { 703 WCHAR szBuff[40]; 704 UINT ich = 0, ichValue; 705 #if (WINVER >= _WIN32_WINNT_VISTA) 706 BOOL bMinus = (llValue < 0); 707 708 if (bMinus) 709 llValue = -llValue; 710 #endif 711 712 if (cchValue <= 0) 713 return E_FAIL; 714 715 do 716 { 717 szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10)); 718 llValue /= 10; 719 } while (llValue != 0 && ich < _countof(szBuff) - 1); 720 721 #if (WINVER >= _WIN32_WINNT_VISTA) 722 if (bMinus && ich < _countof(szBuff)) 723 szBuff[ich++] = '-'; 724 #endif 725 726 for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue) 727 { 728 --ich; 729 pszValue[ichValue] = szBuff[ich]; 730 } 731 732 if (ichValue >= cchValue) 733 { 734 pszValue[cchValue - 1] = UNICODE_NULL; 735 return E_FAIL; 736 } 737 738 pszValue[ichValue] = UNICODE_NULL; 739 return S_OK; 740 } 741 742 static VOID 743 Int64GetNumFormat( 744 _Out_ NUMBERFMTW *pDest, 745 _In_opt_ const NUMBERFMTW *pSrc, 746 _In_ DWORD dwNumberFlags, 747 _Out_writes_(cchDecimal) LPWSTR pszDecimal, 748 _In_ INT cchDecimal, 749 _Out_writes_(cchThousand) LPWSTR pszThousand, 750 _In_ INT cchThousand) 751 { 752 WCHAR szBuff[20]; 753 754 if (pSrc) 755 *pDest = *pSrc; 756 else 757 dwNumberFlags = 0; 758 759 if (!(dwNumberFlags & FMT_USE_NUMDIGITS)) 760 { 761 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff)); 762 pDest->NumDigits = StrToIntW(szBuff); 763 } 764 765 if (!(dwNumberFlags & FMT_USE_LEADZERO)) 766 { 767 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff)); 768 pDest->LeadingZero = StrToIntW(szBuff); 769 } 770 771 if (!(dwNumberFlags & FMT_USE_GROUPING)) 772 { 773 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff)); 774 pDest->Grouping = StrToIntW(szBuff); 775 } 776 777 if (!(dwNumberFlags & FMT_USE_DECIMAL)) 778 { 779 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal); 780 pDest->lpDecimalSep = pszDecimal; 781 } 782 783 if (!(dwNumberFlags & FMT_USE_THOUSAND)) 784 { 785 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand); 786 pDest->lpThousandSep = pszThousand; 787 } 788 789 if (!(dwNumberFlags & FMT_USE_NEGNUMBER)) 790 { 791 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff)); 792 pDest->NegativeOrder = StrToIntW(szBuff); 793 } 794 } 795 796 /************************************************************************* 797 * Int64ToString [SHELL32.209] 798 * 799 * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php 800 */ 801 EXTERN_C 802 INT WINAPI 803 Int64ToString( 804 _In_ LONGLONG llValue, 805 _Out_writes_(cchOut) LPWSTR pszOut, 806 _In_ UINT cchOut, 807 _In_ BOOL bUseFormat, 808 _In_opt_ const NUMBERFMTW *pNumberFormat, 809 _In_ DWORD dwNumberFlags) 810 { 811 INT ret; 812 NUMBERFMTW NumFormat; 813 WCHAR szValue[80], szDecimalSep[6], szThousandSep[6]; 814 815 Int64ToStr(llValue, szValue, _countof(szValue)); 816 817 if (bUseFormat) 818 { 819 Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags, 820 szDecimalSep, _countof(szDecimalSep), 821 szThousandSep, _countof(szThousandSep)); 822 ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut); 823 if (ret) 824 --ret; 825 return ret; 826 } 827 828 if (FAILED(StringCchCopyW(pszOut, cchOut, szValue))) 829 return 0; 830 831 return lstrlenW(pszOut); 832 } 833 834 /************************************************************************* 835 * LargeIntegerToString [SHELL32.210] 836 * 837 * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php 838 */ 839 EXTERN_C 840 INT WINAPI 841 LargeIntegerToString( 842 _In_ const LARGE_INTEGER *pLargeInt, 843 _Out_writes_(cchOut) LPWSTR pszOut, 844 _In_ UINT cchOut, 845 _In_ BOOL bUseFormat, 846 _In_opt_ const NUMBERFMTW *pNumberFormat, 847 _In_ DWORD dwNumberFlags) 848 { 849 return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat, 850 pNumberFormat, dwNumberFlags); 851 } 852 853 /************************************************************************* 854 * CopyStreamUI [SHELL32.726] 855 * 856 * Copy a stream to another stream with optional progress display. 857 */ 858 EXTERN_C 859 HRESULT WINAPI 860 CopyStreamUI( 861 _In_ IStream *pSrc, 862 _Out_ IStream *pDst, 863 _Inout_opt_ IProgressDialog *pProgress, 864 _In_opt_ DWORDLONG dwlSize) 865 { 866 HRESULT hr = E_FAIL; 867 DWORD cbBuff, cbRead, dwSizeToWrite; 868 DWORDLONG cbDone; 869 LPVOID pBuff; 870 CComHeapPtr<BYTE> pHeapPtr; 871 STATSTG Stat; 872 BYTE abBuff[1024]; 873 874 TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize); 875 876 if (dwlSize == 0) // Invalid size? 877 { 878 // Get the stream size 879 ZeroMemory(&Stat, sizeof(Stat)); 880 if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME))) 881 pProgress = NULL; // No size info. Disable progress 882 else 883 dwlSize = Stat.cbSize.QuadPart; 884 } 885 886 if (!pProgress) // Progress is disabled? 887 { 888 ULARGE_INTEGER uliSize; 889 890 if (dwlSize > 0) 891 uliSize.QuadPart = dwlSize; 892 else 893 uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE; 894 895 return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch 896 } 897 898 // Allocate the buffer if necessary 899 if (dwlSize > 0 && dwlSize <= sizeof(abBuff)) 900 { 901 cbBuff = sizeof(abBuff); 902 pBuff = abBuff; 903 } 904 else 905 { 906 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000 907 cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE; 908 if (pHeapPtr.AllocateBytes(cbBuff)) 909 { 910 pBuff = pHeapPtr; 911 } 912 else // Low memory? 913 { 914 cbBuff = sizeof(abBuff); 915 pBuff = abBuff; 916 } 917 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE 918 } 919 920 // Start reading 921 LARGE_INTEGER zero; 922 zero.QuadPart = 0; 923 pSrc->Seek(zero, 0, NULL); 924 pDst->Seek(zero, 0, NULL); 925 cbDone = 0; 926 pProgress->SetProgress64(cbDone, dwlSize); 927 928 // Repeat reading and writing until goal 929 for (;;) 930 { 931 hr = pSrc->Read(pBuff, cbBuff, &cbRead); 932 if (FAILED(hr)) 933 break; 934 935 // Calculate the size to write 936 if (dwlSize > 0) 937 dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead); 938 else 939 dwSizeToWrite = cbRead; 940 941 if (dwSizeToWrite == 0) // No need to write? 942 { 943 hr = S_OK; 944 break; 945 } 946 947 hr = pDst->Write(pBuff, dwSizeToWrite, NULL); 948 if (hr != S_OK) 949 break; 950 951 cbDone += dwSizeToWrite; 952 953 if (pProgress->HasUserCancelled()) // Cancelled? 954 { 955 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 956 break; 957 } 958 pProgress->SetProgress64(cbDone, dwlSize); 959 960 if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal? 961 { 962 hr = S_OK; 963 break; 964 } 965 } 966 967 return hr; 968 } 969 970 /************************************************************************* 971 * SHOpenPropSheetA [SHELL32.707] 972 * 973 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta 974 */ 975 EXTERN_C 976 BOOL WINAPI 977 SHOpenPropSheetA( 978 _In_opt_ LPCSTR pszCaption, 979 _In_opt_ HKEY *ahKeys, 980 _In_ UINT cKeys, 981 _In_ const CLSID *pclsidDefault, 982 _In_ IDataObject *pDataObject, 983 _In_opt_ IShellBrowser *pShellBrowser, 984 _In_opt_ LPCSTR pszStartPage) 985 { 986 CStringW strStartPageW, strCaptionW; 987 LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL; 988 989 TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault, 990 pDataObject, pShellBrowser, debugstr_a(pszStartPage)); 991 992 if (pszCaption) 993 { 994 strStartPageW = pszCaption; 995 pszCaptionW = strCaptionW; 996 } 997 998 if (pszStartPage) 999 { 1000 strStartPageW = pszStartPage; 1001 pszStartPageW = strStartPageW; 1002 } 1003 1004 return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault, 1005 pDataObject, pShellBrowser, pszStartPageW); 1006 } 1007 1008 /************************************************************************* 1009 * Activate_RunDLL [SHELL32.105] 1010 * 1011 * Unlocks the foreground window and allows the shell window to become the 1012 * foreground window. Every parameter is unused. 1013 */ 1014 EXTERN_C 1015 BOOL WINAPI 1016 Activate_RunDLL( 1017 _In_ HWND hwnd, 1018 _In_ HINSTANCE hinst, 1019 _In_ LPCWSTR cmdline, 1020 _In_ INT cmdshow) 1021 { 1022 DWORD dwProcessID; 1023 1024 UNREFERENCED_PARAMETER(hwnd); 1025 UNREFERENCED_PARAMETER(hinst); 1026 UNREFERENCED_PARAMETER(cmdline); 1027 UNREFERENCED_PARAMETER(cmdshow); 1028 1029 TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline); 1030 1031 GetWindowThreadProcessId(GetShellWindow(), &dwProcessID); 1032 return AllowSetForegroundWindow(dwProcessID); 1033 } 1034 1035 /************************************************************************* 1036 * SHStartNetConnectionDialogA (SHELL32.12) 1037 * 1038 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga 1039 */ 1040 EXTERN_C 1041 HRESULT WINAPI 1042 SHStartNetConnectionDialogA( 1043 _In_ HWND hwnd, 1044 _In_ LPCSTR pszRemoteName, 1045 _In_ DWORD dwType) 1046 { 1047 LPCWSTR pszRemoteNameW = NULL; 1048 CStringW strRemoteNameW; 1049 1050 TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType); 1051 1052 if (pszRemoteName) 1053 { 1054 strRemoteNameW = pszRemoteName; 1055 pszRemoteNameW = strRemoteNameW; 1056 } 1057 1058 return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType); 1059 } 1060 1061 /************************************************************************* 1062 * Helper functions for PathIsEqualOrSubFolder 1063 */ 1064 1065 static INT 1066 DynamicPathCommonPrefixW( 1067 _In_ LPCWSTR lpszPath1, 1068 _In_ LPCWSTR lpszPath2, 1069 _Out_ CStringW& strPath) 1070 { 1071 SIZE_T cchPath1 = wcslen(lpszPath1); 1072 SIZE_T cchPath2 = wcslen(lpszPath2); 1073 LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16); 1074 INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath); 1075 strPath.ReleaseBuffer(); 1076 return ret; 1077 } 1078 1079 EXTERN_C HRESULT WINAPI 1080 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax); 1081 1082 static HRESULT 1083 DynamicSHGetPathFromIDListW( 1084 _In_ LPCITEMIDLIST pidl, 1085 _Out_ CStringW& strPath) 1086 { 1087 HRESULT hr; 1088 1089 for (UINT cchPath = MAX_PATH;; cchPath *= 2) 1090 { 1091 LPWSTR lpszPath = strPath.GetBuffer(cchPath); 1092 if (!lpszPath) 1093 return E_OUTOFMEMORY; 1094 1095 hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath); 1096 strPath.ReleaseBuffer(); 1097 1098 if (hr != E_NOT_SUFFICIENT_BUFFER) 1099 break; 1100 1101 if (cchPath >= MAXUINT / 2) 1102 { 1103 hr = E_FAIL; 1104 break; 1105 } 1106 } 1107 1108 if (FAILED(hr)) 1109 strPath.Empty(); 1110 1111 return hr; 1112 } 1113 1114 static HRESULT 1115 DynamicSHGetSpecialFolderPathW( 1116 _In_ HWND hwndOwner, 1117 _Out_ CStringW& strPath, 1118 _In_ INT nCSIDL, 1119 _In_ BOOL bCreate) 1120 { 1121 LPITEMIDLIST pidl; 1122 HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl); 1123 if (SUCCEEDED(hr)) 1124 { 1125 hr = DynamicSHGetPathFromIDListW(pidl, strPath); 1126 CoTaskMemFree(pidl); 1127 } 1128 1129 if (FAILED(hr)) 1130 strPath.Empty(); 1131 else if (bCreate) 1132 CreateDirectoryW(strPath, NULL); 1133 1134 return hr; 1135 } 1136 1137 static VOID 1138 DynamicPathRemoveBackslashW( 1139 _Out_ CStringW& strPath) 1140 { 1141 INT nLength = strPath.GetLength(); 1142 if (nLength > 0 && strPath[nLength - 1] == L'\\') 1143 strPath = strPath.Left(nLength - 1); 1144 } 1145 1146 /************************************************************************* 1147 * PathIsEqualOrSubFolder (SHELL32.755) 1148 */ 1149 EXTERN_C 1150 BOOL WINAPI 1151 PathIsEqualOrSubFolder( 1152 _In_ LPCWSTR pszPath1OrCSIDL, 1153 _In_ LPCWSTR pszPath2) 1154 { 1155 CStringW strCommon, strPath1; 1156 1157 TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2)); 1158 1159 if (IS_INTRESOURCE(pszPath1OrCSIDL)) 1160 { 1161 DynamicSHGetSpecialFolderPathW( 1162 NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE); 1163 } 1164 else 1165 { 1166 strPath1 = pszPath1OrCSIDL; 1167 } 1168 1169 DynamicPathRemoveBackslashW(strPath1); 1170 1171 if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon)) 1172 return FALSE; 1173 1174 return strPath1.CompareNoCase(strCommon) == 0; 1175 } 1176