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