1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Misc functions 5 * COPYRIGHT: Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org) 6 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com) 7 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 8 */ 9 #include "rapps.h" 10 11 #include "misc.h" 12 13 static HANDLE hLog = NULL; 14 15 static BOOL bIsSys64ResultCached = FALSE; 16 static BOOL bIsSys64Result = FALSE; 17 18 INT GetWindowWidth(HWND hwnd) 19 { 20 RECT Rect; 21 22 GetWindowRect(hwnd, &Rect); 23 return (Rect.right - Rect.left); 24 } 25 26 INT GetWindowHeight(HWND hwnd) 27 { 28 RECT Rect; 29 30 GetWindowRect(hwnd, &Rect); 31 return (Rect.bottom - Rect.top); 32 } 33 34 INT GetClientWindowWidth(HWND hwnd) 35 { 36 RECT Rect; 37 38 GetClientRect(hwnd, &Rect); 39 return (Rect.right - Rect.left); 40 } 41 42 INT GetClientWindowHeight(HWND hwnd) 43 { 44 RECT Rect; 45 46 GetClientRect(hwnd, &Rect); 47 return (Rect.bottom - Rect.top); 48 } 49 50 VOID CopyTextToClipboard(LPCWSTR lpszText) 51 { 52 if (!OpenClipboard(NULL)) 53 { 54 return; 55 } 56 57 HRESULT hr; 58 HGLOBAL ClipBuffer; 59 LPWSTR Buffer; 60 DWORD cchBuffer; 61 62 EmptyClipboard(); 63 cchBuffer = wcslen(lpszText) + 1; 64 ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR)); 65 66 Buffer = (PWCHAR) GlobalLock(ClipBuffer); 67 hr = StringCchCopyW(Buffer, cchBuffer, lpszText); 68 GlobalUnlock(ClipBuffer); 69 70 if (SUCCEEDED(hr)) 71 SetClipboardData(CF_UNICODETEXT, ClipBuffer); 72 73 CloseClipboard(); 74 } 75 76 VOID ShowPopupMenuEx(HWND hwnd, HWND hwndOwner, UINT MenuID, UINT DefaultItem) 77 { 78 HMENU hMenu = NULL; 79 HMENU hPopupMenu; 80 MENUITEMINFO ItemInfo; 81 POINT pt; 82 83 if (MenuID) 84 { 85 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID)); 86 hPopupMenu = GetSubMenu(hMenu, 0); 87 } 88 else 89 { 90 hPopupMenu = GetMenu(hwnd); 91 } 92 93 ZeroMemory(&ItemInfo, sizeof(ItemInfo)); 94 ItemInfo.cbSize = sizeof(ItemInfo); 95 ItemInfo.fMask = MIIM_STATE; 96 97 GetMenuItemInfoW(hPopupMenu, DefaultItem, FALSE, &ItemInfo); 98 99 if (!(ItemInfo.fState & MFS_GRAYED)) 100 { 101 SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE); 102 } 103 104 GetCursorPos(&pt); 105 106 SetForegroundWindow(hwnd); 107 TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hwndOwner, NULL); 108 109 if (hMenu) 110 { 111 DestroyMenu(hMenu); 112 } 113 } 114 115 VOID ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem) 116 { 117 ShowPopupMenuEx(hwnd, hMainWnd, MenuID, DefaultItem); 118 } 119 120 121 BOOL StartProcess(ATL::CStringW &Path, BOOL Wait) 122 { 123 return StartProcess(const_cast<LPWSTR>(Path.GetString()), Wait);; 124 } 125 126 BOOL StartProcess(LPWSTR lpPath, BOOL Wait) 127 { 128 PROCESS_INFORMATION pi; 129 STARTUPINFOW si; 130 DWORD dwRet; 131 MSG msg; 132 133 ZeroMemory(&si, sizeof(si)); 134 si.cb = sizeof(si); 135 si.dwFlags = STARTF_USESHOWWINDOW; 136 si.wShowWindow = SW_SHOW; 137 138 if (!CreateProcessW(NULL, lpPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) 139 { 140 return FALSE; 141 } 142 143 CloseHandle(pi.hThread); 144 145 if (Wait) 146 { 147 EnableWindow(hMainWnd, FALSE); 148 } 149 150 while (Wait) 151 { 152 dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS); 153 if (dwRet == WAIT_OBJECT_0 + 1) 154 { 155 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 156 { 157 TranslateMessage(&msg); 158 DispatchMessageW(&msg); 159 } 160 } 161 else 162 { 163 if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED) 164 break; 165 } 166 } 167 168 CloseHandle(pi.hProcess); 169 170 if (Wait) 171 { 172 EnableWindow(hMainWnd, TRUE); 173 SetForegroundWindow(hMainWnd); 174 SetFocus(hMainWnd); 175 } 176 177 return TRUE; 178 } 179 180 BOOL GetStorageDirectory(ATL::CStringW& Directory) 181 { 182 LPWSTR DirectoryStr = Directory.GetBuffer(MAX_PATH); 183 if (!SHGetSpecialFolderPathW(NULL, DirectoryStr, CSIDL_LOCAL_APPDATA, TRUE)) 184 { 185 Directory.ReleaseBuffer(); 186 return FALSE; 187 } 188 189 PathAppendW(DirectoryStr, L"rapps"); 190 Directory.ReleaseBuffer(); 191 192 return (CreateDirectoryW(Directory.GetString(), NULL) || GetLastError() == ERROR_ALREADY_EXISTS); 193 } 194 195 VOID InitLogs() 196 { 197 if (!SettingsInfo.bLogEnabled) 198 { 199 return; 200 } 201 202 WCHAR szPath[MAX_PATH]; 203 DWORD dwCategoryNum = 1; 204 DWORD dwDisp, dwData; 205 ATL::CRegKey key; 206 207 if (key.Create(HKEY_LOCAL_MACHINE, 208 L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager", 209 REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &dwDisp) != ERROR_SUCCESS) 210 { 211 return; 212 } 213 214 if (!GetModuleFileNameW(NULL, szPath, _countof(szPath))) 215 { 216 return; 217 } 218 219 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | 220 EVENTLOG_INFORMATION_TYPE; 221 222 if ((key.SetStringValue(L"EventMessageFile", 223 szPath, 224 REG_EXPAND_SZ) == ERROR_SUCCESS) 225 && (key.SetStringValue(L"CategoryMessageFile", 226 szPath, 227 REG_EXPAND_SZ) == ERROR_SUCCESS) 228 && (key.SetDWORDValue(L"TypesSupported", 229 dwData) == ERROR_SUCCESS) 230 && (key.SetDWORDValue(L"CategoryCount", 231 dwCategoryNum) == ERROR_SUCCESS)) 232 233 { 234 hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager"); 235 } 236 } 237 238 239 VOID FreeLogs() 240 { 241 if (hLog) 242 { 243 DeregisterEventSource(hLog); 244 } 245 } 246 247 248 BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg) 249 { 250 if (!SettingsInfo.bLogEnabled) 251 { 252 return TRUE; 253 } 254 255 if (!ReportEventW(hLog, wType, 0, dwEventID, 256 NULL, 1, 0, &lpMsg, NULL)) 257 { 258 return FALSE; 259 } 260 261 return TRUE; 262 } 263 264 BOOL GetInstalledVersion_WowUser(ATL::CStringW* szVersionResult, 265 const ATL::CStringW& szRegName, 266 BOOL IsUserKey, 267 REGSAM keyWow) 268 { 269 BOOL bHasSucceded = FALSE; 270 ATL::CRegKey key; 271 ATL::CStringW szVersion; 272 ATL::CStringW szPath = ATL::CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%ls") + szRegName; 273 274 if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, 275 szPath.GetString(), 276 keyWow | KEY_READ) != ERROR_SUCCESS) 277 { 278 return FALSE; 279 } 280 281 if (szVersionResult != NULL) 282 { 283 ULONG dwSize = MAX_PATH * sizeof(WCHAR); 284 285 if (key.QueryStringValue(L"DisplayVersion", 286 szVersion.GetBuffer(MAX_PATH), 287 &dwSize) == ERROR_SUCCESS) 288 { 289 szVersion.ReleaseBuffer(); 290 *szVersionResult = szVersion; 291 bHasSucceded = TRUE; 292 } 293 else 294 { 295 szVersion.ReleaseBuffer(); 296 } 297 } 298 else 299 { 300 bHasSucceded = TRUE; 301 szVersion.ReleaseBuffer(); 302 } 303 304 return bHasSucceded; 305 } 306 307 BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName) 308 { 309 return (!szRegName.IsEmpty() 310 && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY) 311 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY) 312 || GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY) 313 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY))); 314 } 315 316 // CConfigParser 317 318 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName)) 319 { 320 CacheINILocale(); 321 } 322 323 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName) 324 { 325 ATL::CStringW szDir; 326 ATL::CStringW szBuffer; 327 328 GetStorageDirectory(szDir); 329 szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString()); 330 331 return szBuffer; 332 } 333 334 VOID CConfigParser::CacheINILocale() 335 { 336 // TODO: Set default locale if call fails 337 // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale 338 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, 339 m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize); 340 341 m_szLocaleID.ReleaseBuffer(); 342 m_szCachedINISectionLocale = L"Section." + m_szLocaleID; 343 344 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part 345 if (m_szLocaleID.GetLength() >= 2) 346 m_szCachedINISectionLocaleNeutral = L"Section." + m_szLocaleID.Right(2); 347 else 348 m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale; 349 } 350 351 BOOL CConfigParser::GetStringWorker(const ATL::CStringW& KeyName, PCWSTR Suffix, ATL::CStringW& ResultString) 352 { 353 DWORD dwResult; 354 355 LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH); 356 // 1st - find localized strings (e.g. "Section.0c0a") 357 dwResult = GetPrivateProfileStringW((m_szCachedINISectionLocale + Suffix).GetString(), 358 KeyName.GetString(), 359 NULL, 360 ResultStringBuffer, 361 MAX_PATH, 362 szConfigPath.GetString()); 363 364 if (!dwResult) 365 { 366 // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a") 367 dwResult = GetPrivateProfileStringW((m_szCachedINISectionLocaleNeutral + Suffix).GetString(), 368 KeyName.GetString(), 369 NULL, 370 ResultStringBuffer, 371 MAX_PATH, 372 szConfigPath.GetString()); 373 if (!dwResult) 374 { 375 // 3rd - if they weren't present fallback to standard english strings (just "Section") 376 dwResult = GetPrivateProfileStringW((ATL::CStringW(L"Section") + Suffix).GetString(), 377 KeyName.GetString(), 378 NULL, 379 ResultStringBuffer, 380 MAX_PATH, 381 szConfigPath.GetString()); 382 } 383 } 384 385 ResultString.ReleaseBuffer(); 386 return (dwResult != 0 ? TRUE : FALSE); 387 } 388 389 BOOL CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString) 390 { 391 /* First try */ 392 if (GetStringWorker(KeyName, L"." CurrentArchitecture, ResultString)) 393 { 394 return TRUE; 395 } 396 397 #ifndef _M_IX86 398 /* On non-x86 architecture we need the architecture specific URL */ 399 if (KeyName == L"URLDownload") 400 { 401 return FALSE; 402 } 403 #endif 404 405 /* Fall back to default */ 406 return GetStringWorker(KeyName, L"", ResultString); 407 } 408 409 BOOL CConfigParser::GetInt(const ATL::CStringW& KeyName, INT& iResult) 410 { 411 ATL::CStringW Buffer; 412 413 iResult = 0; 414 415 // grab the text version of our entry 416 if (!GetString(KeyName, Buffer)) 417 return FALSE; 418 419 if (Buffer.IsEmpty()) 420 return FALSE; 421 422 // convert it to an actual integer 423 iResult = StrToIntW(Buffer.GetString()); 424 425 // we only care about values > 0 426 return (iResult > 0); 427 } 428 // CConfigParser 429 430 431 BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore) 432 { 433 WCHAR pszPathBuffer[MAX_PATH]; // buffer to store result 434 WCHAR pszPathCopy[MAX_PATH]; 435 436 if (!PathCanonicalizeW(pszPathCopy, pszPath)) 437 { 438 return FALSE; 439 } 440 441 PathRemoveBackslashW(pszPathCopy); 442 443 if (StringCchCopyW(pszPathBuffer, _countof(pszPathBuffer), pszPathCopy) != S_OK) 444 { 445 return FALSE; 446 } 447 448 if (!PathAppendW(pszPathBuffer, pszMore)) 449 { 450 return FALSE; 451 } 452 453 size_t PathLen; 454 if (StringCchLengthW(pszPathCopy, _countof(pszPathCopy), &PathLen) != S_OK) 455 { 456 return FALSE; 457 } 458 int CommonPrefixLen = PathCommonPrefixW(pszPathCopy, pszPathBuffer, NULL); 459 460 if ((unsigned int)CommonPrefixLen != PathLen) 461 { 462 // pszPathBuffer should be a file/folder under pszPath. 463 // but now common prefix len is smaller than length of pszPathCopy 464 // hacking use ".." ? 465 return FALSE; 466 } 467 468 if (StringCchCopyW(pszPath, MAX_PATH, pszPathBuffer) != S_OK) 469 { 470 return FALSE; 471 } 472 473 return TRUE; 474 } 475 476 477 478 BOOL IsSystem64Bit() 479 { 480 if (bIsSys64ResultCached) 481 { 482 // just return cached result 483 return bIsSys64Result; 484 } 485 486 SYSTEM_INFO si; 487 typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO); 488 LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo"); 489 if (pGetNativeSystemInfo) 490 { 491 pGetNativeSystemInfo(&si); 492 if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) 493 { 494 bIsSys64Result = TRUE; 495 } 496 } 497 else 498 { 499 bIsSys64Result = FALSE; 500 } 501 502 bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result 503 return bIsSys64Result; 504 } 505 506 INT GetSystemColorDepth() 507 { 508 DEVMODEW pDevMode; 509 INT ColorDepth; 510 511 pDevMode.dmSize = sizeof(pDevMode); 512 pDevMode.dmDriverExtra = 0; 513 514 if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode)) 515 { 516 /* TODO: Error message */ 517 return ILC_COLOR; 518 } 519 520 switch (pDevMode.dmBitsPerPel) 521 { 522 case 32: ColorDepth = ILC_COLOR32; break; 523 case 24: ColorDepth = ILC_COLOR24; break; 524 case 16: ColorDepth = ILC_COLOR16; break; 525 case 8: ColorDepth = ILC_COLOR8; break; 526 case 4: ColorDepth = ILC_COLOR4; break; 527 default: ColorDepth = ILC_COLOR; break; 528 } 529 530 return ColorDepth; 531 } 532 533 void UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime) 534 { 535 // Note that LONGLONG is a 64-bit value 536 LONGLONG ll; 537 538 ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000; 539 pFileTime->dwLowDateTime = (DWORD)ll; 540 pFileTime->dwHighDateTime = ll >> 32; 541 } 542 543 BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) 544 { 545 if (!*szNeedle) 546 return TRUE; 547 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ 548 return StrStrIW(szHaystack, szNeedle) != NULL; 549 } 550