xref: /reactos/dll/win32/shell32/utils.cpp (revision f7d612f3)
1 /*
2  * PROJECT:     shell32
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     Utility functions
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "precomp.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(shell);
11 
12 static BOOL
13 OpenEffectiveToken(
14     _In_ DWORD DesiredAccess,
15     _Out_ HANDLE *phToken)
16 {
17     BOOL ret;
18 
19     if (phToken == NULL)
20     {
21         SetLastError(ERROR_INVALID_PARAMETER);
22         return FALSE;
23     }
24 
25     *phToken = NULL;
26 
27     ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken);
28     if (!ret && GetLastError() == ERROR_NO_TOKEN)
29         ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken);
30 
31     return ret;
32 }
33 
34 /*************************************************************************
35  *                SHSetFolderPathA (SHELL32.231)
36  *
37  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shsetfolderpatha
38  */
39 EXTERN_C
40 HRESULT WINAPI
41 SHSetFolderPathA(
42     _In_ INT csidl,
43     _In_ HANDLE hToken,
44     _In_ DWORD dwFlags,
45     _In_ LPCSTR pszPath)
46 {
47     TRACE("(%d, %p, 0x%X, %s)\n", csidl, hToken, dwFlags, debugstr_a(pszPath));
48     CStringW strPathW(pszPath);
49     return SHSetFolderPathW(csidl, hToken, dwFlags, strPathW);
50 }
51 
52 /*************************************************************************
53  *                PathIsSlowA (SHELL32.240)
54  *
55  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathisslowa
56  */
57 EXTERN_C
58 BOOL WINAPI
59 PathIsSlowA(
60     _In_ LPCSTR pszFile,
61     _In_ DWORD dwAttr)
62 {
63     TRACE("(%s, 0x%X)\n", debugstr_a(pszFile), dwAttr);
64     CStringW strFileW(pszFile);
65     return PathIsSlowW(strFileW, dwAttr);
66 }
67 
68 /*************************************************************************
69  *                ExtractIconResInfoA (SHELL32.221)
70  */
71 EXTERN_C
72 WORD WINAPI
73 ExtractIconResInfoA(
74     _In_ HANDLE hHandle,
75     _In_ LPCSTR lpFileName,
76     _In_ WORD wIndex,
77     _Out_ LPWORD lpSize,
78     _Out_ LPHANDLE lpIcon)
79 {
80     TRACE("(%p, %s, %u, %p, %p)\n", hHandle, debugstr_a(lpFileName), wIndex, lpSize, lpIcon);
81 
82     if (!lpFileName)
83         return 0;
84 
85     CStringW strFileNameW(lpFileName);
86     return ExtractIconResInfoW(hHandle, strFileNameW, wIndex, lpSize, lpIcon);
87 }
88 
89 /*************************************************************************
90  *                ShortSizeFormatW (SHELL32.204)
91  */
92 EXTERN_C
93 LPWSTR WINAPI
94 ShortSizeFormatW(
95     _In_ DWORD dwNumber,
96     _Out_writes_(0x8FFF) LPWSTR pszBuffer)
97 {
98     TRACE("(%lu, %p)\n", dwNumber, pszBuffer);
99     return StrFormatByteSizeW(dwNumber, pszBuffer, 0x8FFF);
100 }
101 
102 /*************************************************************************
103  *                SHOpenEffectiveToken (SHELL32.235)
104  */
105 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken)
106 {
107     TRACE("%p\n", phToken);
108     return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken);
109 }
110 
111 /*************************************************************************
112  *                SHGetUserSessionId (SHELL32.248)
113  */
114 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken)
115 {
116     DWORD dwSessionId, dwLength;
117     BOOL bOpenToken = FALSE;
118 
119     TRACE("%p\n", hToken);
120 
121     if (!hToken)
122         bOpenToken = SHOpenEffectiveToken(&hToken);
123 
124     if (!hToken ||
125         !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength))
126     {
127         dwSessionId = 0;
128     }
129 
130     if (bOpenToken)
131         CloseHandle(hToken);
132 
133     return dwSessionId;
134 }
135 
136 /*************************************************************************
137  *                SHInvokePrivilegedFunctionW (SHELL32.246)
138  */
139 EXTERN_C
140 HRESULT WINAPI
141 SHInvokePrivilegedFunctionW(
142     _In_ LPCWSTR pszName,
143     _In_ PRIVILEGED_FUNCTION fn,
144     _In_opt_ LPARAM lParam)
145 {
146     TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam);
147 
148     if (!pszName || !fn)
149         return E_INVALIDARG;
150 
151     HANDLE hToken = NULL;
152     TOKEN_PRIVILEGES NewPriv, PrevPriv;
153     BOOL bAdjusted = FALSE;
154 
155     if (SHOpenEffectiveToken(&hToken) &&
156         ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid))
157     {
158         NewPriv.PrivilegeCount = 1;
159         NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
160 
161         DWORD dwReturnSize;
162         bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv,
163                                             sizeof(PrevPriv), &PrevPriv, &dwReturnSize);
164     }
165 
166     HRESULT hr = fn(lParam);
167 
168     if (bAdjusted)
169         ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL);
170 
171     if (hToken)
172         ::CloseHandle(hToken);
173 
174     return hr;
175 }
176 
177 /*************************************************************************
178  *                SHTestTokenPrivilegeW (SHELL32.236)
179  *
180  * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php
181  */
182 EXTERN_C
183 BOOL WINAPI
184 SHTestTokenPrivilegeW(
185     _In_opt_ HANDLE hToken,
186     _In_ LPCWSTR lpName)
187 {
188     LUID Luid;
189     DWORD dwLength;
190     PTOKEN_PRIVILEGES pTokenPriv;
191     HANDLE hNewToken = NULL;
192     BOOL ret = FALSE;
193 
194     TRACE("(%p, %s)\n", hToken, debugstr_w(lpName));
195 
196     if (!lpName)
197         return FALSE;
198 
199     if (!hToken)
200     {
201         if (!SHOpenEffectiveToken(&hNewToken))
202             goto Quit;
203 
204         if (!hNewToken)
205             return FALSE;
206 
207         hToken = hNewToken;
208     }
209 
210     if (!LookupPrivilegeValueW(NULL, lpName, &Luid))
211         return FALSE;
212 
213     dwLength = 0;
214     if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength))
215         goto Quit;
216 
217     pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength);
218     if (!pTokenPriv)
219         goto Quit;
220 
221     if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength))
222     {
223         UINT iPriv, cPrivs;
224         cPrivs = pTokenPriv->PrivilegeCount;
225         for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv)
226         {
227             ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid);
228         }
229     }
230 
231     LocalFree(pTokenPriv);
232 
233 Quit:
234     if (hToken == hNewToken)
235         CloseHandle(hNewToken);
236 
237     return ret;
238 }
239 
240 BOOL IsShutdownAllowed(VOID)
241 {
242     return SHTestTokenPrivilegeW(NULL, SE_SHUTDOWN_NAME);
243 }
244 
245 /*************************************************************************
246  *                IsSuspendAllowed (SHELL32.53)
247  */
248 BOOL WINAPI IsSuspendAllowed(VOID)
249 {
250     TRACE("()\n");
251     return IsShutdownAllowed() && IsPwrSuspendAllowed();
252 }
253 
254 /*************************************************************************
255  *                SHGetShellStyleHInstance (SHELL32.749)
256  */
257 EXTERN_C HINSTANCE
258 WINAPI
259 SHGetShellStyleHInstance(VOID)
260 {
261     HINSTANCE hInst = NULL;
262     WCHAR szPath[MAX_PATH], szColorName[100];
263     HRESULT hr;
264     CStringW strShellStyle;
265 
266     TRACE("SHGetShellStyleHInstance called\n");
267 
268     /* First, attempt to load the shellstyle dll from the current active theme */
269     hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0);
270     if (FAILED(hr))
271         goto DoDefault;
272 
273     /* Strip the theme filename */
274     PathRemoveFileSpecW(szPath);
275 
276     strShellStyle = szPath;
277     strShellStyle += L"\\Shell\\";
278     strShellStyle += szColorName;
279     strShellStyle += L"\\ShellStyle.dll";
280 
281     hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE);
282     if (hInst)
283         return hInst;
284 
285     /* Otherwise, use the version stored in the System32 directory */
286 DoDefault:
287     if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll",
288                                    szPath, _countof(szPath)))
289     {
290         ERR("Expand failed\n");
291         return NULL;
292     }
293     return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
294 }
295 
296 /*************************************************************************
297  *                SHCreatePropertyBag (SHELL32.715)
298  */
299 EXTERN_C HRESULT
300 WINAPI
301 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj)
302 {
303     return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj);
304 }
305 
306 /*************************************************************************
307  *                SheRemoveQuotesA (SHELL32.@)
308  */
309 EXTERN_C LPSTR
310 WINAPI
311 SheRemoveQuotesA(LPSTR psz)
312 {
313     PCHAR pch;
314 
315     if (*psz == '"')
316     {
317         for (pch = psz + 1; *pch && *pch != '"'; ++pch)
318         {
319             *(pch - 1) = *pch;
320         }
321 
322         if (*pch == '"')
323             *(pch - 1) = ANSI_NULL;
324     }
325 
326     return psz;
327 }
328 
329 /*************************************************************************
330  *                SheRemoveQuotesW (SHELL32.@)
331  *
332  * ExtractAssociatedIconExW uses this function.
333  */
334 EXTERN_C LPWSTR
335 WINAPI
336 SheRemoveQuotesW(LPWSTR psz)
337 {
338     PWCHAR pch;
339 
340     if (*psz == L'"')
341     {
342         for (pch = psz + 1; *pch && *pch != L'"'; ++pch)
343         {
344             *(pch - 1) = *pch;
345         }
346 
347         if (*pch == L'"')
348             *(pch - 1) = UNICODE_NULL;
349     }
350 
351     return psz;
352 }
353 
354 /*************************************************************************
355  *  SHFindComputer [SHELL32.91]
356  *
357  * Invokes the shell search in My Computer. Used in SHFindFiles.
358  * Two parameters are ignored.
359  */
360 EXTERN_C BOOL
361 WINAPI
362 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch)
363 {
364     UNREFERENCED_PARAMETER(pidlRoot);
365     UNREFERENCED_PARAMETER(pidlSavedSearch);
366 
367     TRACE("%p %p\n", pidlRoot, pidlSavedSearch);
368 
369     IContextMenu *pCM;
370     HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER,
371                                   IID_IContextMenu, (void **)&pCM);
372     if (FAILED(hr))
373     {
374         ERR("0x%08X\n", hr);
375         return hr;
376     }
377 
378     CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) };
379     InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}";
380     InvokeInfo.nShow = SW_SHOWNORMAL;
381     hr = pCM->InvokeCommand(&InvokeInfo);
382     pCM->Release();
383 
384     return SUCCEEDED(hr);
385 }
386 
387 static HRESULT
388 Int64ToStr(
389     _In_ LONGLONG llValue,
390     _Out_writes_(cchValue) LPWSTR pszValue,
391     _In_ UINT cchValue)
392 {
393     WCHAR szBuff[40];
394     UINT ich = 0, ichValue;
395 #if (WINVER >= _WIN32_WINNT_VISTA)
396     BOOL bMinus = (llValue < 0);
397 
398     if (bMinus)
399         llValue = -llValue;
400 #endif
401 
402     if (cchValue <= 0)
403         return E_FAIL;
404 
405     do
406     {
407         szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10));
408         llValue /= 10;
409     } while (llValue != 0 && ich < _countof(szBuff) - 1);
410 
411 #if (WINVER >= _WIN32_WINNT_VISTA)
412     if (bMinus && ich < _countof(szBuff))
413         szBuff[ich++] = '-';
414 #endif
415 
416     for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue)
417     {
418         --ich;
419         pszValue[ichValue] = szBuff[ich];
420     }
421 
422     if (ichValue >= cchValue)
423     {
424         pszValue[cchValue - 1] = UNICODE_NULL;
425         return E_FAIL;
426     }
427 
428     pszValue[ichValue] = UNICODE_NULL;
429     return S_OK;
430 }
431 
432 static VOID
433 Int64GetNumFormat(
434     _Out_ NUMBERFMTW *pDest,
435     _In_opt_ const NUMBERFMTW *pSrc,
436     _In_ DWORD dwNumberFlags,
437     _Out_writes_(cchDecimal) LPWSTR pszDecimal,
438     _In_ INT cchDecimal,
439     _Out_writes_(cchThousand) LPWSTR pszThousand,
440     _In_ INT cchThousand)
441 {
442     WCHAR szBuff[20];
443 
444     if (pSrc)
445         *pDest = *pSrc;
446     else
447         dwNumberFlags = 0;
448 
449     if (!(dwNumberFlags & FMT_USE_NUMDIGITS))
450     {
451         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff));
452         pDest->NumDigits = StrToIntW(szBuff);
453     }
454 
455     if (!(dwNumberFlags & FMT_USE_LEADZERO))
456     {
457         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff));
458         pDest->LeadingZero = StrToIntW(szBuff);
459     }
460 
461     if (!(dwNumberFlags & FMT_USE_GROUPING))
462     {
463         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff));
464         pDest->Grouping = StrToIntW(szBuff);
465     }
466 
467     if (!(dwNumberFlags & FMT_USE_DECIMAL))
468     {
469         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal);
470         pDest->lpDecimalSep = pszDecimal;
471     }
472 
473     if (!(dwNumberFlags & FMT_USE_THOUSAND))
474     {
475         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand);
476         pDest->lpThousandSep = pszThousand;
477     }
478 
479     if (!(dwNumberFlags & FMT_USE_NEGNUMBER))
480     {
481         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff));
482         pDest->NegativeOrder = StrToIntW(szBuff);
483     }
484 }
485 
486 /*************************************************************************
487  *  Int64ToString [SHELL32.209]
488  *
489  * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php
490  */
491 EXTERN_C
492 INT WINAPI
493 Int64ToString(
494     _In_ LONGLONG llValue,
495     _Out_writes_(cchOut) LPWSTR pszOut,
496     _In_ UINT cchOut,
497     _In_ BOOL bUseFormat,
498     _In_opt_ const NUMBERFMTW *pNumberFormat,
499     _In_ DWORD dwNumberFlags)
500 {
501     INT ret;
502     NUMBERFMTW NumFormat;
503     WCHAR szValue[80], szDecimalSep[6], szThousandSep[6];
504 
505     Int64ToStr(llValue, szValue, _countof(szValue));
506 
507     if (bUseFormat)
508     {
509         Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags,
510                           szDecimalSep, _countof(szDecimalSep),
511                           szThousandSep, _countof(szThousandSep));
512         ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut);
513         if (ret)
514             --ret;
515         return ret;
516     }
517 
518     if (FAILED(StringCchCopyW(pszOut, cchOut, szValue)))
519         return 0;
520 
521     return lstrlenW(pszOut);
522 }
523 
524 /*************************************************************************
525  *  LargeIntegerToString [SHELL32.210]
526  *
527  * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php
528  */
529 EXTERN_C
530 INT WINAPI
531 LargeIntegerToString(
532     _In_ const LARGE_INTEGER *pLargeInt,
533     _Out_writes_(cchOut) LPWSTR pszOut,
534     _In_ UINT cchOut,
535     _In_ BOOL bUseFormat,
536     _In_opt_ const NUMBERFMTW *pNumberFormat,
537     _In_ DWORD dwNumberFlags)
538 {
539     return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat,
540                          pNumberFormat, dwNumberFlags);
541 }
542 
543 /*************************************************************************
544  *  CopyStreamUI [SHELL32.726]
545  *
546  * Copy a stream to another stream with optional progress display.
547  */
548 EXTERN_C
549 HRESULT WINAPI
550 CopyStreamUI(
551     _In_ IStream *pSrc,
552     _Out_ IStream *pDst,
553     _Inout_opt_ IProgressDialog *pProgress,
554     _In_opt_ DWORDLONG dwlSize)
555 {
556     HRESULT hr = E_FAIL;
557     DWORD cbBuff, cbRead, dwSizeToWrite;
558     DWORDLONG cbDone;
559     LPVOID pBuff;
560     CComHeapPtr<BYTE> pHeapPtr;
561     STATSTG Stat;
562     BYTE abBuff[1024];
563 
564     TRACE("(%p, %p, %p, %I64u)\n", pSrc, pDst, pProgress, dwlSize);
565 
566     if (dwlSize == 0) // Invalid size?
567     {
568         // Get the stream size
569         ZeroMemory(&Stat, sizeof(Stat));
570         if (FAILED(pSrc->Stat(&Stat, STATFLAG_NONAME)))
571             pProgress = NULL; // No size info. Disable progress
572         else
573             dwlSize = Stat.cbSize.QuadPart;
574     }
575 
576     if (!pProgress) // Progress is disabled?
577     {
578         ULARGE_INTEGER uliSize;
579 
580         if (dwlSize > 0)
581             uliSize.QuadPart = dwlSize;
582         else
583             uliSize.HighPart = uliSize.LowPart = INVALID_FILE_SIZE;
584 
585         return pSrc->CopyTo(pDst, uliSize, NULL, NULL); // One punch
586     }
587 
588     // Allocate the buffer if necessary
589     if (dwlSize > 0 && dwlSize <= sizeof(abBuff))
590     {
591         cbBuff = sizeof(abBuff);
592         pBuff = abBuff;
593     }
594     else
595     {
596 #define COPY_STREAM_DEFAULT_BUFFER_SIZE 0x4000
597         cbBuff = COPY_STREAM_DEFAULT_BUFFER_SIZE;
598         if (pHeapPtr.AllocateBytes(cbBuff))
599         {
600             pBuff = pHeapPtr;
601         }
602         else // Low memory?
603         {
604             cbBuff = sizeof(abBuff);
605             pBuff = abBuff;
606         }
607 #undef COPY_STREAM_DEFAULT_BUFFER_SIZE
608     }
609 
610     // Start reading
611     LARGE_INTEGER zero;
612     zero.QuadPart = 0;
613     pSrc->Seek(zero, 0, NULL);
614     pDst->Seek(zero, 0, NULL);
615     cbDone = 0;
616     pProgress->SetProgress64(cbDone, dwlSize);
617 
618     // Repeat reading and writing until goal
619     for (;;)
620     {
621         hr = pSrc->Read(pBuff, cbBuff, &cbRead);
622         if (FAILED(hr))
623             break;
624 
625         // Calculate the size to write
626         if (dwlSize > 0)
627             dwSizeToWrite = (DWORD)min((DWORDLONG)(dwlSize - cbDone), (DWORDLONG)cbRead);
628         else
629             dwSizeToWrite = cbRead;
630 
631         if (dwSizeToWrite == 0) // No need to write?
632         {
633             hr = S_OK;
634             break;
635         }
636 
637         hr = pDst->Write(pBuff, dwSizeToWrite, NULL);
638         if (hr != S_OK)
639             break;
640 
641         cbDone += dwSizeToWrite;
642 
643         if (pProgress->HasUserCancelled()) // Cancelled?
644         {
645             hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
646             break;
647         }
648         pProgress->SetProgress64(cbDone, dwlSize);
649 
650         if (dwlSize > 0 && cbDone >= dwlSize) // Reached the goal?
651         {
652             hr = S_OK;
653             break;
654         }
655     }
656 
657     return hr;
658 }
659 
660 /*************************************************************************
661  *  SHOpenPropSheetA [SHELL32.707]
662  *
663  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta
664  */
665 EXTERN_C
666 BOOL WINAPI
667 SHOpenPropSheetA(
668     _In_opt_ LPCSTR pszCaption,
669     _In_opt_ HKEY *ahKeys,
670     _In_ UINT cKeys,
671     _In_ const CLSID *pclsidDefault,
672     _In_ IDataObject *pDataObject,
673     _In_opt_ IShellBrowser *pShellBrowser,
674     _In_opt_ LPCSTR pszStartPage)
675 {
676     CStringW strStartPageW, strCaptionW;
677     LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL;
678 
679     TRACE("(%s, %p, %u, %p, %p, %p, %s)", debugstr_a(pszCaption), ahKeys, cKeys, pclsidDefault,
680           pDataObject, pShellBrowser, debugstr_a(pszStartPage));
681 
682     if (pszCaption)
683     {
684         strStartPageW = pszCaption;
685         pszCaptionW = strCaptionW;
686     }
687 
688     if (pszStartPage)
689     {
690         strStartPageW = pszStartPage;
691         pszStartPageW = strStartPageW;
692     }
693 
694     return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault,
695                             pDataObject, pShellBrowser, pszStartPageW);
696 }
697 
698 /*************************************************************************
699  *  Activate_RunDLL [SHELL32.105]
700  *
701  * Unlocks the foreground window and allows the shell window to become the
702  * foreground window. Every parameter is unused.
703  */
704 EXTERN_C
705 BOOL WINAPI
706 Activate_RunDLL(
707     _In_ HWND hwnd,
708     _In_ HINSTANCE hinst,
709     _In_ LPCWSTR cmdline,
710     _In_ INT cmdshow)
711 {
712     DWORD dwProcessID;
713 
714     UNREFERENCED_PARAMETER(hwnd);
715     UNREFERENCED_PARAMETER(hinst);
716     UNREFERENCED_PARAMETER(cmdline);
717     UNREFERENCED_PARAMETER(cmdshow);
718 
719     TRACE("(%p, %p, %s, %d)\n", hwnd, hinst, debugstr_w(cmdline), cmdline);
720 
721     GetWindowThreadProcessId(GetShellWindow(), &dwProcessID);
722     return AllowSetForegroundWindow(dwProcessID);
723 }
724 
725 /*************************************************************************
726  *                SHStartNetConnectionDialogA (SHELL32.12)
727  *
728  * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shstartnetconnectiondialoga
729  */
730 EXTERN_C
731 HRESULT WINAPI
732 SHStartNetConnectionDialogA(
733     _In_ HWND hwnd,
734     _In_ LPCSTR pszRemoteName,
735     _In_ DWORD dwType)
736 {
737     LPCWSTR pszRemoteNameW = NULL;
738     CStringW strRemoteNameW;
739 
740     TRACE("(%p, %s, %lu)\n", hwnd, debugstr_a(pszRemoteName), dwType);
741 
742     if (pszRemoteName)
743     {
744         strRemoteNameW = pszRemoteName;
745         pszRemoteNameW = strRemoteNameW;
746     }
747 
748     return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType);
749 }
750 
751 /*************************************************************************
752  * Helper functions for PathIsEqualOrSubFolder
753  */
754 
755 static INT
756 DynamicPathCommonPrefixW(
757     _In_ LPCWSTR lpszPath1,
758     _In_ LPCWSTR lpszPath2,
759     _Out_ CStringW& strPath)
760 {
761     SIZE_T cchPath1 = wcslen(lpszPath1);
762     SIZE_T cchPath2 = wcslen(lpszPath2);
763     LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16);
764     INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath);
765     strPath.ReleaseBuffer();
766     return ret;
767 }
768 
769 EXTERN_C HRESULT WINAPI
770 SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax);
771 
772 static HRESULT
773 DynamicSHGetPathFromIDListW(
774     _In_ LPCITEMIDLIST pidl,
775     _Out_ CStringW& strPath)
776 {
777     HRESULT hr;
778 
779     for (UINT cchPath = MAX_PATH;; cchPath *= 2)
780     {
781         LPWSTR lpszPath = strPath.GetBuffer(cchPath);
782         if (!lpszPath)
783             return E_OUTOFMEMORY;
784 
785         hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath);
786         strPath.ReleaseBuffer();
787 
788         if (hr != E_NOT_SUFFICIENT_BUFFER)
789             break;
790 
791         if (cchPath >= MAXUINT / 2)
792         {
793             hr = E_FAIL;
794             break;
795         }
796     }
797 
798     if (FAILED(hr))
799         strPath.Empty();
800 
801     return hr;
802 }
803 
804 static HRESULT
805 DynamicSHGetSpecialFolderPathW(
806     _In_ HWND hwndOwner,
807     _Out_ CStringW& strPath,
808     _In_ INT nCSIDL,
809     _In_ BOOL bCreate)
810 {
811     LPITEMIDLIST pidl;
812     HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl);
813     if (SUCCEEDED(hr))
814     {
815         hr = DynamicSHGetPathFromIDListW(pidl, strPath);
816         CoTaskMemFree(pidl);
817     }
818 
819     if (FAILED(hr))
820         strPath.Empty();
821     else if (bCreate)
822         CreateDirectoryW(strPath, NULL);
823 
824     return hr;
825 }
826 
827 static VOID
828 DynamicPathRemoveBackslashW(
829     _Out_ CStringW& strPath)
830 {
831     INT nLength = strPath.GetLength();
832     if (nLength > 0 && strPath[nLength - 1] == L'\\')
833         strPath = strPath.Left(nLength - 1);
834 }
835 
836 /*************************************************************************
837  *                PathIsEqualOrSubFolder (SHELL32.755)
838  */
839 EXTERN_C
840 BOOL WINAPI
841 PathIsEqualOrSubFolder(
842     _In_ LPCWSTR pszPath1OrCSIDL,
843     _In_ LPCWSTR pszPath2)
844 {
845     CStringW strCommon, strPath1;
846 
847     TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2));
848 
849     if (IS_INTRESOURCE(pszPath1OrCSIDL))
850     {
851         DynamicSHGetSpecialFolderPathW(
852             NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE);
853     }
854     else
855     {
856         strPath1 = pszPath1OrCSIDL;
857     }
858 
859     DynamicPathRemoveBackslashW(strPath1);
860 
861     if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon))
862         return FALSE;
863 
864     return strPath1.CompareNoCase(strCommon) == 0;
865 }
866