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 HRESULT 263 Shell_DisplayNameOf( 264 _In_ IShellFolder *psf, 265 _In_ LPCITEMIDLIST pidl, 266 _In_ DWORD dwFlags, 267 _Out_ LPWSTR pszBuf, 268 _In_ UINT cchBuf) 269 { 270 *pszBuf = UNICODE_NULL; 271 STRRET sr; 272 HRESULT hr = psf->GetDisplayNameOf(pidl, dwFlags, &sr); 273 if (FAILED(hr)) 274 return hr; 275 return StrRetToBufW(&sr, pidl, pszBuf, cchBuf); 276 } 277 278 DWORD 279 SHGetAttributes(_In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, _In_ DWORD dwAttributes) 280 { 281 LPCITEMIDLIST pidlLast = pidl; 282 IShellFolder *release = NULL; 283 284 if (!psf) 285 { 286 SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 287 if (!psf) 288 return 0; 289 release = psf; 290 } 291 292 DWORD oldAttrs = dwAttributes; 293 if (FAILED(psf->GetAttributesOf(1, &pidlLast, &dwAttributes))) 294 dwAttributes = 0; 295 else 296 dwAttributes &= oldAttrs; 297 298 if ((dwAttributes & SFGAO_FOLDER) && 299 (dwAttributes & SFGAO_STREAM) && 300 !(dwAttributes & SFGAO_STORAGEANCESTOR) && 301 (oldAttrs & SFGAO_STORAGEANCESTOR) && 302 (SHGetObjectCompatFlags(psf, NULL) & 0x200)) 303 { 304 dwAttributes &= ~(SFGAO_STREAM | SFGAO_STORAGEANCESTOR); 305 dwAttributes |= SFGAO_STORAGEANCESTOR; 306 } 307 308 if (release) 309 release->Release(); 310 return dwAttributes; 311 } 312 313 HRESULT SHELL_GetIDListTarget(_In_ LPCITEMIDLIST pidl, _Out_ PIDLIST_ABSOLUTE *ppidl) 314 { 315 IShellLink *pSL; 316 HRESULT hr = SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellLink, &pSL)); 317 if (SUCCEEDED(hr)) 318 { 319 hr = pSL->GetIDList(ppidl); // Note: Returns S_FALSE if no target pidl 320 pSL->Release(); 321 } 322 return hr; 323 } 324 325 HRESULT SHCoInitializeAnyApartment(VOID) 326 { 327 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 328 if (FAILED(hr)) 329 hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); 330 return hr; 331 } 332 333 HRESULT 334 SHGetNameAndFlagsW( 335 _In_ LPCITEMIDLIST pidl, 336 _In_ DWORD dwFlags, 337 _Out_opt_ LPWSTR pszText, 338 _In_ UINT cchBuf, 339 _Inout_opt_ DWORD *pdwAttributes) 340 { 341 if (pszText) 342 *pszText = UNICODE_NULL; 343 344 HRESULT hrCoInit = SHCoInitializeAnyApartment(); 345 346 CComPtr<IShellFolder> psfFolder; 347 LPCITEMIDLIST ppidlLast; 348 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &ppidlLast); 349 if (SUCCEEDED(hr)) 350 { 351 if (pszText) 352 hr = Shell_DisplayNameOf(psfFolder, ppidlLast, dwFlags, pszText, cchBuf); 353 354 if (SUCCEEDED(hr)) 355 { 356 if (pdwAttributes) 357 *pdwAttributes = SHGetAttributes(psfFolder, ppidlLast, *pdwAttributes); 358 } 359 } 360 361 if (SUCCEEDED(hrCoInit)) 362 CoUninitialize(); 363 364 return hr; 365 } 366 367 EXTERN_C HWND 368 BindCtx_GetUIWindow(_In_ IBindCtx *pBindCtx) 369 { 370 HWND hWnd = NULL; 371 372 CComPtr<IUnknown> punk; 373 if (pBindCtx && SUCCEEDED(pBindCtx->GetObjectParam((LPWSTR)L"UI During Binding", &punk))) 374 IUnknown_GetWindow(punk, &hWnd); 375 376 return hWnd; 377 } 378 379 class CDummyOleWindow : public IOleWindow 380 { 381 protected: 382 LONG m_cRefs; 383 HWND m_hWnd; 384 385 public: 386 CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { } 387 virtual ~CDummyOleWindow() { } 388 389 // IUnknown methods 390 STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj) override 391 { 392 static const QITAB c_tab[] = 393 { 394 QITABENT(CDummyOleWindow, IOleWindow), 395 { NULL } 396 }; 397 return ::QISearch(this, c_tab, riid, ppvObj); 398 } 399 STDMETHODIMP_(ULONG) AddRef() override 400 { 401 return ++m_cRefs; 402 } 403 STDMETHODIMP_(ULONG) Release() override 404 { 405 if (--m_cRefs == 0) 406 { 407 delete this; 408 return 0; 409 } 410 return m_cRefs; 411 } 412 413 // IOleWindow methods 414 STDMETHODIMP GetWindow(HWND *phWnd) override 415 { 416 *phWnd = m_hWnd; 417 if (!m_hWnd) 418 return E_NOTIMPL; 419 return S_OK; 420 } 421 STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override 422 { 423 return E_NOTIMPL; 424 } 425 }; 426 427 EXTERN_C HRESULT 428 BindCtx_RegisterObjectParam( 429 _In_ IBindCtx *pBindCtx, 430 _In_ LPOLESTR pszKey, 431 _In_opt_ IUnknown *punk, 432 _Out_ LPBC *ppbc) 433 { 434 HRESULT hr = S_OK; 435 CDummyOleWindow *pUnknown = NULL; 436 437 *ppbc = pBindCtx; 438 439 if (pBindCtx) 440 { 441 pBindCtx->AddRef(); 442 } 443 else 444 { 445 hr = CreateBindCtx(0, ppbc); 446 if (FAILED(hr)) 447 return hr; 448 } 449 450 if (!punk) 451 punk = pUnknown = new CDummyOleWindow(); 452 453 hr = (*ppbc)->RegisterObjectParam(pszKey, punk); 454 455 if (pUnknown) 456 pUnknown->Release(); 457 458 if (FAILED(hr)) 459 { 460 (*ppbc)->Release(); 461 *ppbc = NULL; 462 } 463 464 return hr; 465 } 466 467 /************************************************************************* 468 * SHSetFolderPathA (SHELL32.231) 469 * 470 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha 471 */ 472 EXTERN_C 473 HRESULT WINAPI 474 SHSetFolderPathA( 475 _In_ INT csidl, 476 _In_ HANDLE hToken, 477 _In_ DWORD dwFlags, 478 _In_ LPCSTR pszPath) 479 { 480 TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath)); 481 CStringW strPathW(pszPath); 482 return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW); 483 } 484 485 /************************************************************************* 486 * PathIsSlowA (SHELL32.240) 487 * 488 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa 489 */ 490 EXTERN_C 491 BOOL WINAPI 492 PathIsSlowA( 493 _In_ LPCSTR pszFile, 494 _In_ DWORD dwAttr) 495 { 496 TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr); 497 CStringW strFileW(pszFile); 498 return PathIsSlowW(strFileW, dwAttr); 499 } 500 501 /************************************************************************* 502 * ExtractIconResInfoA (SHELL32.221) 503 */ 504 EXTERN_C 505 WORD WINAPI 506 ExtractIconResInfoA( 507 _In_ HANDLE hHandle, 508 _In_ LPCSTR lpFileName, 509 _In_ WORD wIndex, 510 _Out_ LPWORD lpSize, 511 _Out_ LPHANDLE lpIcon) 512 { 513 TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon); 514 515 if (!lpFileName) 516 return 0; 517 518 CStringW strFileNameW(lpFileName); 519 return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon); 520 } 521 522 /************************************************************************* 523 * ShortSizeFormatW (SHELL32.204) 524 */ 525 EXTERN_C 526 LPWSTR WINAPI 527 ShortSizeFormatW( 528 _In_ DWORD dwNumber, 529 _Out_writes_(0x8FFF) LPWSTR pszBuffer) 530 { 531 TRACE("(%lu, %p)\n", dwNumber, pszBuffer); 532 return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF); 533 } 534 535 /************************************************************************* 536 * SHOpenEffectiveToken (SHELL32.235) 537 */ 538 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken) 539 { 540 TRACE("%p\n", phToken); 541 return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken); 542 } 543 544 /************************************************************************* 545 * SHGetUserSessionId (SHELL32.248) 546 */ 547 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken) 548 { 549 DWORD dwSessionId, dwLength; 550 BOOL bOpenToken = FALSE; 551 552 TRACE("%p\n", hToken); 553 554 if (!hToken) 555 bOpenToken = SHOpenEffectiveToken(&hToken); 556 557 if (!hToken || 558 !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength)) 559 { 560 dwSessionId = 0; 561 } 562 563 if (bOpenToken) 564 CloseHandle(hToken); 565 566 return dwSessionId; 567 } 568 569 /************************************************************************* 570 * SHInvokePrivilegedFunctionW (SHELL32.246) 571 */ 572 EXTERN_C 573 HRESULT WINAPI 574 SHInvokePrivilegedFunctionW( 575 _In_ LPCWSTR pszName, 576 _In_ PRIVILEGED_FUNCTION fn, 577 _In_opt_ LPARAM lParam) 578 { 579 TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam); 580 581 if (!pszName || !fn) 582 return E_INVALIDARG; 583 584 HANDLE hToken = NULL; 585 TOKEN_PRIVILEGES NewPriv, PrevPriv; 586 BOOL bAdjusted = FALSE; 587 588 if (SHOpenEffectiveToken(&hToken) && 589 ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid)) 590 { 591 NewPriv.PrivilegeCount = 1; 592 NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 593 594 DWORD dwReturnSize; 595 bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv, 596 sizeof(PrevPriv), &PrevPriv, &dwReturnSize); 597 } 598 599 HRESULT hr = fn(lParam); 600 601 if (bAdjusted) 602 ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL); 603 604 if (hToken) 605 ::CloseHandle(hToken); 606 607 return hr; 608 } 609 610 /************************************************************************* 611 * SHTestTokenPrivilegeW (SHELL32.236) 612 * 613 * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php 614 */ 615 EXTERN_C 616 BOOL WINAPI 617 SHTestTokenPrivilegeW( 618 _In_opt_ HANDLE hToken, 619 _In_ LPCWSTR lpName) 620 { 621 LUID Luid; 622 DWORD dwLength; 623 PTOKEN_PRIVILEGES pTokenPriv; 624 HANDLE hNewToken = NULL; 625 BOOL ret = FALSE; 626 627 TRACE("(%p, %s)\n", hToken, debugstr_w(lpName)); 628 629 if (!lpName) 630 return FALSE; 631 632 if (!hToken) 633 { 634 if (!SHOpenEffectiveToken(&hNewToken)) 635 goto Quit; 636 637 if (!hNewToken) 638 return FALSE; 639 640 hToken = hNewToken; 641 } 642 643 if (!LookupPrivilegeValueW(NULL, lpName, &Luid)) 644 return FALSE; 645 646 dwLength = 0; 647 if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength)) 648 goto Quit; 649 650 pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength); 651 if (!pTokenPriv) 652 goto Quit; 653 654 if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength)) 655 { 656 UINT iPriv, cPrivs; 657 cPrivs = pTokenPriv->PrivilegeCount; 658 for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv) 659 { 660 ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid); 661 } 662 } 663 664 LocalFree(pTokenPriv); 665 666 Quit: 667 if (hToken == hNewToken) 668 CloseHandle(hNewToken); 669 670 return ret; 671 } 672 673 BOOL IsShutdownAllowed(VOID) 674 { 675 return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME); 676 } 677 678 /************************************************************************* 679 * IsSuspendAllowed (SHELL32.53) 680 */ 681 BOOL WINAPI IsSuspendAllowed(VOID) 682 { 683 TRACE("()\n"); 684 return IsShutdownAllowed() && IsPwrSuspendAllowed(); 685 } 686 687 /************************************************************************* 688 * SHGetShellStyleHInstance (SHELL32.749) 689 */ 690 EXTERN_C HINSTANCE 691 WINAPI 692 SHGetShellStyleHInstance(VOID) 693 { 694 HINSTANCE hInst = NULL; 695 WCHAR szPath[MAX_PATH], szColorName[100]; 696 HRESULT hr; 697 CStringW strShellStyle; 698 699 TRACE("SHGetShellStyleHInstance called\n"); 700 701 /* First, attempt to load the shellstyle dll from the current active theme */ 702 hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0); 703 if (FAILED(hr)) 704 goto DoDefault; 705 706 /* Strip the theme filename */ 707 PathRemoveFileSpecW(szPath); 708 709 strShellStyle = szPath; 710 strShellStyle += L"\\Shell\\"; 711 strShellStyle += szColorName; 712 strShellStyle += L"\\ShellStyle.dll"; 713 714 hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE); 715 if (hInst) 716 return hInst; 717 718 /* Otherwise, use the version stored in the System32 directory */ 719 DoDefault: 720 if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll", 721 szPath, _countof(szPath))) 722 { 723 ERR("Expand failed\n"); 724 return NULL; 725 } 726 return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE); 727 } 728 729 /************************************************************************* 730 * SHCreatePropertyBag (SHELL32.715) 731 */ 732 EXTERN_C HRESULT 733 WINAPI 734 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj) 735 { 736 return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj); 737 } 738 739 /************************************************************************* 740 * SheRemoveQuotesA (SHELL32.@) 741 */ 742 EXTERN_C LPSTR 743 WINAPI 744 SheRemoveQuotesA(LPSTR psz) 745 { 746 PCHAR pch; 747 748 if (*psz == '"') 749 { 750 for (pch = psz + 1; *pch && *pch != '"'; ++pch) 751 { 752 *(pch - 1) = *pch; 753 } 754 755 if (*pch == '"') 756 *(pch - 1) = ANSI_NULL; 757 } 758 759 return psz; 760 } 761 762 /************************************************************************* 763 * SheRemoveQuotesW (SHELL32.@) 764 * 765 * ExtractAssociatedIconExW uses this function. 766 */ 767 EXTERN_C LPWSTR 768 WINAPI 769 SheRemoveQuotesW(LPWSTR psz) 770 { 771 PWCHAR pch; 772 773 if (*psz == L'"') 774 { 775 for (pch = psz + 1; *pch && *pch != L'"'; ++pch) 776 { 777 *(pch - 1) = *pch; 778 } 779 780 if (*pch == L'"') 781 *(pch - 1) = UNICODE_NULL; 782 } 783 784 return psz; 785 } 786 787 /************************************************************************* 788 * SHFindComputer [SHELL32.91] 789 * 790 * Invokes the shell search in My Computer. Used in SHFindFiles. 791 * Two parameters are ignored. 792 */ 793 EXTERN_C BOOL 794 WINAPI 795 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch) 796 { 797 UNREFERENCED_PARAMETER(pidlRoot); 798 UNREFERENCED_PARAMETER(pidlSavedSearch); 799 800 TRACE("%p %p\n", pidlRoot, pidlSavedSearch); 801 802 IContextMenu *pCM; 803 HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER, 804 IID_IContextMenu, (void **)&pCM); 805 if (FAILED(hr)) 806 { 807 ERR("0x%08X\n", hr); 808 return hr; 809 } 810 811 CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) }; 812 InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}"; 813 InvokeInfo.nShow = SW_SHOWNORMAL; 814 hr = pCM->InvokeCommand(&InvokeInfo); 815 pCM->Release(); 816 817 return SUCCEEDED(hr); 818 } 819 820 static HRESULT 821 Int64ToStr( 822 _In_ LONGLONG llValue, 823 _Out_writes_(cchValue) LPWSTR pszValue, 824 _In_ UINT cchValue) 825 { 826 WCHAR szBuff[40]; 827 UINT ich = 0, ichValue; 828 #if (WINVER >= _WIN32_WINNT_VISTA) 829 BOOL bMinus = (llValue < 0); 830 831 if (bMinus) 832 llValue = -llValue; 833 #endif 834 835 if (cchValue <= 0) 836 return E_FAIL; 837 838 do 839 { 840 szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10)); 841 llValue /= 10; 842 } while (llValue != 0 && ich < _countof(szBuff) - 1); 843 844 #if (WINVER >= _WIN32_WINNT_VISTA) 845 if (bMinus && ich < _countof(szBuff)) 846 szBuff[ich++] = '-'; 847 #endif 848 849 for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue) 850 { 851 --ich; 852 pszValue[ichValue] = szBuff[ich]; 853 } 854 855 if (ichValue >= cchValue) 856 { 857 pszValue[cchValue - 1] = UNICODE_NULL; 858 return E_FAIL; 859 } 860 861 pszValue[ichValue] = UNICODE_NULL; 862 return S_OK; 863 } 864 865 static VOID 866 Int64GetNumFormat( 867 _Out_ NUMBERFMTW *pDest, 868 _In_opt_ const NUMBERFMTW *pSrc, 869 _In_ DWORD dwNumberFlags, 870 _Out_writes_(cchDecimal) LPWSTR pszDecimal, 871 _In_ INT cchDecimal, 872 _Out_writes_(cchThousand) LPWSTR pszThousand, 873 _In_ INT cchThousand) 874 { 875 WCHAR szBuff[20]; 876 877 if (pSrc) 878 *pDest = *pSrc; 879 else 880 dwNumberFlags = 0; 881 882 if (!(dwNumberFlags & FMT_USE_NUMDIGITS)) 883 { 884 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff)); 885 pDest->NumDigits = StrToIntW(szBuff); 886 } 887 888 if (!(dwNumberFlags & FMT_USE_LEADZERO)) 889 { 890 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff)); 891 pDest->LeadingZero = StrToIntW(szBuff); 892 } 893 894 if (!(dwNumberFlags & FMT_USE_GROUPING)) 895 { 896 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff)); 897 pDest->Grouping = StrToIntW(szBuff); 898 } 899 900 if (!(dwNumberFlags & FMT_USE_DECIMAL)) 901 { 902 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal); 903 pDest->lpDecimalSep = pszDecimal; 904 } 905 906 if (!(dwNumberFlags & FMT_USE_THOUSAND)) 907 { 908 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand); 909 pDest->lpThousandSep = pszThousand; 910 } 911 912 if (!(dwNumberFlags & FMT_USE_NEGNUMBER)) 913 { 914 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff)); 915 pDest->NegativeOrder = StrToIntW(szBuff); 916 } 917 } 918 919 /************************************************************************* 920 * Int64ToString [SHELL32.209] 921 * 922 * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php 923 */ 924 EXTERN_C 925 INT WINAPI 926 Int64ToString( 927 _In_ LONGLONG llValue, 928 _Out_writes_(cchOut) LPWSTR pszOut, 929 _In_ UINT cchOut, 930 _In_ BOOL bUseFormat, 931 _In_opt_ const NUMBERFMTW *pNumberFormat, 932 _In_ DWORD dwNumberFlags) 933 { 934 INT ret; 935 NUMBERFMTW NumFormat; 936 WCHAR szValue[80], szDecimalSep[6], szThousandSep[6]; 937 938 Int64ToStr(llValue, szValue, _countof(szValue)); 939 940 if (bUseFormat) 941 { 942 Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags, 943 szDecimalSep, _countof(szDecimalSep), 944 szThousandSep, _countof(szThousandSep)); 945 ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut); 946 if (ret) 947 --ret; 948 return ret; 949 } 950 951 if (FAILED(StringCchCopyW(pszOut, cchOut, szValue))) 952 return 0; 953 954 return lstrlenW(pszOut); 955 } 956 957 /************************************************************************* 958 * LargeIntegerToString [SHELL32.210] 959 * 960 * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php 961 */ 962 EXTERN_C 963 INT WINAPI 964 LargeIntegerToString( 965 _In_ const LARGE_INTEGER *pLargeInt, 966 _Out_writes_(cchOut) LPWSTR pszOut, 967 _In_ UINT cchOut, 968 _In_ BOOL bUseFormat, 969 _In_opt_ const NUMBERFMTW *pNumberFormat, 970 _In_ DWORD dwNumberFlags) 971 { 972 return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat, 973 pNumberFormat, dwNumberFlags); 974 } 975 976 /************************************************************************* 977 * CopyStreamUI [SHELL32.726] 978 * 979 * Copy a stream to another stream with optional progress display. 980 */ 981 EXTERN_C 982 HRESULT WINAPI 983 CopyStreamUI( 984 _In_ IStream *pSrc, 985 _Out_ IStream *pDst, 986 _Inout_opt_ IProgressDialog *pProgress, 987 _In_opt_ DWORDLONG dwlSize) 988 { 989 HRESULT hr = E_FAIL; 990 DWORD cbBuff, cbRead, dwSizeToWrite; 991 DWORDLONG cbDone; 992 LPVOID pBuff; 993 CComHeapPtr<BYTE> pHeapPtr; 994 STATSTG Stat; 995 BYTE abBuff[1024]; 996 997 TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize); 998 999 if (dwlSize == 0) // Invalid size? 1000 { 1001 // Get the stream size 1002 ZeroMemory(&Stat, sizeof(Stat)); 1003 if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME))) 1004 pProgress = NULL; // No size info. Disable progress 1005 else 1006 dwlSize = Stat.cbSize.QuadPart; 1007 } 1008 1009 if (!pProgress) // Progress is disabled? 1010 { 1011 ULARGE_INTEGER uliSize; 1012 1013 if (dwlSize > 0) 1014 uliSize.QuadPart = dwlSize; 1015 else 1016 uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE; 1017 1018 return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch 1019 } 1020 1021 // Allocate the buffer if necessary 1022 if (dwlSize > 0 && dwlSize <= sizeof(abBuff)) 1023 { 1024 cbBuff = sizeof(abBuff); 1025 pBuff = abBuff; 1026 } 1027 else 1028 { 1029 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000 1030 cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE; 1031 if (pHeapPtr.AllocateBytes(cbBuff)) 1032 { 1033 pBuff = pHeapPtr; 1034 } 1035 else // Low memory? 1036 { 1037 cbBuff = sizeof(abBuff); 1038 pBuff = abBuff; 1039 } 1040 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE 1041 } 1042 1043 // Start reading 1044 LARGE_INTEGER zero; 1045 zero.QuadPart = 0; 1046 pSrc->Seek(zero, 0, NULL); 1047 pDst->Seek(zero, 0, NULL); 1048 cbDone = 0; 1049 pProgress->SetProgress64(cbDone, dwlSize); 1050 1051 // Repeat reading and writing until goal 1052 for (;;) 1053 { 1054 hr = pSrc->Read(pBuff, cbBuff, &cbRead); 1055 if (FAILED(hr)) 1056 break; 1057 1058 // Calculate the size to write 1059 if (dwlSize > 0) 1060 dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead); 1061 else 1062 dwSizeToWrite = cbRead; 1063 1064 if (dwSizeToWrite == 0) // No need to write? 1065 { 1066 hr = S_OK; 1067 break; 1068 } 1069 1070 hr = pDst->Write(pBuff, dwSizeToWrite, NULL); 1071 if (hr != S_OK) 1072 break; 1073 1074 cbDone += dwSizeToWrite; 1075 1076 if (pProgress->HasUserCancelled()) // Cancelled? 1077 { 1078 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); 1079 break; 1080 } 1081 pProgress->SetProgress64(cbDone, dwlSize); 1082 1083 if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal? 1084 { 1085 hr = S_OK; 1086 break; 1087 } 1088 } 1089 1090 return hr; 1091 } 1092 1093 /************************************************************************* 1094 * SHOpenPropSheetA [SHELL32.707] 1095 * 1096 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta 1097 */ 1098 EXTERN_C 1099 BOOL WINAPI 1100 SHOpenPropSheetA( 1101 _In_opt_ LPCSTR pszCaption, 1102 _In_opt_ HKEY *ahKeys, 1103 _In_ UINT cKeys, 1104 _In_ const CLSID *pclsidDefault, 1105 _In_ IDataObject *pDataObject, 1106 _In_opt_ IShellBrowser *pShellBrowser, 1107 _In_opt_ LPCSTR pszStartPage) 1108 { 1109 CStringW strStartPageW, strCaptionW; 1110 LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL; 1111 1112 TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault, 1113 pDataObject, pShellBrowser, debugstr_a(pszStartPage)); 1114 1115 if (pszCaption) 1116 { 1117 strStartPageW = pszCaption; 1118 pszCaptionW = strCaptionW; 1119 } 1120 1121 if (pszStartPage) 1122 { 1123 strStartPageW = pszStartPage; 1124 pszStartPageW = strStartPageW; 1125 } 1126 1127 return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault, 1128 pDataObject, pShellBrowser, pszStartPageW); 1129 } 1130 1131 /************************************************************************* 1132 * Activate_RunDLL [SHELL32.105] 1133 * 1134 * Unlocks the foreground window and allows the shell window to become the 1135 * foreground window. Every parameter is unused. 1136 */ 1137 EXTERN_C 1138 BOOL WINAPI 1139 Activate_RunDLL( 1140 _In_ HWND hwnd, 1141 _In_ HINSTANCE hinst, 1142 _In_ LPCWSTR cmdline, 1143 _In_ INT cmdshow) 1144 { 1145 DWORD dwProcessID; 1146 1147 UNREFERENCED_PARAMETER(hwnd); 1148 UNREFERENCED_PARAMETER(hinst); 1149 UNREFERENCED_PARAMETER(cmdline); 1150 UNREFERENCED_PARAMETER(cmdshow); 1151 1152 TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline); 1153 1154 GetWindowThreadProcessId(GetShellWindow(), &dwProcessID); 1155 return AllowSetForegroundWindow(dwProcessID); 1156 } 1157 1158 /************************************************************************* 1159 * SHStartNetConnectionDialogA (SHELL32.12) 1160 * 1161 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga 1162 */ 1163 EXTERN_C 1164 HRESULT WINAPI 1165 SHStartNetConnectionDialogA( 1166 _In_ HWND hwnd, 1167 _In_ LPCSTR pszRemoteName, 1168 _In_ DWORD dwType) 1169 { 1170 LPCWSTR pszRemoteNameW = NULL; 1171 CStringW strRemoteNameW; 1172 1173 TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType); 1174 1175 if (pszRemoteName) 1176 { 1177 strRemoteNameW = pszRemoteName; 1178 pszRemoteNameW = strRemoteNameW; 1179 } 1180 1181 return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType); 1182 } 1183 1184 /************************************************************************* 1185 * Helper functions for PathIsEqualOrSubFolder 1186 */ 1187 1188 static INT 1189 DynamicPathCommonPrefixW( 1190 _In_ LPCWSTR lpszPath1, 1191 _In_ LPCWSTR lpszPath2, 1192 _Out_ CStringW& strPath) 1193 { 1194 SIZE_T cchPath1 = wcslen(lpszPath1); 1195 SIZE_T cchPath2 = wcslen(lpszPath2); 1196 LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16); 1197 INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath); 1198 strPath.ReleaseBuffer(); 1199 return ret; 1200 } 1201 1202 EXTERN_C HRESULT WINAPI 1203 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax); 1204 1205 static HRESULT 1206 DynamicSHGetPathFromIDListW( 1207 _In_ LPCITEMIDLIST pidl, 1208 _Out_ CStringW& strPath) 1209 { 1210 HRESULT hr; 1211 1212 for (UINT cchPath = MAX_PATH;; cchPath *= 2) 1213 { 1214 LPWSTR lpszPath = strPath.GetBuffer(cchPath); 1215 if (!lpszPath) 1216 return E_OUTOFMEMORY; 1217 1218 hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath); 1219 strPath.ReleaseBuffer(); 1220 1221 if (hr != E_NOT_SUFFICIENT_BUFFER) 1222 break; 1223 1224 if (cchPath >= MAXUINT / 2) 1225 { 1226 hr = E_FAIL; 1227 break; 1228 } 1229 } 1230 1231 if (FAILED(hr)) 1232 strPath.Empty(); 1233 1234 return hr; 1235 } 1236 1237 static HRESULT 1238 DynamicSHGetSpecialFolderPathW( 1239 _In_ HWND hwndOwner, 1240 _Out_ CStringW& strPath, 1241 _In_ INT nCSIDL, 1242 _In_ BOOL bCreate) 1243 { 1244 LPITEMIDLIST pidl; 1245 HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl); 1246 if (SUCCEEDED(hr)) 1247 { 1248 hr = DynamicSHGetPathFromIDListW(pidl, strPath); 1249 CoTaskMemFree(pidl); 1250 } 1251 1252 if (FAILED(hr)) 1253 strPath.Empty(); 1254 else if (bCreate) 1255 CreateDirectoryW(strPath, NULL); 1256 1257 return hr; 1258 } 1259 1260 static VOID 1261 DynamicPathRemoveBackslashW( 1262 _Out_ CStringW& strPath) 1263 { 1264 INT nLength = strPath.GetLength(); 1265 if (nLength > 0 && strPath[nLength - 1] == L'\\') 1266 strPath = strPath.Left(nLength - 1); 1267 } 1268 1269 /************************************************************************* 1270 * PathIsEqualOrSubFolder (SHELL32.755) 1271 */ 1272 EXTERN_C 1273 BOOL WINAPI 1274 PathIsEqualOrSubFolder( 1275 _In_ LPCWSTR pszPath1OrCSIDL, 1276 _In_ LPCWSTR pszPath2) 1277 { 1278 CStringW strCommon, strPath1; 1279 1280 TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2)); 1281 1282 if (IS_INTRESOURCE(pszPath1OrCSIDL)) 1283 { 1284 DynamicSHGetSpecialFolderPathW( 1285 NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE); 1286 } 1287 else 1288 { 1289 strPath1 = pszPath1OrCSIDL; 1290 } 1291 1292 DynamicPathRemoveBackslashW(strPath1); 1293 1294 if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon)) 1295 return FALSE; 1296 1297 return strPath1.CompareNoCase(strCommon) == 0; 1298 } 1299 1300 /************************************************************************* 1301 * SHGetRealIDL [SHELL32.98] 1302 */ 1303 EXTERN_C 1304 HRESULT WINAPI 1305 SHGetRealIDL( 1306 _In_ IShellFolder *psf, 1307 _In_ PCUITEMID_CHILD pidlSimple, 1308 _Outptr_ PITEMID_CHILD *ppidlReal) 1309 { 1310 HRESULT hr; 1311 STRRET strret; 1312 WCHAR szPath[MAX_PATH]; 1313 SFGAOF attrs; 1314 1315 *ppidlReal = NULL; 1316 1317 hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_INFOLDER | SHGDN_FORPARSING, 1318 &strret, 0); 1319 if (FAILED_UNEXPECTEDLY(hr)) 1320 return hr; 1321 1322 hr = StrRetToBufW(&strret, pidlSimple, szPath, _countof(szPath)); 1323 if (FAILED_UNEXPECTEDLY(hr)) 1324 return hr; 1325 1326 attrs = SFGAO_FILESYSTEM; 1327 hr = psf->GetAttributesOf(1, &pidlSimple, &attrs); 1328 if (SUCCEEDED(hr) && !(attrs & SFGAO_FILESYSTEM)) 1329 return SHILClone(pidlSimple, ppidlReal); 1330 1331 hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL); 1332 if (hr == E_INVALIDARG || hr == E_NOTIMPL) 1333 return SHILClone(pidlSimple, ppidlReal); 1334 1335 return hr; 1336 } 1337 1338 EXTERN_C HRESULT 1339 IUnknown_InitializeCommand( 1340 _In_ IUnknown *pUnk, 1341 _In_ PCWSTR pszCommandName, 1342 _In_opt_ IPropertyBag *pPB) 1343 { 1344 HRESULT hr; 1345 CComPtr<IInitializeCommand> pIC; 1346 if (SUCCEEDED(hr = pUnk->QueryInterface(IID_PPV_ARG(IInitializeCommand, &pIC)))) 1347 hr = pIC->Initialize(pszCommandName, pPB); 1348 return hr; 1349 } 1350 1351 EXTERN_C HRESULT 1352 InvokeIExecuteCommand( 1353 _In_ IExecuteCommand *pEC, 1354 _In_ PCWSTR pszCommandName, 1355 _In_opt_ IPropertyBag *pPB, 1356 _In_opt_ IShellItemArray *pSIA, 1357 _In_opt_ LPCMINVOKECOMMANDINFOEX pICI, 1358 _In_opt_ IUnknown *pSite) 1359 { 1360 if (!pEC) 1361 return E_INVALIDARG; 1362 1363 if (pSite) 1364 IUnknown_SetSite(pEC, pSite); 1365 IUnknown_InitializeCommand(pEC, pszCommandName, pPB); 1366 1367 CComPtr<IObjectWithSelection> pOWS; 1368 if (pSIA && SUCCEEDED(pEC->QueryInterface(IID_PPV_ARG(IObjectWithSelection, &pOWS)))) 1369 pOWS->SetSelection(pSIA); 1370 1371 DWORD dwKeyState = 0, fMask = pICI ? pICI->fMask : 0; 1372 pEC->SetNoShowUI((fMask & CMIC_MASK_FLAG_NO_UI) != 0); 1373 pEC->SetShowWindow(pICI ? pICI->nShow : SW_SHOW); 1374 if (fMask & CMIC_MASK_SHIFT_DOWN) 1375 dwKeyState |= MK_SHIFT; 1376 if (fMask & CMIC_MASK_CONTROL_DOWN) 1377 dwKeyState |= MK_CONTROL; 1378 pEC->SetKeyState(dwKeyState); 1379 if ((fMask & CMIC_MASK_UNICODE) && pICI->lpDirectoryW) 1380 pEC->SetDirectory(pICI->lpDirectoryW); 1381 if ((fMask & CMIC_MASK_UNICODE) && pICI->lpParametersW) 1382 pEC->SetParameters(pICI->lpParametersW); 1383 if (fMask & CMIC_MASK_PTINVOKE) 1384 pEC->SetPosition(pICI->ptInvoke); 1385 1386 HRESULT hr = pEC->Execute(); 1387 if (pSite) 1388 IUnknown_SetSite(pEC, NULL); 1389 return hr; 1390 } 1391 1392 EXTERN_C HRESULT 1393 InvokeIExecuteCommandWithDataObject( 1394 _In_ IExecuteCommand *pEC, 1395 _In_ PCWSTR pszCommandName, 1396 _In_opt_ IPropertyBag *pPB, 1397 _In_ IDataObject *pDO, 1398 _In_opt_ LPCMINVOKECOMMANDINFOEX pICI, 1399 _In_opt_ IUnknown *pSite) 1400 { 1401 CComPtr<IShellItemArray> pSIA; 1402 HRESULT hr = SHCreateShellItemArrayFromDataObject(pDO, IID_PPV_ARG(IShellItemArray, &pSIA)); 1403 return SUCCEEDED(hr) ? InvokeIExecuteCommand(pEC, pszCommandName, pPB, pSIA, pICI, pSite) : hr; 1404 } 1405 1406 static HRESULT 1407 GetCommandStringA(_In_ IContextMenu *pCM, _In_ UINT_PTR Id, _In_ UINT GCS, _Out_writes_(cchMax) LPSTR Buf, _In_ UINT cchMax) 1408 { 1409 HRESULT hr = pCM->GetCommandString(Id, GCS & ~GCS_UNICODE, NULL, Buf, cchMax); 1410 if (FAILED(hr)) 1411 { 1412 WCHAR buf[MAX_PATH]; 1413 hr = pCM->GetCommandString(Id, GCS | GCS_UNICODE, NULL, (LPSTR)buf, _countof(buf)); 1414 if (SUCCEEDED(hr)) 1415 hr = SHUnicodeToAnsi(buf, Buf, cchMax) > 0 ? S_OK : E_FAIL; 1416 } 1417 return hr; 1418 } 1419 1420 UINT 1421 GetDfmCmd(_In_ IContextMenu *pCM, _In_ LPCSTR verba) 1422 { 1423 CHAR buf[MAX_PATH]; 1424 if (IS_INTRESOURCE(verba)) 1425 { 1426 if (FAILED(GetCommandStringA(pCM, LOWORD(verba), GCS_VERB, buf, _countof(buf)))) 1427 return 0; 1428 verba = buf; 1429 } 1430 return MapVerbToDfmCmd(verba); // Returns DFM_CMD_* or 0 1431 } 1432