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