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
SHILClone(_In_opt_ LPCITEMIDLIST pidl,_Outptr_ LPITEMIDLIST * ppidl)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
PathIsDotOrDotDotW(_In_ LPCWSTR pszPath)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
PathIsValidElement(_In_ LPCWSTR pszPath)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
PathIsDosDevice(_In_ LPCWSTR pszName)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
SHILAppend(_Inout_ LPITEMIDLIST pidl,_Inout_ LPITEMIDLIST * ppidl)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
OpenEffectiveToken(_In_ DWORD DesiredAccess,_Out_ HANDLE * phToken)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
Shell_TranslateIDListAlias(_In_ LPCITEMIDLIST pidl,_In_ HANDLE hToken,_Out_ LPITEMIDLIST * ppidlAlias,_In_ DWORD dwFlags)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
BindCtx_ContainsObject(_In_ IBindCtx * pBindCtx,_In_ LPCWSTR pszName)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
BindCtx_GetMode(_In_ IBindCtx * pbc,_In_ DWORD dwDefault)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
SHSkipJunctionBinding(_In_ IBindCtx * pbc,_In_ CLSID * pclsid)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
SHIsFileSysBindCtx(_In_ IBindCtx * pBindCtx,_Out_opt_ WIN32_FIND_DATAW * pFindData)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
Shell_FailForceReturn(_In_ HRESULT hr)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
SHBindToObjectEx(_In_opt_ IShellFolder * pShellFolder,_In_ LPCITEMIDLIST pidl,_In_opt_ IBindCtx * pBindCtx,_In_ REFIID riid,_Out_ void ** ppvObj)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
SHBindToObject(_In_opt_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ REFIID riid,_Out_ void ** ppvObj)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
Shell_DisplayNameOf(_In_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ DWORD dwFlags,_Out_ LPWSTR pszBuf,_In_ UINT cchBuf)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
SHGetAttributes(_In_ IShellFolder * psf,_In_ LPCITEMIDLIST pidl,_In_ DWORD dwAttributes)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
SHCoInitializeAnyApartment(VOID)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
SHGetNameAndFlagsW(_In_ LPCITEMIDLIST pidl,_In_ DWORD dwFlags,_Out_opt_ LPWSTR pszText,_In_ UINT cchBuf,_Inout_opt_ DWORD * pdwAttributes)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
BindCtx_GetUIWindow(_In_ IBindCtx * pBindCtx)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:
CDummyOleWindow()341 CDummyOleWindow() : m_cRefs(1), m_hWnd(NULL) { }
~CDummyOleWindow()342 virtual ~CDummyOleWindow() { }
343
344 // IUnknown methods
QueryInterface(REFIID riid,LPVOID * ppvObj)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 }
AddRef()354 STDMETHODIMP_(ULONG) AddRef() override
355 {
356 return ++m_cRefs;
357 }
Release()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
GetWindow(HWND * phWnd)369 STDMETHODIMP GetWindow(HWND *phWnd) override
370 {
371 *phWnd = m_hWnd;
372 if (!m_hWnd)
373 return E_NOTIMPL;
374 return S_OK;
375 }
ContextSensitiveHelp(BOOL fEnterMode)376 STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override
377 {
378 return E_NOTIMPL;
379 }
380 };
381
382 EXTERN_C HRESULT
BindCtx_RegisterObjectParam(_In_ IBindCtx * pBindCtx,_In_ LPOLESTR pszKey,_In_opt_ IUnknown * punk,_Out_ LPBC * ppbc)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
SHSetFolderPathA(_In_ INT csidl,_In_ HANDLE hToken,_In_ DWORD dwFlags,_In_ LPCSTR pszPath)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
PathIsSlowA(_In_ LPCSTR pszFile,_In_ DWORD dwAttr)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
ExtractIconResInfoA(_In_ HANDLE hHandle,_In_ LPCSTR lpFileName,_In_ WORD wIndex,_Out_ LPWORD lpSize,_Out_ LPHANDLE lpIcon)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 */
SHOpenEffectiveToken(_Out_ LPHANDLE phToken)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 */
SHGetUserSessionId(_In_opt_ HANDLE hToken)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
SHInvokePrivilegedFunctionW(_In_ LPCWSTR pszName,_In_ PRIVILEGED_FUNCTION fn,_In_opt_ LPARAM lParam)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
SHTestTokenPrivilegeW(_In_opt_ HANDLE hToken,_In_ LPCWSTR lpName)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
IsShutdownAllowed(VOID)628 BOOL IsShutdownAllowed(VOID)
629 {
630 return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME);
631 }
632
633 /*************************************************************************
634 * IsSuspendAllowed (SHELL32.53)
635 */
IsSuspendAllowed(VOID)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
SHGetShellStyleHInstance(VOID)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
SHCreatePropertyBag(_In_ REFIID riid,_Out_ void ** ppvObj)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
SheRemoveQuotesA(LPSTR psz)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
SheRemoveQuotesW(LPWSTR psz)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
SHFindComputer(LPCITEMIDLIST pidlRoot,LPCITEMIDLIST pidlSavedSearch)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
Int64ToStr(_In_ LONGLONG llValue,_Out_writes_ (cchValue)LPWSTR pszValue,_In_ UINT cchValue)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
Int64GetNumFormat(_Out_ NUMBERFMTW * pDest,_In_opt_ const NUMBERFMTW * pSrc,_In_ DWORD dwNumberFlags,_Out_writes_ (cchDecimal)LPWSTR pszDecimal,_In_ INT cchDecimal,_Out_writes_ (cchThousand)LPWSTR pszThousand,_In_ INT cchThousand)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
Int64ToString(_In_ LONGLONG llValue,_Out_writes_ (cchOut)LPWSTR pszOut,_In_ UINT cchOut,_In_ BOOL bUseFormat,_In_opt_ const NUMBERFMTW * pNumberFormat,_In_ DWORD dwNumberFlags)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
LargeIntegerToString(_In_ const LARGE_INTEGER * pLargeInt,_Out_writes_ (cchOut)LPWSTR pszOut,_In_ UINT cchOut,_In_ BOOL bUseFormat,_In_opt_ const NUMBERFMTW * pNumberFormat,_In_ DWORD dwNumberFlags)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
CopyStreamUI(_In_ IStream * pSrc,_Out_ IStream * pDst,_Inout_opt_ IProgressDialog * pProgress,_In_opt_ DWORDLONG dwlSize)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
SHOpenPropSheetA(_In_opt_ LPCSTR pszCaption,_In_opt_ HKEY * ahKeys,_In_ UINT cKeys,_In_ const CLSID * pclsidDefault,_In_ IDataObject * pDataObject,_In_opt_ IShellBrowser * pShellBrowser,_In_opt_ LPCSTR pszStartPage)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
Activate_RunDLL(_In_ HWND hwnd,_In_ HINSTANCE hinst,_In_ LPCWSTR cmdline,_In_ INT cmdshow)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
SHStartNetConnectionDialogA(_In_ HWND hwnd,_In_ LPCSTR pszRemoteName,_In_ DWORD dwType)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
DynamicPathCommonPrefixW(_In_ LPCWSTR lpszPath1,_In_ LPCWSTR lpszPath2,_Out_ CStringW & strPath)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
DynamicSHGetPathFromIDListW(_In_ LPCITEMIDLIST pidl,_Out_ CStringW & strPath)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
DynamicSHGetSpecialFolderPathW(_In_ HWND hwndOwner,_Out_ CStringW & strPath,_In_ INT nCSIDL,_In_ BOOL bCreate)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
DynamicPathRemoveBackslashW(_Out_ CStringW & strPath)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
PathIsEqualOrSubFolder(_In_ LPCWSTR pszPath1OrCSIDL,_In_ LPCWSTR pszPath2)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
SHGetRealIDL(_In_ IShellFolder * psf,_In_ PCUITEMID_CHILD pidlSimple,_Outptr_ PITEMID_CHILD * ppidlReal)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
IUnknown_InitializeCommand(_In_ IUnknown * pUnk,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB)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
InvokeIExecuteCommand(_In_ IExecuteCommand * pEC,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB,_In_opt_ IShellItemArray * pSIA,_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,_In_opt_ IUnknown * pSite)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
InvokeIExecuteCommandWithDataObject(_In_ IExecuteCommand * pEC,_In_ PCWSTR pszCommandName,_In_opt_ IPropertyBag * pPB,_In_ IDataObject * pDO,_In_opt_ LPCMINVOKECOMMANDINFOEX pICI,_In_opt_ IUnknown * pSite)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
GetCommandStringA(_In_ IContextMenu * pCM,_In_ UINT_PTR Id,_In_ UINT GCS,_Out_writes_ (cchMax)LPSTR Buf,_In_ UINT cchMax)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
GetDfmCmd(_In_ IContextMenu * pCM,_In_ LPCSTR verba)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