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