xref: /reactos/dll/win32/shell32/utils.cpp (revision 29c0e23f)
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 OpenEffectiveToken(DWORD DesiredAccess, HANDLE *phToken)
13 {
14     BOOL ret;
15 
16     if (phToken == NULL)
17     {
18         SetLastError(ERROR_INVALID_PARAMETER);
19         return FALSE;
20     }
21 
22     *phToken = NULL;
23 
24     ret = OpenThreadToken(GetCurrentThread(), DesiredAccess, FALSE, phToken);
25     if (!ret && GetLastError() == ERROR_NO_TOKEN)
26         ret = OpenProcessToken(GetCurrentProcess(), DesiredAccess, phToken);
27 
28     return ret;
29 }
30 
31 /*************************************************************************
32  *                SHOpenEffectiveToken (SHELL32.235)
33  */
34 EXTERN_C BOOL WINAPI SHOpenEffectiveToken(_Out_ LPHANDLE phToken)
35 {
36     TRACE("%p\n", phToken);
37     return OpenEffectiveToken(TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, phToken);
38 }
39 
40 /*************************************************************************
41  *                SHGetUserSessionId (SHELL32.248)
42  */
43 EXTERN_C DWORD WINAPI SHGetUserSessionId(_In_opt_ HANDLE hToken)
44 {
45     DWORD dwSessionId, dwLength;
46     BOOL bOpenToken = FALSE;
47 
48     TRACE("%p\n", hToken);
49 
50     if (!hToken)
51         bOpenToken = SHOpenEffectiveToken(&hToken);
52 
53     if (!hToken ||
54         !GetTokenInformation(hToken, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwLength))
55     {
56         dwSessionId = 0;
57     }
58 
59     if (bOpenToken)
60         CloseHandle(hToken);
61 
62     return dwSessionId;
63 }
64 
65 /*************************************************************************
66  *                SHInvokePrivilegedFunctionW (SHELL32.246)
67  */
68 EXTERN_C
69 HRESULT WINAPI
70 SHInvokePrivilegedFunctionW(
71     _In_z_ LPCWSTR pszName,
72     _In_ PRIVILEGED_FUNCTION fn,
73     _In_opt_ LPARAM lParam)
74 {
75     TRACE("(%s %p %p)\n", debugstr_w(pszName), fn, lParam);
76 
77     if (!pszName || !fn)
78         return E_INVALIDARG;
79 
80     HANDLE hToken = NULL;
81     TOKEN_PRIVILEGES NewPriv, PrevPriv;
82     BOOL bAdjusted = FALSE;
83 
84     if (SHOpenEffectiveToken(&hToken) &&
85         ::LookupPrivilegeValueW(NULL, pszName, &NewPriv.Privileges[0].Luid))
86     {
87         NewPriv.PrivilegeCount = 1;
88         NewPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
89 
90         DWORD dwReturnSize;
91         bAdjusted = ::AdjustTokenPrivileges(hToken, FALSE, &NewPriv,
92                                             sizeof(PrevPriv), &PrevPriv, &dwReturnSize);
93     }
94 
95     HRESULT hr = fn(lParam);
96 
97     if (bAdjusted)
98         ::AdjustTokenPrivileges(hToken, FALSE, &PrevPriv, 0, NULL, NULL);
99 
100     if (hToken)
101         ::CloseHandle(hToken);
102 
103     return hr;
104 }
105 
106 /*************************************************************************
107  *                SHTestTokenPrivilegeW (SHELL32.236)
108  *
109  * @see http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php
110  */
111 EXTERN_C
112 BOOL WINAPI
113 SHTestTokenPrivilegeW(_In_opt_ HANDLE hToken, _In_z_ LPCWSTR lpName)
114 {
115     LUID Luid;
116     DWORD dwLength;
117     PTOKEN_PRIVILEGES pTokenPriv;
118     HANDLE hNewToken = NULL;
119     BOOL ret = FALSE;
120 
121     TRACE("(%p, %s)\n", hToken, debugstr_w(lpName));
122 
123     if (!lpName)
124         return FALSE;
125 
126     if (!hToken)
127     {
128         if (!SHOpenEffectiveToken(&hNewToken))
129             goto Quit;
130 
131         if (!hNewToken)
132             return FALSE;
133 
134         hToken = hNewToken;
135     }
136 
137     if (!LookupPrivilegeValueW(NULL, lpName, &Luid))
138         return FALSE;
139 
140     dwLength = 0;
141     if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength))
142         goto Quit;
143 
144     pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength);
145     if (!pTokenPriv)
146         goto Quit;
147 
148     if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength))
149     {
150         UINT iPriv, cPrivs;
151         cPrivs = pTokenPriv->PrivilegeCount;
152         for (iPriv = 0; !ret && iPriv < cPrivs; ++iPriv)
153         {
154             ret = RtlEqualLuid(&Luid, &pTokenPriv->Privileges[iPriv].Luid);
155         }
156     }
157 
158     LocalFree(pTokenPriv);
159 
160 Quit:
161     if (hToken == hNewToken)
162         CloseHandle(hNewToken);
163 
164     return ret;
165 }
166 
167 /*************************************************************************
168  *                SHGetShellStyleHInstance (SHELL32.749)
169  */
170 EXTERN_C HINSTANCE
171 WINAPI
172 SHGetShellStyleHInstance(VOID)
173 {
174     HINSTANCE hInst = NULL;
175     WCHAR szPath[MAX_PATH], szColorName[100];
176     HRESULT hr;
177     CStringW strShellStyle;
178 
179     TRACE("SHGetShellStyleHInstance called\n");
180 
181     /* First, attempt to load the shellstyle dll from the current active theme */
182     hr = GetCurrentThemeName(szPath, _countof(szPath), szColorName, _countof(szColorName), NULL, 0);
183     if (FAILED(hr))
184         goto DoDefault;
185 
186     /* Strip the theme filename */
187     PathRemoveFileSpecW(szPath);
188 
189     strShellStyle = szPath;
190     strShellStyle += L"\\Shell\\";
191     strShellStyle += szColorName;
192     strShellStyle += L"\\ShellStyle.dll";
193 
194     hInst = LoadLibraryExW(strShellStyle, NULL, LOAD_LIBRARY_AS_DATAFILE);
195     if (hInst)
196         return hInst;
197 
198     /* Otherwise, use the version stored in the System32 directory */
199 DoDefault:
200     if (!ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\ShellStyle.dll",
201                                    szPath, _countof(szPath)))
202     {
203         ERR("Expand failed\n");
204         return NULL;
205     }
206     return LoadLibraryExW(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
207 }
208 
209 /*************************************************************************
210  *                SHCreatePropertyBag (SHELL32.715)
211  */
212 EXTERN_C HRESULT
213 WINAPI
214 SHCreatePropertyBag(_In_ REFIID riid, _Out_ void **ppvObj)
215 {
216     return SHCreatePropertyBagOnMemory(STGM_READWRITE, riid, ppvObj);
217 }
218 
219 /*************************************************************************
220  *                SheRemoveQuotesA (SHELL32.@)
221  */
222 EXTERN_C LPSTR
223 WINAPI
224 SheRemoveQuotesA(LPSTR psz)
225 {
226     PCHAR pch;
227 
228     if (*psz == '"')
229     {
230         for (pch = psz + 1; *pch && *pch != '"'; ++pch)
231         {
232             *(pch - 1) = *pch;
233         }
234 
235         if (*pch == '"')
236             *(pch - 1) = ANSI_NULL;
237     }
238 
239     return psz;
240 }
241 
242 /*************************************************************************
243  *                SheRemoveQuotesW (SHELL32.@)
244  *
245  * ExtractAssociatedIconExW uses this function.
246  */
247 EXTERN_C LPWSTR
248 WINAPI
249 SheRemoveQuotesW(LPWSTR psz)
250 {
251     PWCHAR pch;
252 
253     if (*psz == L'"')
254     {
255         for (pch = psz + 1; *pch && *pch != L'"'; ++pch)
256         {
257             *(pch - 1) = *pch;
258         }
259 
260         if (*pch == L'"')
261             *(pch - 1) = UNICODE_NULL;
262     }
263 
264     return psz;
265 }
266 
267 /*************************************************************************
268  *  SHFindComputer [SHELL32.91]
269  *
270  * Invokes the shell search in My Computer. Used in SHFindFiles.
271  * Two parameters are ignored.
272  */
273 EXTERN_C BOOL
274 WINAPI
275 SHFindComputer(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlSavedSearch)
276 {
277     UNREFERENCED_PARAMETER(pidlRoot);
278     UNREFERENCED_PARAMETER(pidlSavedSearch);
279 
280     TRACE("%p %p\n", pidlRoot, pidlSavedSearch);
281 
282     IContextMenu *pCM;
283     HRESULT hr = CoCreateInstance(CLSID_ShellSearchExt, NULL, CLSCTX_INPROC_SERVER,
284                                   IID_IContextMenu, (void **)&pCM);
285     if (FAILED(hr))
286     {
287         ERR("0x%08X\n", hr);
288         return hr;
289     }
290 
291     CMINVOKECOMMANDINFO InvokeInfo = { sizeof(InvokeInfo) };
292     InvokeInfo.lpParameters = "{996E1EB1-B524-11D1-9120-00A0C98BA67D}";
293     InvokeInfo.nShow = SW_SHOWNORMAL;
294     hr = pCM->InvokeCommand(&InvokeInfo);
295     pCM->Release();
296 
297     return SUCCEEDED(hr);
298 }
299 
300 static HRESULT
301 Int64ToStr(
302     _In_ LONGLONG llValue,
303     _Out_writes_z_(cchValue) LPWSTR pszValue,
304     _In_ UINT cchValue)
305 {
306     WCHAR szBuff[40];
307     UINT ich = 0, ichValue;
308 #if (WINVER >= _WIN32_WINNT_VISTA)
309     BOOL bMinus = (llValue < 0);
310 
311     if (bMinus)
312         llValue = -llValue;
313 #endif
314 
315     if (cchValue <= 0)
316         return E_FAIL;
317 
318     do
319     {
320         szBuff[ich++] = (WCHAR)(L'0' + (llValue % 10));
321         llValue /= 10;
322     } while (llValue != 0 && ich < _countof(szBuff) - 1);
323 
324 #if (WINVER >= _WIN32_WINNT_VISTA)
325     if (bMinus && ich < _countof(szBuff))
326         szBuff[ich++] = '-';
327 #endif
328 
329     for (ichValue = 0; ich > 0 && ichValue < cchValue; ++ichValue)
330     {
331         --ich;
332         pszValue[ichValue] = szBuff[ich];
333     }
334 
335     if (ichValue >= cchValue)
336     {
337         pszValue[cchValue - 1] = UNICODE_NULL;
338         return E_FAIL;
339     }
340 
341     pszValue[ichValue] = UNICODE_NULL;
342     return S_OK;
343 }
344 
345 static VOID
346 Int64GetNumFormat(
347     _Out_ NUMBERFMTW *pDest,
348     _In_opt_ const NUMBERFMTW *pSrc,
349     _In_ DWORD dwNumberFlags,
350     _Out_writes_z_(cchDecimal) LPWSTR pszDecimal,
351     _In_ INT cchDecimal,
352     _Out_writes_z_(cchThousand) LPWSTR pszThousand,
353     _In_ INT cchThousand)
354 {
355     WCHAR szBuff[20];
356 
357     if (pSrc)
358         *pDest = *pSrc;
359     else
360         dwNumberFlags = 0;
361 
362     if (!(dwNumberFlags & FMT_USE_NUMDIGITS))
363     {
364         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szBuff, _countof(szBuff));
365         pDest->NumDigits = StrToIntW(szBuff);
366     }
367 
368     if (!(dwNumberFlags & FMT_USE_LEADZERO))
369     {
370         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szBuff, _countof(szBuff));
371         pDest->LeadingZero = StrToIntW(szBuff);
372     }
373 
374     if (!(dwNumberFlags & FMT_USE_GROUPING))
375     {
376         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szBuff, _countof(szBuff));
377         pDest->Grouping = StrToIntW(szBuff);
378     }
379 
380     if (!(dwNumberFlags & FMT_USE_DECIMAL))
381     {
382         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, pszDecimal, cchDecimal);
383         pDest->lpDecimalSep = pszDecimal;
384     }
385 
386     if (!(dwNumberFlags & FMT_USE_THOUSAND))
387     {
388         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, cchThousand);
389         pDest->lpThousandSep = pszThousand;
390     }
391 
392     if (!(dwNumberFlags & FMT_USE_NEGNUMBER))
393     {
394         GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szBuff, _countof(szBuff));
395         pDest->NegativeOrder = StrToIntW(szBuff);
396     }
397 }
398 
399 /*************************************************************************
400  *  Int64ToString [SHELL32.209]
401  *
402  * @see http://undoc.airesoft.co.uk/shell32.dll/Int64ToString.php
403  */
404 EXTERN_C
405 INT WINAPI
406 Int64ToString(
407     _In_ LONGLONG llValue,
408     _Out_writes_z_(cchOut) LPWSTR pszOut,
409     _In_ UINT cchOut,
410     _In_ BOOL bUseFormat,
411     _In_opt_ const NUMBERFMTW *pNumberFormat,
412     _In_ DWORD dwNumberFlags)
413 {
414     INT ret;
415     NUMBERFMTW NumFormat;
416     WCHAR szValue[80], szDecimalSep[6], szThousandSep[6];
417 
418     Int64ToStr(llValue, szValue, _countof(szValue));
419 
420     if (bUseFormat)
421     {
422         Int64GetNumFormat(&NumFormat, pNumberFormat, dwNumberFlags,
423                           szDecimalSep, _countof(szDecimalSep),
424                           szThousandSep, _countof(szThousandSep));
425         ret = GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szValue, &NumFormat, pszOut, cchOut);
426         if (ret)
427             --ret;
428         return ret;
429     }
430 
431     if (FAILED(StringCchCopyW(pszOut, cchOut, szValue)))
432         return 0;
433 
434     return lstrlenW(pszOut);
435 }
436 
437 /*************************************************************************
438  *  LargeIntegerToString [SHELL32.210]
439  *
440  * @see http://undoc.airesoft.co.uk/shell32.dll/LargeIntegerToString.php
441  */
442 EXTERN_C
443 INT WINAPI
444 LargeIntegerToString(
445     _In_ const LARGE_INTEGER *pLargeInt,
446     _Out_writes_z_(cchOut) LPWSTR pszOut,
447     _In_ UINT cchOut,
448     _In_ BOOL bUseFormat,
449     _In_opt_ const NUMBERFMTW *pNumberFormat,
450     _In_ DWORD dwNumberFlags)
451 {
452     return Int64ToString(pLargeInt->QuadPart, pszOut, cchOut, bUseFormat,
453                          pNumberFormat, dwNumberFlags);
454 }
455