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 10 WINE_DEFAULT_DEBUG_CHANNEL(shell); 11 12 HWND 13 CStubWindow32::FindStubWindow(UINT Type, LPCWSTR Path) 14 { 15 for (HWND hWnd, hWndAfter = NULL;;) 16 { 17 hWnd = hWndAfter = FindWindowExW(NULL, hWndAfter, CSTUBWINDOW32_CLASSNAME, Path); 18 if (!hWnd || !Path) 19 return NULL; 20 if (GetPropW(hWnd, GetTypePropName()) == ULongToHandle(Type)) 21 return hWnd; 22 } 23 } 24 25 HRESULT 26 CStubWindow32::CreateStub(UINT Type, LPCWSTR Path, const POINT *pPt) 27 { 28 if (HWND hWnd = FindStubWindow(Type, Path)) 29 { 30 ::SwitchToThisWindow(::GetLastActivePopup(hWnd), TRUE); 31 return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); 32 } 33 RECT rcPosition = { pPt ? pPt->x : CW_USEDEFAULT, pPt ? pPt->y : CW_USEDEFAULT, 0, 0 }; 34 DWORD Style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION; 35 DWORD ExStyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; 36 if (!Create(NULL, rcPosition, Path, Style, ExStyle)) 37 { 38 ERR("StubWindow32 creation failed\n"); 39 return E_FAIL; 40 } 41 ::SetPropW(*this, GetTypePropName(), ULongToHandle(Type)); 42 return S_OK; 43 } 44 45 HRESULT 46 SHILClone( 47 _In_opt_ LPCITEMIDLIST pidl, 48 _Outptr_ LPITEMIDLIST *ppidl) 49 { 50 if (!pidl) 51 { 52 *ppidl = NULL; 53 return S_OK; 54 } 55 *ppidl = ILClone(pidl); 56 return (*ppidl ? S_OK : E_OUTOFMEMORY); 57 } 58 59 BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath) 60 { 61 if (pszPath[0] != L'.') 62 return FALSE; 63 return !pszPath[1] || (pszPath[1] == L'.' && !pszPath[2]); 64 } 65 66 #define PATH_VALID_ELEMENT ( \ 67 PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \ 68 PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID \ 69 ) 70 71 BOOL PathIsValidElement(_In_ LPCWSTR pszPath) 72 { 73 if (!*pszPath || PathIsDotOrDotDotW(pszPath)) 74 return FALSE; 75 76 for (LPCWSTR pch = pszPath; *pch; ++pch) 77 { 78 if (!PathIsValidCharW(*pch, PATH_VALID_ELEMENT)) 79 return FALSE; 80 } 81 82 return TRUE; 83 } 84 85 BOOL PathIsDosDevice(_In_ LPCWSTR pszName) 86 { 87 WCHAR szPath[MAX_PATH]; 88 StringCchCopyW(szPath, _countof(szPath), pszName); 89 PathRemoveExtensionW(szPath); 90 91 if (lstrcmpiW(szPath, L"NUL") == 0 || lstrcmpiW(szPath, L"PRN") == 0 || 92 lstrcmpiW(szPath, L"CON") == 0 || lstrcmpiW(szPath, L"AUX") == 0) 93 { 94 return TRUE; 95 } 96 97 if (_wcsnicmp(szPath, L"LPT", 3) == 0 || _wcsnicmp(szPath, L"COM", 3) == 0) 98 { 99 if ((L'0' <= szPath[3] && szPath[3] <= L'9') && szPath[4] == UNICODE_NULL) 100 return TRUE; 101 } 102 103 return FALSE; 104 } 105 106 HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl) 107 { 108 LPITEMIDLIST pidlOld = *ppidl; 109 if (!pidlOld) 110 { 111 *ppidl = pidl; 112 return S_OK; 113 } 114 115 HRESULT hr = SHILCombine(*ppidl, pidl, ppidl); 116 ILFree(pidlOld); 117 ILFree(pidl); 118 return hr; 119 } 120 121 static BOOL 122 OpenEffectiveToken( 123 _In_ DWORD DesiredAccess, 124 _Out_ HANDLE *phToken) 125 { 126 BOOL ret; 127 128 if (phToken == NULL) 129 { 130 SetLastError(ERROR_INVALID_PARAMETER); 131 return FALSE; 132 } 133 134 *phToken = NULL; 135 136 ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken); 137 if (!ret && GetLastError() == ERROR_NO_TOKEN) 138 ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken); 139 140 return ret; 141 } 142 143 HRESULT 144 Shell_TranslateIDListAlias( 145 _In_ LPCITEMIDLIST pidl, 146 _In_ HANDLE hToken, 147 _Out_ LPITEMIDLIST *ppidlAlias, 148 _In_ DWORD dwFlags) 149 { 150 return E_FAIL; //FIXME 151 } 152 153 BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName) 154 { 155 CComPtr<IUnknown> punk; 156 if (!pBindCtx || FAILED(pBindCtx->GetObjectParam(const_cast<LPWSTR>(pszName), &punk))) 157 return FALSE; 158 return TRUE; 159 } 160 161 DWORD BindCtx_GetMode(_In_ IBindCtx *pbc, _In_ DWORD dwDefault) 162 { 163 if (!pbc) 164 return dwDefault; 165 166 BIND_OPTS BindOpts = { sizeof(BindOpts) }; 167 HRESULT hr = pbc->GetBindOptions(&BindOpts); 168 if (FAILED(hr)) 169 return dwDefault; 170 171 return BindOpts.grfMode; 172 } 173 174 BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid) 175 { 176 if (!pbc) 177 return FALSE; 178 179 BIND_OPTS BindOps = { sizeof(BindOps) }; 180 if (SUCCEEDED(pbc->GetBindOptions(&BindOps)) && BindOps.grfFlags == OLECONTF_LINKS) 181 return TRUE; 182 183 return pclsid && SHSkipJunction(pbc, pclsid); 184 } 185 186 HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *pFindData) 187 { 188 CComPtr<IUnknown> punk; 189 CComPtr<IFileSystemBindData> pBindData; 190 191 if (!pBindCtx || FAILED(pBindCtx->GetObjectParam((LPWSTR)STR_FILE_SYS_BIND_DATA, &punk))) 192 return S_FALSE; 193 194 if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData)))) 195 return S_FALSE; 196 197 if (pFindData) 198 pBindData->GetFindData(pFindData); 199 200 return S_OK; 201 } 202 203 BOOL Shell_FailForceReturn(_In_ HRESULT hr) 204 { 205 DWORD code = HRESULT_CODE(hr); 206 207 switch (code) 208 { 209 case ERROR_BAD_NETPATH: 210 case ERROR_BAD_NET_NAME: 211 case ERROR_CANCELLED: 212 return TRUE; 213 214 default: 215 return (ERROR_FILE_NOT_FOUND <= code && code <= ERROR_PATH_NOT_FOUND); 216 } 217 } 218 219 HRESULT 220 SHBindToObjectEx( 221 _In_opt_ IShellFolder *pShellFolder, 222 _In_ LPCITEMIDLIST pidl, 223 _In_opt_ IBindCtx *pBindCtx, 224 _In_ REFIID riid, 225 _Out_ void **ppvObj) 226 { 227 CComPtr<IShellFolder> psfDesktop; 228 229 *ppvObj = NULL; 230 231 if (!pShellFolder) 232 { 233 SHGetDesktopFolder(&psfDesktop); 234 if (!psfDesktop) 235 return E_FAIL; 236 237 pShellFolder = psfDesktop; 238 } 239 240 HRESULT hr; 241 if (_ILIsDesktop(pidl)) 242 hr = pShellFolder->QueryInterface(riid, ppvObj); 243 else 244 hr = pShellFolder->BindToObject(pidl, pBindCtx, riid, ppvObj); 245 246 if (SUCCEEDED(hr) && !*ppvObj) 247 hr = E_FAIL; 248 249 return hr; 250 } 251 252 EXTERN_C 253 HRESULT SHBindToObject( 254 _In_opt_ IShellFolder *psf, 255 _In_ LPCITEMIDLIST pidl, 256 _In_ REFIID riid, 257 _Out_ void **ppvObj) 258 { 259 return SHBindToObjectEx(psf, pidl, NULL, riid, ppvObj); 260 } 261 262 EXTERN_C HRESULT 263 SHELL_GetUIObjectOfAbsoluteItem( 264 _In_opt_ HWND hWnd, 265 _In_ PCIDLIST_ABSOLUTE pidl, 266 _In_ REFIID riid, _Out_ void **ppvObj) 267 { 268 if (!ppvObj) 269 return E_INVALIDARG; 270 *ppvObj = NULL; 271 IShellFolder *psf; 272 PCUITEMID_CHILD pidlChild; 273 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild); 274 if (SUCCEEDED(hr)) 275 { 276 hr = psf->GetUIObjectOf(hWnd, 1, &pidlChild, riid, NULL, ppvObj); 277 psf->Release(); 278 if (SUCCEEDED(hr)) 279 { 280 if (*ppvObj) 281 return hr; 282 hr = E_FAIL; 283 } 284 } 285 return hr; 286 } 287 288 HRESULT 289 Shell_DisplayNameOf( 290 _In_ IShellFolder *psf, 291 _In_ LPCITEMIDLIST pidl, 292 _In_ DWORD dwFlags, 293 _Out_ LPWSTR pszBuf, 294 _In_ UINT cchBuf) 295 { 296 *pszBuf = UNICODE_NULL; 297 STRRET sr; 298 HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr); 299 if (FAILED(hr)) 300 return hr; 301 return StrRetToBufW(&sr, pidl, pszBuf, cchBuf); 302 } 303 304 DWORD 305 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes) 306 { 307 LPCITEMIDLIST pidlLast = pidl; 308 IShellFolder *release = NULL; 309 310 if (!psf) 311 { 312 SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 313 if (!psf) 314 return 0; 315 release = psf; 316 } 317 318 DWORD oldAttrs = dwAttributes; 319 if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes))) 320 dwAttributes = 0; 321 else 322 dwAttributes &= oldAttrs; 323 324 if ((dwAttributes & SFGAO_FOLDER) && 325 (dwAttributes & SFGAO_STREAM) && 326 !(dwAttributes & SFGAO_STORAGEANCESTOR) && 327 (oldAttrs & SFGAO_STORAGEANCESTOR) && 328 (SHGetObjectCompatFlags(psf, NULL) & 0x200)) 329 { 330 dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR); 331 dwAttributes |= SFGAO_STORAGEANCESTOR; 332 } 333 334 if (release) 335 release->Release(); 336 return dwAttributes; 337 } 338 339 HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl) 340 { 341 IShellLink *pSL; 342 HRESULT hr = SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellLink, &pSL)); 343 if (SUCCEEDED(hr)) 344 { 345 hr = pSL->GetIDList(ppidl); // Note: Returns S_FALSE if no target pidl 346 pSL->Release(); 347 } 348 return hr; 349 } 350 351 HRESULT SHCoInitializeAnyApartment(VOID) 352 { 353 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 354 if (FAILED(hr)) 355 hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); 356 return hr; 357 } 358 359 HRESULT 360 SHGetNameAndFlagsW( 361 _In_ LPCITEMIDLIST pidl, 362 _In_ DWORD dwFlags, 363 _Out_opt_ LPWSTR pszText, 364 _In_ UINT cchBuf, 365 _Inout_opt_ DWORD *pdwAttributes) 366 { 367 if (pszText) 368 *pszText = UNICODE_NULL; 369 370 HRESULT hrCoInit = SHCoInitializeAnyApartment(); 371 372 CComPtr<IShellFolder> psfFolder; 373 LPCITEMIDLIST ppidlLast; 374 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast); 375 if (SUCCEEDED(hr)) 376 { 377 if (pszText) 378 hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf); 379 380 if (SUCCEEDED(hr)) 381 { 382 if (pdwAttributes) 383 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes); 384 } 385 } 386 387 if (SUCCEEDED(hrCoInit)) 388 CoUninitialize(); 389 390 return hr; 391 } 392 393 EXTERN_C HWND 394 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx) 395 { 396 HWND hWnd = NULL; 397 398 CComPtr<IUnknown> punk; 399 if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk))) 400 IUnknown_GetWindow(punk, &hWnd); 401 402 return hWnd; 403 } 404 405 class CDummyOleWindow : public IOleWindow 406 { 407 protected: 408 LONG m_cRefs; 409 HWND m_hWnd; 410 411 public: 412 CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { } 413 virtual ~CDummyOleWindow() { } 414 415 // IUnknown methods 416 STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override 417 { 418 static const QITAB c_tab[] = 419 { 420 QITABENT(CDummyOleWindow, IOleWindow), 421 { NULL } 422 }; 423 return ::QISearch(this, c_tab, riid, ppvObj); 424 } 425 STDMETHODIMP_(ULONG) AddRef() override 426 { 427 return ++m_cRefs; 428 } 429 STDMETHODIMP_(ULONG) Release() override 430 { 431 if (--m_cRefs == 0) 432 { 433 delete this; 434 return 0; 435 } 436 return m_cRefs; 437 } 438 439 // IOleWindow methods 440 STDMETHODIMP GetWindow(HWND *phWnd) override 441 { 442 *phWnd = m_hWnd; 443 if (!m_hWnd) 444 return E_NOTIMPL; 445 return S_OK; 446 } 447 STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override 448 { 449 return E_NOTIMPL; 450 } 451 }; 452 453 EXTERN_C HRESULT 454 BindCtx_RegisterObjectParam( 455 _In_ IBindCtx *pBindCtx, 456 _In_ LPOLESTR pszKey, 457 _In_opt_ IUnknown *punk, 458 _Out_ LPBC *ppbc) 459 { 460 HRESULT hr = S_OK; 461 CDummyOleWindow *pUnknown = NULL; 462 463 *ppbc = pBindCtx; 464 465 if (pBindCtx) 466 { 467 pBindCtx->AddRef(); 468 } 469 else 470 { 471 hr = CreateBindCtx(0, ppbc); 472 if (FAILED(hr)) 473 return hr; 474 } 475 476 if (!punk) 477 punk = pUnknown = new CDummyOleWindow(); 478 479 hr = (*ppbc)->RegisterObjectParam(pszKey, punk); 480 481 if (pUnknown) 482 pUnknown->Release(); 483 484 if (FAILED(hr)) 485 { 486 (*ppbc)->Release(); 487 *ppbc = NULL; 488 } 489 490 return hr; 491 } 492 493 /************************************************************************* 494 * SHSetFolderPathA (SHELL32.231) 495 * 496 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha 497 */ 498 EXTERN_C 499 HRESULT WINAPI 500 SHSetFolderPathA( 501 _In_ INT csidl, 502 _In_ HANDLE hToken, 503 _In_ DWORD dwFlags, 504 _In_ LPCSTR pszPath) 505 { 506 TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath)); 507 CStringW strPathW(pszPath); 508 return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW); 509 } 510 511 /************************************************************************* 512 * PathIsSlowA (SHELL32.240) 513 * 514 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa 515 */ 516 EXTERN_C 517 BOOL WINAPI 518 PathIsSlowA( 519 _In_ LPCSTR pszFile, 520 _In_ DWORD dwAttr) 521 { 522 TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr); 523 CStringW strFileW(pszFile); 524 return PathIsSlowW(strFileW, dwAttr); 525 } 526 527 /************************************************************************* 528 * ExtractIconResInfoA (SHELL32.221) 529 */ 530 EXTERN_C 531 WORD WINAPI 532 ExtractIconResInfoA( 533 _In_ HANDLE hHandle, 534 _In_ LPCSTR lpFileName, 535 _In_ WORD wIndex, 536 _Out_ LPWORD lpSize, 537 _Out_ LPHANDLE lpIcon) 538 { 539 TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon); 540 541 if (!lpFileName) 542 return 0; 543 544 CStringW strFileNameW(lpFileName); 545 return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon); 546 } 547 548 /************************************************************************* 549 * ShortSizeFormatW (SHELL32.204) 550 */ 551 EXTERN_C 552 LPWSTR WINAPI 553 ShortSizeFormatW( 554 _In_ DWORD dwNumber, 555 _Out_writes_(0x8FFF) LPWSTR pszBuffer) 556 { 557 TRACE("(%lu, %p)\n", dwNumber, pszBuffer); 558 return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF); 559 } 560 561 /************************************************************************* 562 * SHOpenEffectiveToken (SHELL32.235) 563 */ 564 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken) 565 { 566 TRACE("%p\n", phToken); 567 return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken); 568 } 569 570 /************************************************************************* 571 * SHGetUserSessionId (SHELL32.248) 572 */ 573 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken) 574 { 575 DWORD dwSessionId, dwLength; 576 BOOL bOpenToken = FALSE; 577 578 TRACE("%p\n", hToken); 579 580 if (!hToken) 581 bOpenToken = SHOpenEffectiveToken(&hToken); 582 583 if (!hToken || 584 !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength)) 585 { 586 dwSessionId = 0; 587 } 588 589 if (bOpenToken) 590 CloseHandle(hToken); 591 592 return dwSessionId; 593 } 594 595 /************************************************************************* 596 * SHInvokePrivilegedFunctionW (SHELL32.246) 597 */ 598 EXTERN_C 599 HRESULT WINAPI 600 SHInvokePrivilegedFunctionW( 601 _In_ LPCWSTR pszName, 602 _In_ PRIVILEGED_FUNCTION fn, 603 _In_opt_ LPARAM lParam) 604 { 605 TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam); 606 607 if (!pszName || !fn) 608 return E_INVALIDARG; 609 610 HANDLE hToken = NULL; 611 TOKEN_PRIVILEGES NewPriv, PrevPriv; 612 BOOL bAdjusted = FALSE; 613 614 if (SHOpenEffectiveToken(&hToken) && 615 ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid)) 616 { 617 NewPriv.PrivilegeCount = 1; 618 NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 619 620 DWORD dwReturnSize; 621 bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv, 622 sizeof(PrevPriv), &PrevPriv, &dwReturnSize); 623 } 624 625 HRESULT hr = fn(lParam); 626 627 if (bAdjusted) 628 ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL); 629 630 if (hToken) 631 ::CloseHandle(hToken); 632 633 return hr; 634 } 635 636 /************************************************************************* 637 * SHTestTokenPrivilegeW (SHELL32.236) 638 * 639 * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php 640 */ 641 EXTERN_C 642 BOOL WINAPI 643 SHTestTokenPrivilegeW( 644 _In_opt_ HANDLE hToken, 645 _In_ LPCWSTR lpName) 646 { 647 LUID Luid; 648 DWORD dwLength; 649 PTOKEN_PRIVILEGES pTokenPriv; 650 HANDLE hNewToken = NULL; 651 BOOL ret = FALSE; 652 653 TRACE("(%p, %s)\n", hToken, debugstr_w(lpName)); 654 655 if (!lpName) 656 return FALSE; 657 658 if (!hToken) 659 { 660 if (!SHOpenEffectiveToken(&hNewToken)) 661 goto Quit; 662 663 if (!hNewToken) 664 return FALSE; 665 666 hToken = hNewToken; 667 } 668 669 if (!LookupPrivilegeValueW(NULL, lpName, &Luid)) 670 return FALSE; 671 672 dwLength = 0; 673 if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength)) 674 goto Quit; 675 676 pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength); 677 if (!pTokenPriv) 678 goto Quit; 679 680 if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength)) 681 { 682 UINT iPriv, cPrivs; 683 cPrivs = pTokenPriv->PrivilegeCount; 684 for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv) 685 { 686 ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid); 687 } 688 } 689 690 LocalFree(pTokenPriv); 691 692 Quit: 693 if (hToken == hNewToken) 694 CloseHandle(hNewToken); 695 696 return ret; 697 } 698 699 BOOL IsShutdownAllowed(VOID) 700 { 701 return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME); 702 } 703 704 /************************************************************************* 705 * IsSuspendAllowed (SHELL32.53) 706 */ 707 BOOL WINAPI IsSuspendAllowed(VOID) 708 { 709 TRACE("()\n"); 710 return IsShutdownAllowed() && IsPwrSuspendAllowed(); 711 } 712 713 /************************************************************************* 714 * SHGetShellStyleHInstance (SHELL32.749) 715 */ 716 EXTERN_C HINSTANCE 717 WINAPI 718 SHGetShellStyleHInstance(VOID) 719 { 720 HINSTANCE hInst = NULL; 721 WCHAR szPath[MAX_PATH], szColorName[100]; 722 HRESULT hr; 723 CStringW strShellStyle; 724 725 TRACE("SHGetShellStyleHInstance called\n"); 726 727 /* First, attempt to load the shellstyle dll from the current active theme */ 728 hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0); 729 if (FAILED(hr)) 730 goto DoDefault; 731 732 /* Strip the theme filename */ 733 PathRemoveFileSpecW(szPath); 734 735 strShellStyle = szPath; 736 strShellStyle += L"\\Shell\\"; 737 strShellStyle += szColorName; 738 strShellStyle += L"\\ShellStyle.dll"; 739 740 hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE); 741 if (hInst) 742 return hInst; 743 744 /* Otherwise, use the version stored in the System32 directory */ 745 DoDefault: 746 if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll", 747 szPath, _countof(szPath))) 748 { 749 ERR("Expand failed\n"); 750 return NULL; 751 } 752 return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE); 753 } 754 755 /************************************************************************* 756 * SHCreatePropertyBag (SHELL32.715) 757 */ 758 EXTERN_C HRESULT 759 WINAPI 760 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj) 761 { 762 return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj); 763 } 764 765 /************************************************************************* 766 * SheRemoveQuotesA (SHELL32.@) 767 */ 768 EXTERN_C LPSTR 769 WINAPI 770 SheRemoveQuotesA(LPSTR psz) 771 { 772 PCHAR pch; 773 774 if (*psz == '"') 775 { 776 for (pch = psz + 1; *pch && *pch != '"'; ++pch) 777 { 778 *(pch - 1) = *pch; 779 } 780 781 if (*pch == '"') 782 *(pch - 1) = ANSI_NULL; 783 } 784 785 return psz; 786 } 787 788 /************************************************************************* 789 * SheRemoveQuotesW (SHELL32.@) 790 * 791 * ExtractAssociatedIconExW uses this function. 792 */ 793 EXTERN_C LPWSTR 794 WINAPI 795 SheRemoveQuotesW(LPWSTR psz) 796 { 797 PWCHAR pch; 798 799 if (*psz == L'"') 800 { 801 for (pch = psz + 1; *pch && *pch != L'"'; ++pch) 802 { 803 *(pch - 1) = *pch; 804 } 805 806 if (*pch == L'"') 807 *(pch - 1) = UNICODE_NULL; 808 } 809 810 return psz; 811 } 812 813 /************************************************************************* 814 * SHFindComputer [SHELL32.91] 815 * 816 * Invokes the shell search in My Computer. Used in SHFindFiles. 817 * Two parameters are ignored. 818 */ 819 EXTERN_C BOOL 820 WINAPI 821 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch) 822 { 823 UNREFERENCED_PARAMETER(pidlRoot); 824 UNREFERENCED_PARAMETER(pidlSavedSearch); 825 826 TRACE("%p %p\n", pidlRoot, pidlSavedSearch); 827 828 IContextMenu *pCM; 829 HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, 830 IID_IContextMenu, (void **)&pCM); 831 if (FAILED(hr)) 832 { 833 ERR("0x%08X\n", hr); 834 return hr; 835 } 836 837 CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) }; 838 InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}"; 839 InvokeInfo.nShow = SW_SHOWNORMAL; 840 hr = pCM->InvokeCommand(&InvokeInfo); 841 pCM->Release(); 842 843 return SUCCEEDED(hr); 844 } 845 846 static HRESULT 847 Int64ToStr( 848 _In_ LONGLONG llValue, 849 _Out_writes_(cchValue) LPWSTR pszValue, 850 _In_ UINT cchValue) 851 { 852 WCHAR szBuff[40]; 853 UINT ich = 0, ichValue; 854 #if (WINVER >= _WIN32_WINNT_VISTA) 855 BOOL bMinus = (llValue < 0); 856 857 if (bMinus) 858 llValue = -llValue; 859 #endif 860 861 if (cchValue <= 0) 862 return E_FAIL; 863 864 do 865 { 866 szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10)); 867 llValue /= 10; 868 } while (llValue != 0 && ich < _countof(szBuff) - 1); 869 870 #if (WINVER >= _WIN32_WINNT_VISTA) 871 if (bMinus && ich < _countof(szBuff)) 872 szBuff[ich++] = '-'; 873 #endif 874 875 for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue) 876 { 877 --ich; 878 pszValue[ichValue] = szBuff[ich]; 879 } 880 881 if (ichValue >= cchValue) 882 { 883 pszValue[cchValue - 1] = UNICODE_NULL; 884 return E_FAIL; 885 } 886 887 pszValue[ichValue] = UNICODE_NULL; 888 return S_OK; 889 } 890 891 static VOID 892 Int64GetNumFormat( 893 _Out_ NUMBERFMTW *pDest, 894 _In_opt_ const NUMBERFMTW *pSrc, 895 _In_ DWORD dwNumberFlags, 896 _Out_writes_(cchDecimal) LPWSTR pszDecimal, 897 _In_ INT cchDecimal, 898 _Out_writes_(cchThousand) LPWSTR pszThousand, 899 _In_ INT cchThousand) 900 { 901 WCHAR szBuff[20]; 902 903 if (pSrc) 904 *pDest = *pSrc; 905 else 906 dwNumberFlags = 0; 907 908 if (!(dwNumberFlags & FMT_USE_NUMDIGITS)) 909 { 910 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff)); 911 pDest->NumDigits = StrToIntW(szBuff); 912 } 913 914 if (!(dwNumberFlags & FMT_USE_LEADZERO)) 915 { 916 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff)); 917 pDest->LeadingZero = StrToIntW(szBuff); 918 } 919 920 if (!(dwNumberFlags & FMT_USE_GROUPING)) 921 { 922 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff)); 923 pDest->Grouping = StrToIntW(szBuff); 924 } 925 926 if (!(dwNumberFlags & FMT_USE_DECIMAL)) 927 { 928 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal); 929 pDest->lpDecimalSep = pszDecimal; 930 } 931 932 if (!(dwNumberFlags & FMT_USE_THOUSAND)) 933 { 934 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand); 935 pDest->lpThousandSep = pszThousand; 936 } 937 938 if (!(dwNumberFlags & FMT_USE_NEGNUMBER)) 939 { 940 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff)); 941 pDest->NegativeOrder = StrToIntW(szBuff); 942 } 943 } 944 945 /************************************************************************* 946 * Int64ToString [SHELL32.209] 947 * 948 * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php 949 */ 950 EXTERN_C 951 INT WINAPI 952 Int64ToString( 953 _In_ LONGLONG llValue, 954 _Out_writes_(cchOut) LPWSTR pszOut, 955 _In_ UINT cchOut, 956 _In_ BOOL bUseFormat, 957 _In_opt_ const NUMBERFMTW *pNumberFormat, 958 _In_ DWORD dwNumberFlags) 959 { 960 INT ret; 961 NUMBERFMTW NumFormat; 962 WCHAR szValue[80], szDecimalSep[6], szThousandSep[6]; 963 964 Int64ToStr(llValue, szValue, _countof(szValue)); 965 966 if (bUseFormat) 967 { 968 Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags, 969 szDecimalSep, _countof(szDecimalSep), 970 szThousandSep, _countof(szThousandSep)); 971 ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut); 972 if (ret) 973 --ret; 974 return ret; 975 } 976 977 if (FAILED(StringCchCopyW(pszOut, cchOut, szValue))) 978 return 0; 979 980 return lstrlenW(pszOut); 981 } 982 983 /************************************************************************* 984 * LargeIntegerToString [SHELL32.210] 985 * 986 * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php 987 */ 988 EXTERN_C 989 INT WINAPI 990 LargeIntegerToString( 991 _In_ const LARGE_INTEGER *pLargeInt, 992 _Out_writes_(cchOut) LPWSTR pszOut, 993 _In_ UINT cchOut, 994 _In_ BOOL bUseFormat, 995 _In_opt_ const NUMBERFMTW *pNumberFormat, 996 _In_ DWORD dwNumberFlags) 997 { 998 return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat, 999 pNumberFormat, dwNumberFlags); 1000 } 1001 1002 /************************************************************************* 1003 * CopyStreamUI [SHELL32.726] 1004 * 1005 * Copy a stream to another stream with optional progress display. 1006 */ 1007 EXTERN_C 1008 HRESULT WINAPI 1009 CopyStreamUI( 1010 _In_ IStream *pSrc, 1011 _Out_ IStream *pDst, 1012 _Inout_opt_ IProgressDialog *pProgress, 1013 _In_opt_ DWORDLONG dwlSize) 1014 { 1015 HRESULT hr = E_FAIL; 1016 DWORD cbBuff, cbRead, dwSizeToWrite; 1017 DWORDLONG cbDone; 1018 LPVOID pBuff; 1019 CComHeapPtr<BYTE> pHeapPtr; 1020 STATSTG Stat; 1021 BYTE abBuff[1024]; 1022 1023 TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize); 1024 1025 if (dwlSize == 0) // Invalid size? 1026 { 1027 // Get the stream size 1028 ZeroMemory(&Stat, sizeof(Stat)); 1029 if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME))) 1030 pProgress = NULL; // No size info. Disable progress 1031 else 1032 dwlSize = Stat.cbSize.QuadPart; 1033 } 1034 1035 if (!pProgress) // Progress is disabled? 1036 { 1037 ULARGE_INTEGER uliSize; 1038 1039 if (dwlSize > 0) 1040 uliSize.QuadPart = dwlSize; 1041 else 1042 uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE; 1043 1044 return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch 1045 } 1046 1047 // Allocate the buffer if necessary 1048 if (dwlSize > 0 && dwlSize <= sizeof(abBuff)) 1049 { 1050 cbBuff = sizeof(abBuff); 1051 pBuff = abBuff; 1052 } 1053 else 1054 { 1055 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000 1056 cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE; 1057 if (pHeapPtr.AllocateBytes(cbBuff)) 1058 { 1059 pBuff = pHeapPtr; 1060 } 1061 else // Low memory? 1062 { 1063 cbBuff = sizeof(abBuff); 1064 pBuff = abBuff; 1065 } 1066 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE 1067 } 1068 1069 // Start reading 1070 LARGE_INTEGER zero; 1071 zero.QuadPart = 0; 1072 pSrc->Seek(zero, 0, NULL); 1073 pDst->Seek(zero, 0, NULL); 1074 cbDone = 0; 1075 pProgress->SetProgress64(cbDone, dwlSize); 1076 1077 // Repeat reading and writing until goal 1078 for (;;) 1079 { 1080 hr = pSrc->Read(pBuff, cbBuff, &cbRead); 1081 if (FAILED(hr)) 1082 break; 1083 1084 // Calculate the size to write 1085 if (dwlSize > 0) 1086 dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead); 1087 else 1088 dwSizeToWrite = cbRead; 1089 1090 if (dwSizeToWrite == 0) // No need to write? 1091 { 1092 hr = S_OK; 1093 break; 1094 } 1095 1096 hr = pDst->Write(pBuff, dwSizeToWrite, NULL); 1097 if (hr != S_OK) 1098 break; 1099 1100 cbDone += dwSizeToWrite; 1101 1102 if (pProgress->HasUserCancelled()) // Cancelled? 1103 { 1104 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 1105 break; 1106 } 1107 pProgress->SetProgress64(cbDone, dwlSize); 1108 1109 if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal? 1110 { 1111 hr = S_OK; 1112 break; 1113 } 1114 } 1115 1116 return hr; 1117 } 1118 1119 /************************************************************************* 1120 * SHOpenPropSheetA [SHELL32.707] 1121 * 1122 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta 1123 */ 1124 EXTERN_C 1125 BOOL WINAPI 1126 SHOpenPropSheetA( 1127 _In_opt_ LPCSTR pszCaption, 1128 _In_opt_ HKEY *ahKeys, 1129 _In_ UINT cKeys, 1130 _In_ const CLSID *pclsidDefault, 1131 _In_ IDataObject *pDataObject, 1132 _In_opt_ IShellBrowser *pShellBrowser, 1133 _In_opt_ LPCSTR pszStartPage) 1134 { 1135 CStringW strStartPageW, strCaptionW; 1136 LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL; 1137 1138 TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault, 1139 pDataObject, pShellBrowser, debugstr_a(pszStartPage)); 1140 1141 if (pszCaption) 1142 { 1143 strStartPageW = pszCaption; 1144 pszCaptionW = strCaptionW; 1145 } 1146 1147 if (pszStartPage) 1148 { 1149 strStartPageW = pszStartPage; 1150 pszStartPageW = strStartPageW; 1151 } 1152 1153 return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault, 1154 pDataObject, pShellBrowser, pszStartPageW); 1155 } 1156 1157 /************************************************************************* 1158 * Activate_RunDLL [SHELL32.105] 1159 * 1160 * Unlocks the foreground window and allows the shell window to become the 1161 * foreground window. Every parameter is unused. 1162 */ 1163 EXTERN_C 1164 BOOL WINAPI 1165 Activate_RunDLL( 1166 _In_ HWND hwnd, 1167 _In_ HINSTANCE hinst, 1168 _In_ LPCWSTR cmdline, 1169 _In_ INT cmdshow) 1170 { 1171 DWORD dwProcessID; 1172 1173 UNREFERENCED_PARAMETER(hwnd); 1174 UNREFERENCED_PARAMETER(hinst); 1175 UNREFERENCED_PARAMETER(cmdline); 1176 UNREFERENCED_PARAMETER(cmdshow); 1177 1178 TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline); 1179 1180 GetWindowThreadProcessId(GetShellWindow(), &dwProcessID); 1181 return AllowSetForegroundWindow(dwProcessID); 1182 } 1183 1184 /************************************************************************* 1185 * SHStartNetConnectionDialogA (SHELL32.12) 1186 * 1187 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga 1188 */ 1189 EXTERN_C 1190 HRESULT WINAPI 1191 SHStartNetConnectionDialogA( 1192 _In_ HWND hwnd, 1193 _In_ LPCSTR pszRemoteName, 1194 _In_ DWORD dwType) 1195 { 1196 LPCWSTR pszRemoteNameW = NULL; 1197 CStringW strRemoteNameW; 1198 1199 TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType); 1200 1201 if (pszRemoteName) 1202 { 1203 strRemoteNameW = pszRemoteName; 1204 pszRemoteNameW = strRemoteNameW; 1205 } 1206 1207 return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType); 1208 } 1209 1210 /************************************************************************* 1211 * Helper functions for PathIsEqualOrSubFolder 1212 */ 1213 1214 static INT 1215 DynamicPathCommonPrefixW( 1216 _In_ LPCWSTR lpszPath1, 1217 _In_ LPCWSTR lpszPath2, 1218 _Out_ CStringW& strPath) 1219 { 1220 SIZE_T cchPath1 = wcslen(lpszPath1); 1221 SIZE_T cchPath2 = wcslen(lpszPath2); 1222 LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16); 1223 INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath); 1224 strPath.ReleaseBuffer(); 1225 return ret; 1226 } 1227 1228 EXTERN_C HRESULT WINAPI 1229 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax); 1230 1231 static HRESULT 1232 DynamicSHGetPathFromIDListW( 1233 _In_ LPCITEMIDLIST pidl, 1234 _Out_ CStringW& strPath) 1235 { 1236 HRESULT hr; 1237 1238 for (UINT cchPath = MAX_PATH;; cchPath *= 2) 1239 { 1240 LPWSTR lpszPath = strPath.GetBuffer(cchPath); 1241 if (!lpszPath) 1242 return E_OUTOFMEMORY; 1243 1244 hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath); 1245 strPath.ReleaseBuffer(); 1246 1247 if (hr != E_NOT_SUFFICIENT_BUFFER) 1248 break; 1249 1250 if (cchPath >= MAXUINT / 2) 1251 { 1252 hr = E_FAIL; 1253 break; 1254 } 1255 } 1256 1257 if (FAILED(hr)) 1258 strPath.Empty(); 1259 1260 return hr; 1261 } 1262 1263 static HRESULT 1264 DynamicSHGetSpecialFolderPathW( 1265 _In_ HWND hwndOwner, 1266 _Out_ CStringW& strPath, 1267 _In_ INT nCSIDL, 1268 _In_ BOOL bCreate) 1269 { 1270 LPITEMIDLIST pidl; 1271 HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl); 1272 if (SUCCEEDED(hr)) 1273 { 1274 hr = DynamicSHGetPathFromIDListW(pidl, strPath); 1275 CoTaskMemFree(pidl); 1276 } 1277 1278 if (FAILED(hr)) 1279 strPath.Empty(); 1280 else if (bCreate) 1281 CreateDirectoryW(strPath, NULL); 1282 1283 return hr; 1284 } 1285 1286 static VOID 1287 DynamicPathRemoveBackslashW( 1288 _Out_ CStringW& strPath) 1289 { 1290 INT nLength = strPath.GetLength(); 1291 if (nLength > 0 && strPath[nLength - 1] == L'\\') 1292 strPath = strPath.Left(nLength - 1); 1293 } 1294 1295 /************************************************************************* 1296 * PathIsEqualOrSubFolder (SHELL32.755) 1297 */ 1298 EXTERN_C 1299 BOOL WINAPI 1300 PathIsEqualOrSubFolder( 1301 _In_ LPCWSTR pszPath1OrCSIDL, 1302 _In_ LPCWSTR pszPath2) 1303 { 1304 CStringW strCommon, strPath1; 1305 1306 TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2)); 1307 1308 if (IS_INTRESOURCE(pszPath1OrCSIDL)) 1309 { 1310 DynamicSHGetSpecialFolderPathW( 1311 NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE); 1312 } 1313 else 1314 { 1315 strPath1 = pszPath1OrCSIDL; 1316 } 1317 1318 DynamicPathRemoveBackslashW(strPath1); 1319 1320 if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon)) 1321 return FALSE; 1322 1323 return strPath1.CompareNoCase(strCommon) == 0; 1324 } 1325 1326 /************************************************************************* 1327 * SHGetRealIDL [SHELL32.98] 1328 */ 1329 EXTERN_C 1330 HRESULT WINAPI 1331 SHGetRealIDL( 1332 _In_ IShellFolder *psf, 1333 _In_ PCUITEMID_CHILD pidlSimple, 1334 _Outptr_ PITEMID_CHILD *ppidlReal) 1335 { 1336 HRESULT hr; 1337 STRRET strret; 1338 WCHAR szPath[MAX_PATH]; 1339 SFGAOF attrs; 1340 1341 *ppidlReal = NULL; 1342 1343 hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_INFOLDER | SHGDN_FORPARSING, 1344 &strret, 0); 1345 if (FAILED_UNEXPECTEDLY(hr)) 1346 return hr; 1347 1348 hr = StrRetToBufW(&strret, pidlSimple, szPath, _countof(szPath)); 1349 if (FAILED_UNEXPECTEDLY(hr)) 1350 return hr; 1351 1352 attrs = SFGAO_FILESYSTEM; 1353 hr = psf->GetAttributesOf(1, &pidlSimple, &attrs); 1354 if (SUCCEEDED(hr) && !(attrs & SFGAO_FILESYSTEM)) 1355 return SHILClone(pidlSimple, ppidlReal); 1356 1357 hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL); 1358 if (hr == E_INVALIDARG || hr == E_NOTIMPL) 1359 return SHILClone(pidlSimple, ppidlReal); 1360 1361 return hr; 1362 } 1363 1364 EXTERN_C HRESULT 1365 IUnknown_InitializeCommand( 1366 _In_ IUnknown *pUnk, 1367 _In_ PCWSTR pszCommandName, 1368 _In_opt_ IPropertyBag *pPB) 1369 { 1370 HRESULT hr; 1371 CComPtr<IInitializeCommand> pIC; 1372 if (SUCCEEDED(hr = pUnk->QueryInterface(IID_PPV_ARG(IInitializeCommand, &pIC)))) 1373 hr = pIC->Initialize(pszCommandName, pPB); 1374 return hr; 1375 } 1376 1377 EXTERN_C HRESULT 1378 InvokeIExecuteCommand( 1379 _In_ IExecuteCommand *pEC, 1380 _In_ PCWSTR pszCommandName, 1381 _In_opt_ IPropertyBag *pPB, 1382 _In_opt_ IShellItemArray *pSIA, 1383 _In_opt_ LPCMINVOKECOMMANDINFOEX pICI, 1384 _In_opt_ IUnknown *pSite) 1385 { 1386 if (!pEC) 1387 return E_INVALIDARG; 1388 1389 if (pSite) 1390 IUnknown_SetSite(pEC, pSite); 1391 IUnknown_InitializeCommand(pEC, pszCommandName, pPB); 1392 1393 CComPtr<IObjectWithSelection> pOWS; 1394 if (pSIA && SUCCEEDED(pEC->QueryInterface(IID_PPV_ARG(IObjectWithSelection, &pOWS)))) 1395 pOWS->SetSelection(pSIA); 1396 1397 DWORD dwKeyState = 0, fMask = pICI ? pICI->fMask : 0; 1398 pEC->SetNoShowUI((fMask & CMIC_MASK_FLAG_NO_UI) != 0); 1399 pEC->SetShowWindow(pICI ? pICI->nShow : SW_SHOW); 1400 if (fMask & CMIC_MASK_SHIFT_DOWN) 1401 dwKeyState |= MK_SHIFT; 1402 if (fMask & CMIC_MASK_CONTROL_DOWN) 1403 dwKeyState |= MK_CONTROL; 1404 pEC->SetKeyState(dwKeyState); 1405 if ((fMask & CMIC_MASK_UNICODE) && pICI->lpDirectoryW) 1406 pEC->SetDirectory(pICI->lpDirectoryW); 1407 if ((fMask & CMIC_MASK_UNICODE) && pICI->lpParametersW) 1408 pEC->SetParameters(pICI->lpParametersW); 1409 if (fMask & CMIC_MASK_PTINVOKE) 1410 pEC->SetPosition(pICI->ptInvoke); 1411 1412 HRESULT hr = pEC->Execute(); 1413 if (pSite) 1414 IUnknown_SetSite(pEC, NULL); 1415 return hr; 1416 } 1417 1418 EXTERN_C HRESULT 1419 InvokeIExecuteCommandWithDataObject( 1420 _In_ IExecuteCommand *pEC, 1421 _In_ PCWSTR pszCommandName, 1422 _In_opt_ IPropertyBag *pPB, 1423 _In_ IDataObject *pDO, 1424 _In_opt_ LPCMINVOKECOMMANDINFOEX pICI, 1425 _In_opt_ IUnknown *pSite) 1426 { 1427 CComPtr<IShellItemArray> pSIA; 1428 HRESULT hr = SHCreateShellItemArrayFromDataObject(pDO, IID_PPV_ARG(IShellItemArray, &pSIA)); 1429 return SUCCEEDED(hr) ? InvokeIExecuteCommand(pEC, pszCommandName, pPB, pSIA, pICI, pSite) : hr; 1430 } 1431 1432 static HRESULT 1433 GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax) 1434 { 1435 HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax); 1436 if (FAILED(hr)) 1437 { 1438 WCHAR buf[MAX_PATH]; 1439 hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf)); 1440 if (SUCCEEDED(hr)) 1441 hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL; 1442 } 1443 return hr; 1444 } 1445 1446 UINT 1447 GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba) 1448 { 1449 CHAR buf[MAX_PATH]; 1450 if (IS_INTRESOURCE(verba)) 1451 { 1452 if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf)))) 1453 return 0; 1454 verba = buf; 1455 } 1456 return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0 1457 } 1458 1459 HRESULT 1460 SHELL_MapContextMenuVerbToCmdId(LPCMINVOKECOMMANDINFO pICI, const CMVERBMAP *pMap) 1461 { 1462 LPCSTR pVerbA = pICI->lpVerb; 1463 CHAR buf[MAX_PATH]; 1464 LPCMINVOKECOMMANDINFOEX pICIX = (LPCMINVOKECOMMANDINFOEX)pICI; 1465 if (IsUnicode(*pICIX) && !IS_INTRESOURCE(pICIX->lpVerbW)) 1466 { 1467 if (SHUnicodeToAnsi(pICIX->lpVerbW, buf, _countof(buf))) 1468 pVerbA = buf; 1469 } 1470 1471 if (IS_INTRESOURCE(pVerbA)) 1472 return LOWORD(pVerbA); 1473 for (SIZE_T i = 0; pMap[i].Verb; ++i) 1474 { 1475 assert(SUCCEEDED((int)(pMap[i].CmdId))); // The id must be >= 0 and ideally in the 0..0x7fff range 1476 if (!lstrcmpiA(pMap[i].Verb, pVerbA) && pVerbA[0]) 1477 return pMap[i].CmdId; 1478 } 1479 return E_FAIL; 1480 } 1481 1482 static const CMVERBMAP* 1483 FindVerbMapEntry(UINT_PTR CmdId, const CMVERBMAP *pMap) 1484 { 1485 for (SIZE_T i = 0; pMap[i].Verb; ++i) 1486 { 1487 if (pMap[i].CmdId == CmdId) 1488 return &pMap[i]; 1489 } 1490 return NULL; 1491 } 1492 1493 HRESULT 1494 SHELL_GetCommandStringImpl(SIZE_T CmdId, UINT uFlags, LPSTR Buf, UINT cchBuf, const CMVERBMAP *pMap) 1495 { 1496 const CMVERBMAP* pEntry; 1497 switch (uFlags | GCS_UNICODE) 1498 { 1499 case GCS_VALIDATEW: 1500 case GCS_VERBW: 1501 pEntry = FindVerbMapEntry(CmdId, pMap); 1502 if ((uFlags | GCS_UNICODE) == GCS_VERBW) 1503 { 1504 if (!pEntry) 1505 return E_INVALIDARG; 1506 else if (uFlags & GCS_UNICODE) 1507 return SHAnsiToUnicode(pEntry->Verb, (LPWSTR)Buf, cchBuf) ? S_OK : E_FAIL; 1508 else 1509 return StringCchCopyA(Buf, cchBuf, pEntry->Verb); 1510 } 1511 return pEntry ? S_OK : S_FALSE; // GCS_VALIDATE 1512 } 1513 return E_NOTIMPL; 1514 } 1515 1516 HRESULT 1517 SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut) 1518 { 1519 CComPtr<IDefaultExtractIconInit> initIcon; 1520 HRESULT hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit, &initIcon)); 1521 if (FAILED_UNEXPECTEDLY(hr)) 1522 return hr; 1523 initIcon->SetNormalIcon(swShell32Name, IconIndex); 1524 return initIcon->QueryInterface(riid, ppvOut); 1525 } 1526