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 /************************************************************************* 35 * SHSetFolderPathA (SHELL32.231) 36 * 37 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha 38 */ 39 EXTERN_C 40 HRESULT WINAPI 41 SHSetFolderPathA( 42 _In_ INT csidl, 43 _In_ HANDLE hToken, 44 _In_ DWORD dwFlags, 45 _In_ LPCSTR pszPath) 46 { 47 TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath)); 48 CStringW strPathW(pszPath); 49 return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW); 50 } 51 52 /************************************************************************* 53 * PathIsSlowA (SHELL32.240) 54 * 55 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa 56 */ 57 EXTERN_C 58 BOOL WINAPI 59 PathIsSlowA( 60 _In_ LPCSTR pszFile, 61 _In_ DWORD dwAttr) 62 { 63 TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr); 64 CStringW strFileW(pszFile); 65 return PathIsSlowW(strFileW, dwAttr); 66 } 67 68 /************************************************************************* 69 * ExtractIconResInfoA (SHELL32.221) 70 */ 71 EXTERN_C 72 WORD WINAPI 73 ExtractIconResInfoA( 74 _In_ HANDLE hHandle, 75 _In_ LPCSTR lpFileName, 76 _In_ WORD wIndex, 77 _Out_ LPWORD lpSize, 78 _Out_ LPHANDLE lpIcon) 79 { 80 TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon); 81 82 if (!lpFileName) 83 return 0; 84 85 CStringW strFileNameW(lpFileName); 86 return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon); 87 } 88 89 /************************************************************************* 90 * ShortSizeFormatW (SHELL32.204) 91 */ 92 EXTERN_C 93 LPWSTR WINAPI 94 ShortSizeFormatW( 95 _In_ DWORD dwNumber, 96 _Out_writes_(0x8FFF) LPWSTR pszBuffer) 97 { 98 TRACE("(%lu, %p)\n", dwNumber, pszBuffer); 99 return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF); 100 } 101 102 /************************************************************************* 103 * SHOpenEffectiveToken (SHELL32.235) 104 */ 105 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken) 106 { 107 TRACE("%p\n", phToken); 108 return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken); 109 } 110 111 /************************************************************************* 112 * SHGetUserSessionId (SHELL32.248) 113 */ 114 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken) 115 { 116 DWORD dwSessionId, dwLength; 117 BOOL bOpenToken = FALSE; 118 119 TRACE("%p\n", hToken); 120 121 if (!hToken) 122 bOpenToken = SHOpenEffectiveToken(&hToken); 123 124 if (!hToken || 125 !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength)) 126 { 127 dwSessionId = 0; 128 } 129 130 if (bOpenToken) 131 CloseHandle(hToken); 132 133 return dwSessionId; 134 } 135 136 /************************************************************************* 137 * SHInvokePrivilegedFunctionW (SHELL32.246) 138 */ 139 EXTERN_C 140 HRESULT WINAPI 141 SHInvokePrivilegedFunctionW( 142 _In_ LPCWSTR pszName, 143 _In_ PRIVILEGED_FUNCTION fn, 144 _In_opt_ LPARAM lParam) 145 { 146 TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam); 147 148 if (!pszName || !fn) 149 return E_INVALIDARG; 150 151 HANDLE hToken = NULL; 152 TOKEN_PRIVILEGES NewPriv, PrevPriv; 153 BOOL bAdjusted = FALSE; 154 155 if (SHOpenEffectiveToken(&hToken) && 156 ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid)) 157 { 158 NewPriv.PrivilegeCount = 1; 159 NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 160 161 DWORD dwReturnSize; 162 bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv, 163 sizeof(PrevPriv), &PrevPriv, &dwReturnSize); 164 } 165 166 HRESULT hr = fn(lParam); 167 168 if (bAdjusted) 169 ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL); 170 171 if (hToken) 172 ::CloseHandle(hToken); 173 174 return hr; 175 } 176 177 /************************************************************************* 178 * SHTestTokenPrivilegeW (SHELL32.236) 179 * 180 * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php 181 */ 182 EXTERN_C 183 BOOL WINAPI 184 SHTestTokenPrivilegeW( 185 _In_opt_ HANDLE hToken, 186 _In_ LPCWSTR lpName) 187 { 188 LUID Luid; 189 DWORD dwLength; 190 PTOKEN_PRIVILEGES pTokenPriv; 191 HANDLE hNewToken = NULL; 192 BOOL ret = FALSE; 193 194 TRACE("(%p, %s)\n", hToken, debugstr_w(lpName)); 195 196 if (!lpName) 197 return FALSE; 198 199 if (!hToken) 200 { 201 if (!SHOpenEffectiveToken(&hNewToken)) 202 goto Quit; 203 204 if (!hNewToken) 205 return FALSE; 206 207 hToken = hNewToken; 208 } 209 210 if (!LookupPrivilegeValueW(NULL, lpName, &Luid)) 211 return FALSE; 212 213 dwLength = 0; 214 if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength)) 215 goto Quit; 216 217 pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength); 218 if (!pTokenPriv) 219 goto Quit; 220 221 if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength)) 222 { 223 UINT iPriv, cPrivs; 224 cPrivs = pTokenPriv->PrivilegeCount; 225 for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv) 226 { 227 ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid); 228 } 229 } 230 231 LocalFree(pTokenPriv); 232 233 Quit: 234 if (hToken == hNewToken) 235 CloseHandle(hNewToken); 236 237 return ret; 238 } 239 240 BOOL IsShutdownAllowed(VOID) 241 { 242 return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME); 243 } 244 245 /************************************************************************* 246 * IsSuspendAllowed (SHELL32.53) 247 */ 248 BOOL WINAPI IsSuspendAllowed(VOID) 249 { 250 TRACE("()\n"); 251 return IsShutdownAllowed() && IsPwrSuspendAllowed(); 252 } 253 254 /************************************************************************* 255 * SHGetShellStyleHInstance (SHELL32.749) 256 */ 257 EXTERN_C HINSTANCE 258 WINAPI 259 SHGetShellStyleHInstance(VOID) 260 { 261 HINSTANCE hInst = NULL; 262 WCHAR szPath[MAX_PATH], szColorName[100]; 263 HRESULT hr; 264 CStringW strShellStyle; 265 266 TRACE("SHGetShellStyleHInstance called\n"); 267 268 /* First, attempt to load the shellstyle dll from the current active theme */ 269 hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0); 270 if (FAILED(hr)) 271 goto DoDefault; 272 273 /* Strip the theme filename */ 274 PathRemoveFileSpecW(szPath); 275 276 strShellStyle = szPath; 277 strShellStyle += L"\\Shell\\"; 278 strShellStyle += szColorName; 279 strShellStyle += L"\\ShellStyle.dll"; 280 281 hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE); 282 if (hInst) 283 return hInst; 284 285 /* Otherwise, use the version stored in the System32 directory */ 286 DoDefault: 287 if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll", 288 szPath, _countof(szPath))) 289 { 290 ERR("Expand failed\n"); 291 return NULL; 292 } 293 return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE); 294 } 295 296 /************************************************************************* 297 * SHCreatePropertyBag (SHELL32.715) 298 */ 299 EXTERN_C HRESULT 300 WINAPI 301 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj) 302 { 303 return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj); 304 } 305 306 /************************************************************************* 307 * SheRemoveQuotesA (SHELL32.@) 308 */ 309 EXTERN_C LPSTR 310 WINAPI 311 SheRemoveQuotesA(LPSTR psz) 312 { 313 PCHAR pch; 314 315 if (*psz == '"') 316 { 317 for (pch = psz + 1; *pch && *pch != '"'; ++pch) 318 { 319 *(pch - 1) = *pch; 320 } 321 322 if (*pch == '"') 323 *(pch - 1) = ANSI_NULL; 324 } 325 326 return psz; 327 } 328 329 /************************************************************************* 330 * SheRemoveQuotesW (SHELL32.@) 331 * 332 * ExtractAssociatedIconExW uses this function. 333 */ 334 EXTERN_C LPWSTR 335 WINAPI 336 SheRemoveQuotesW(LPWSTR psz) 337 { 338 PWCHAR pch; 339 340 if (*psz == L'"') 341 { 342 for (pch = psz + 1; *pch && *pch != L'"'; ++pch) 343 { 344 *(pch - 1) = *pch; 345 } 346 347 if (*pch == L'"') 348 *(pch - 1) = UNICODE_NULL; 349 } 350 351 return psz; 352 } 353 354 /************************************************************************* 355 * SHFindComputer [SHELL32.91] 356 * 357 * Invokes the shell search in My Computer. Used in SHFindFiles. 358 * Two parameters are ignored. 359 */ 360 EXTERN_C BOOL 361 WINAPI 362 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch) 363 { 364 UNREFERENCED_PARAMETER(pidlRoot); 365 UNREFERENCED_PARAMETER(pidlSavedSearch); 366 367 TRACE("%p %p\n", pidlRoot, pidlSavedSearch); 368 369 IContextMenu *pCM; 370 HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, 371 IID_IContextMenu, (void **)&pCM); 372 if (FAILED(hr)) 373 { 374 ERR("0x%08X\n", hr); 375 return hr; 376 } 377 378 CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) }; 379 InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}"; 380 InvokeInfo.nShow = SW_SHOWNORMAL; 381 hr = pCM->InvokeCommand(&InvokeInfo); 382 pCM->Release(); 383 384 return SUCCEEDED(hr); 385 } 386 387 static HRESULT 388 Int64ToStr( 389 _In_ LONGLONG llValue, 390 _Out_writes_(cchValue) LPWSTR pszValue, 391 _In_ UINT cchValue) 392 { 393 WCHAR szBuff[40]; 394 UINT ich = 0, ichValue; 395 #if (WINVER >= _WIN32_WINNT_VISTA) 396 BOOL bMinus = (llValue < 0); 397 398 if (bMinus) 399 llValue = -llValue; 400 #endif 401 402 if (cchValue <= 0) 403 return E_FAIL; 404 405 do 406 { 407 szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10)); 408 llValue /= 10; 409 } while (llValue != 0 && ich < _countof(szBuff) - 1); 410 411 #if (WINVER >= _WIN32_WINNT_VISTA) 412 if (bMinus && ich < _countof(szBuff)) 413 szBuff[ich++] = '-'; 414 #endif 415 416 for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue) 417 { 418 --ich; 419 pszValue[ichValue] = szBuff[ich]; 420 } 421 422 if (ichValue >= cchValue) 423 { 424 pszValue[cchValue - 1] = UNICODE_NULL; 425 return E_FAIL; 426 } 427 428 pszValue[ichValue] = UNICODE_NULL; 429 return S_OK; 430 } 431 432 static VOID 433 Int64GetNumFormat( 434 _Out_ NUMBERFMTW *pDest, 435 _In_opt_ const NUMBERFMTW *pSrc, 436 _In_ DWORD dwNumberFlags, 437 _Out_writes_(cchDecimal) LPWSTR pszDecimal, 438 _In_ INT cchDecimal, 439 _Out_writes_(cchThousand) LPWSTR pszThousand, 440 _In_ INT cchThousand) 441 { 442 WCHAR szBuff[20]; 443 444 if (pSrc) 445 *pDest = *pSrc; 446 else 447 dwNumberFlags = 0; 448 449 if (!(dwNumberFlags & FMT_USE_NUMDIGITS)) 450 { 451 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff)); 452 pDest->NumDigits = StrToIntW(szBuff); 453 } 454 455 if (!(dwNumberFlags & FMT_USE_LEADZERO)) 456 { 457 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff)); 458 pDest->LeadingZero = StrToIntW(szBuff); 459 } 460 461 if (!(dwNumberFlags & FMT_USE_GROUPING)) 462 { 463 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff)); 464 pDest->Grouping = StrToIntW(szBuff); 465 } 466 467 if (!(dwNumberFlags & FMT_USE_DECIMAL)) 468 { 469 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal); 470 pDest->lpDecimalSep = pszDecimal; 471 } 472 473 if (!(dwNumberFlags & FMT_USE_THOUSAND)) 474 { 475 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand); 476 pDest->lpThousandSep = pszThousand; 477 } 478 479 if (!(dwNumberFlags & FMT_USE_NEGNUMBER)) 480 { 481 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff)); 482 pDest->NegativeOrder = StrToIntW(szBuff); 483 } 484 } 485 486 /************************************************************************* 487 * Int64ToString [SHELL32.209] 488 * 489 * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php 490 */ 491 EXTERN_C 492 INT WINAPI 493 Int64ToString( 494 _In_ LONGLONG llValue, 495 _Out_writes_(cchOut) LPWSTR pszOut, 496 _In_ UINT cchOut, 497 _In_ BOOL bUseFormat, 498 _In_opt_ const NUMBERFMTW *pNumberFormat, 499 _In_ DWORD dwNumberFlags) 500 { 501 INT ret; 502 NUMBERFMTW NumFormat; 503 WCHAR szValue[80], szDecimalSep[6], szThousandSep[6]; 504 505 Int64ToStr(llValue, szValue, _countof(szValue)); 506 507 if (bUseFormat) 508 { 509 Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags, 510 szDecimalSep, _countof(szDecimalSep), 511 szThousandSep, _countof(szThousandSep)); 512 ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut); 513 if (ret) 514 --ret; 515 return ret; 516 } 517 518 if (FAILED(StringCchCopyW(pszOut, cchOut, szValue))) 519 return 0; 520 521 return lstrlenW(pszOut); 522 } 523 524 /************************************************************************* 525 * LargeIntegerToString [SHELL32.210] 526 * 527 * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php 528 */ 529 EXTERN_C 530 INT WINAPI 531 LargeIntegerToString( 532 _In_ const LARGE_INTEGER *pLargeInt, 533 _Out_writes_(cchOut) LPWSTR pszOut, 534 _In_ UINT cchOut, 535 _In_ BOOL bUseFormat, 536 _In_opt_ const NUMBERFMTW *pNumberFormat, 537 _In_ DWORD dwNumberFlags) 538 { 539 return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat, 540 pNumberFormat, dwNumberFlags); 541 } 542 543 /************************************************************************* 544 * CopyStreamUI [SHELL32.726] 545 * 546 * Copy a stream to another stream with optional progress display. 547 */ 548 EXTERN_C 549 HRESULT WINAPI 550 CopyStreamUI( 551 _In_ IStream *pSrc, 552 _Out_ IStream *pDst, 553 _Inout_opt_ IProgressDialog *pProgress, 554 _In_opt_ DWORDLONG dwlSize) 555 { 556 HRESULT hr = E_FAIL; 557 DWORD cbBuff, cbRead, dwSizeToWrite; 558 DWORDLONG cbDone; 559 LPVOID pBuff; 560 CComHeapPtr<BYTE> pHeapPtr; 561 STATSTG Stat; 562 BYTE abBuff[1024]; 563 564 TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize); 565 566 if (dwlSize == 0) // Invalid size? 567 { 568 // Get the stream size 569 ZeroMemory(&Stat, sizeof(Stat)); 570 if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME))) 571 pProgress = NULL; // No size info. Disable progress 572 else 573 dwlSize = Stat.cbSize.QuadPart; 574 } 575 576 if (!pProgress) // Progress is disabled? 577 { 578 ULARGE_INTEGER uliSize; 579 580 if (dwlSize > 0) 581 uliSize.QuadPart = dwlSize; 582 else 583 uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE; 584 585 return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch 586 } 587 588 // Allocate the buffer if necessary 589 if (dwlSize > 0 && dwlSize <= sizeof(abBuff)) 590 { 591 cbBuff = sizeof(abBuff); 592 pBuff = abBuff; 593 } 594 else 595 { 596 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000 597 cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE; 598 if (pHeapPtr.AllocateBytes(cbBuff)) 599 { 600 pBuff = pHeapPtr; 601 } 602 else // Low memory? 603 { 604 cbBuff = sizeof(abBuff); 605 pBuff = abBuff; 606 } 607 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE 608 } 609 610 // Start reading 611 LARGE_INTEGER zero; 612 zero.QuadPart = 0; 613 pSrc->Seek(zero, 0, NULL); 614 pDst->Seek(zero, 0, NULL); 615 cbDone = 0; 616 pProgress->SetProgress64(cbDone, dwlSize); 617 618 // Repeat reading and writing until goal 619 for (;;) 620 { 621 hr = pSrc->Read(pBuff, cbBuff, &cbRead); 622 if (FAILED(hr)) 623 break; 624 625 // Calculate the size to write 626 if (dwlSize > 0) 627 dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead); 628 else 629 dwSizeToWrite = cbRead; 630 631 if (dwSizeToWrite == 0) // No need to write? 632 { 633 hr = S_OK; 634 break; 635 } 636 637 hr = pDst->Write(pBuff, dwSizeToWrite, NULL); 638 if (hr != S_OK) 639 break; 640 641 cbDone += dwSizeToWrite; 642 643 if (pProgress->HasUserCancelled()) // Cancelled? 644 { 645 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 646 break; 647 } 648 pProgress->SetProgress64(cbDone, dwlSize); 649 650 if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal? 651 { 652 hr = S_OK; 653 break; 654 } 655 } 656 657 return hr; 658 } 659 660 /************************************************************************* 661 * SHOpenPropSheetA [SHELL32.707] 662 * 663 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta 664 */ 665 EXTERN_C 666 BOOL WINAPI 667 SHOpenPropSheetA( 668 _In_opt_ LPCSTR pszCaption, 669 _In_opt_ HKEY *ahKeys, 670 _In_ UINT cKeys, 671 _In_ const CLSID *pclsidDefault, 672 _In_ IDataObject *pDataObject, 673 _In_opt_ IShellBrowser *pShellBrowser, 674 _In_opt_ LPCSTR pszStartPage) 675 { 676 CStringW strStartPageW, strCaptionW; 677 LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL; 678 679 TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault, 680 pDataObject, pShellBrowser, debugstr_a(pszStartPage)); 681 682 if (pszCaption) 683 { 684 strStartPageW = pszCaption; 685 pszCaptionW = strCaptionW; 686 } 687 688 if (pszStartPage) 689 { 690 strStartPageW = pszStartPage; 691 pszStartPageW = strStartPageW; 692 } 693 694 return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault, 695 pDataObject, pShellBrowser, pszStartPageW); 696 } 697 698 /************************************************************************* 699 * Activate_RunDLL [SHELL32.105] 700 * 701 * Unlocks the foreground window and allows the shell window to become the 702 * foreground window. Every parameter is unused. 703 */ 704 EXTERN_C 705 BOOL WINAPI 706 Activate_RunDLL( 707 _In_ HWND hwnd, 708 _In_ HINSTANCE hinst, 709 _In_ LPCWSTR cmdline, 710 _In_ INT cmdshow) 711 { 712 DWORD dwProcessID; 713 714 UNREFERENCED_PARAMETER(hwnd); 715 UNREFERENCED_PARAMETER(hinst); 716 UNREFERENCED_PARAMETER(cmdline); 717 UNREFERENCED_PARAMETER(cmdshow); 718 719 TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline); 720 721 GetWindowThreadProcessId(GetShellWindow(), &dwProcessID); 722 return AllowSetForegroundWindow(dwProcessID); 723 } 724 725 /************************************************************************* 726 * SHStartNetConnectionDialogA (SHELL32.12) 727 * 728 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga 729 */ 730 EXTERN_C 731 HRESULT WINAPI 732 SHStartNetConnectionDialogA( 733 _In_ HWND hwnd, 734 _In_ LPCSTR pszRemoteName, 735 _In_ DWORD dwType) 736 { 737 LPCWSTR pszRemoteNameW = NULL; 738 CStringW strRemoteNameW; 739 740 TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType); 741 742 if (pszRemoteName) 743 { 744 strRemoteNameW = pszRemoteName; 745 pszRemoteNameW = strRemoteNameW; 746 } 747 748 return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType); 749 } 750 751 /************************************************************************* 752 * Helper functions for PathIsEqualOrSubFolder 753 */ 754 755 static INT 756 DynamicPathCommonPrefixW( 757 _In_ LPCWSTR lpszPath1, 758 _In_ LPCWSTR lpszPath2, 759 _Out_ CStringW& strPath) 760 { 761 SIZE_T cchPath1 = wcslen(lpszPath1); 762 SIZE_T cchPath2 = wcslen(lpszPath2); 763 LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16); 764 INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath); 765 strPath.ReleaseBuffer(); 766 return ret; 767 } 768 769 EXTERN_C HRESULT WINAPI 770 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax); 771 772 static HRESULT 773 DynamicSHGetPathFromIDListW( 774 _In_ LPCITEMIDLIST pidl, 775 _Out_ CStringW& strPath) 776 { 777 HRESULT hr; 778 779 for (UINT cchPath = MAX_PATH;; cchPath *= 2) 780 { 781 LPWSTR lpszPath = strPath.GetBuffer(cchPath); 782 if (!lpszPath) 783 return E_OUTOFMEMORY; 784 785 hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath); 786 strPath.ReleaseBuffer(); 787 788 if (hr != E_NOT_SUFFICIENT_BUFFER) 789 break; 790 791 if (cchPath >= MAXUINT / 2) 792 { 793 hr = E_FAIL; 794 break; 795 } 796 } 797 798 if (FAILED(hr)) 799 strPath.Empty(); 800 801 return hr; 802 } 803 804 static HRESULT 805 DynamicSHGetSpecialFolderPathW( 806 _In_ HWND hwndOwner, 807 _Out_ CStringW& strPath, 808 _In_ INT nCSIDL, 809 _In_ BOOL bCreate) 810 { 811 LPITEMIDLIST pidl; 812 HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl); 813 if (SUCCEEDED(hr)) 814 { 815 hr = DynamicSHGetPathFromIDListW(pidl, strPath); 816 CoTaskMemFree(pidl); 817 } 818 819 if (FAILED(hr)) 820 strPath.Empty(); 821 else if (bCreate) 822 CreateDirectoryW(strPath, NULL); 823 824 return hr; 825 } 826 827 static VOID 828 DynamicPathRemoveBackslashW( 829 _Out_ CStringW& strPath) 830 { 831 INT nLength = strPath.GetLength(); 832 if (nLength > 0 && strPath[nLength - 1] == L'\\') 833 strPath = strPath.Left(nLength - 1); 834 } 835 836 /************************************************************************* 837 * PathIsEqualOrSubFolder (SHELL32.755) 838 */ 839 EXTERN_C 840 BOOL WINAPI 841 PathIsEqualOrSubFolder( 842 _In_ LPCWSTR pszPath1OrCSIDL, 843 _In_ LPCWSTR pszPath2) 844 { 845 CStringW strCommon, strPath1; 846 847 TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2)); 848 849 if (IS_INTRESOURCE(pszPath1OrCSIDL)) 850 { 851 DynamicSHGetSpecialFolderPathW( 852 NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE); 853 } 854 else 855 { 856 strPath1 = pszPath1OrCSIDL; 857 } 858 859 DynamicPathRemoveBackslashW(strPath1); 860 861 if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon)) 862 return FALSE; 863 864 return strPath1.CompareNoCase(strCommon) == 0; 865 } 866