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 key.Close(); 238 } 239 240 241 VOID FreeLogs() 242 { 243 if (hLog) 244 { 245 DeregisterEventSource(hLog); 246 } 247 } 248 249 250 BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg) 251 { 252 if (!SettingsInfo.bLogEnabled) 253 { 254 return TRUE; 255 } 256 257 if (!ReportEventW(hLog, wType, 0, dwEventID, 258 NULL, 1, 0, &lpMsg, NULL)) 259 { 260 return FALSE; 261 } 262 263 return TRUE; 264 } 265 266 BOOL GetInstalledVersion_WowUser(ATL::CStringW* szVersionResult, 267 const ATL::CStringW& szRegName, 268 BOOL IsUserKey, 269 REGSAM keyWow) 270 { 271 BOOL bHasSucceded = FALSE; 272 ATL::CRegKey key; 273 ATL::CStringW szVersion; 274 ATL::CStringW szPath = ATL::CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%ls") + szRegName; 275 276 if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, 277 szPath.GetString(), 278 keyWow | KEY_READ) != ERROR_SUCCESS) 279 { 280 return FALSE; 281 } 282 283 if (szVersionResult != NULL) 284 { 285 ULONG dwSize = MAX_PATH * sizeof(WCHAR); 286 287 if (key.QueryStringValue(L"DisplayVersion", 288 szVersion.GetBuffer(MAX_PATH), 289 &dwSize) == ERROR_SUCCESS) 290 { 291 szVersion.ReleaseBuffer(); 292 *szVersionResult = szVersion; 293 bHasSucceded = TRUE; 294 } 295 else 296 { 297 szVersion.ReleaseBuffer(); 298 } 299 } 300 else 301 { 302 bHasSucceded = TRUE; 303 szVersion.ReleaseBuffer(); 304 } 305 key.Close(); 306 307 return bHasSucceded; 308 } 309 310 BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName) 311 { 312 return (!szRegName.IsEmpty() 313 && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY) 314 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY) 315 || GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY) 316 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY))); 317 } 318 319 // CConfigParser 320 321 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName)) 322 { 323 CacheINILocale(); 324 } 325 326 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName) 327 { 328 ATL::CStringW szDir; 329 ATL::CStringW szBuffer; 330 331 GetStorageDirectory(szDir); 332 szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString()); 333 334 return szBuffer; 335 } 336 337 VOID CConfigParser::CacheINILocale() 338 { 339 // TODO: Set default locale if call fails 340 // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale 341 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, 342 m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize); 343 344 m_szLocaleID.ReleaseBuffer(); 345 m_szCachedINISectionLocale = L"Section." + m_szLocaleID; 346 347 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part 348 if (m_szLocaleID.GetLength() >= 2) 349 m_szCachedINISectionLocaleNeutral = L"Section." + m_szLocaleID.Right(2); 350 else 351 m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale; 352 } 353 354 BOOL CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString) 355 { 356 DWORD dwResult; 357 358 LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH); 359 // 1st - find localized strings (e.g. "Section.0c0a") 360 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocale.GetString(), 361 KeyName.GetString(), 362 NULL, 363 ResultStringBuffer, 364 MAX_PATH, 365 szConfigPath.GetString()); 366 367 if (!dwResult) 368 { 369 // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a") 370 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocaleNeutral.GetString(), 371 KeyName.GetString(), 372 NULL, 373 ResultStringBuffer, 374 MAX_PATH, 375 szConfigPath.GetString()); 376 if (!dwResult) 377 { 378 // 3rd - if they weren't present fallback to standard english strings (just "Section") 379 dwResult = GetPrivateProfileStringW(L"Section", 380 KeyName.GetString(), 381 NULL, 382 ResultStringBuffer, 383 MAX_PATH, 384 szConfigPath.GetString()); 385 } 386 } 387 388 ResultString.ReleaseBuffer(); 389 return (dwResult != 0 ? TRUE : FALSE); 390 } 391 392 BOOL CConfigParser::GetInt(const ATL::CStringW& KeyName, INT& iResult) 393 { 394 ATL::CStringW Buffer; 395 396 iResult = 0; 397 398 // grab the text version of our entry 399 if (!GetString(KeyName, Buffer)) 400 return FALSE; 401 402 if (Buffer.IsEmpty()) 403 return FALSE; 404 405 // convert it to an actual integer 406 iResult = StrToIntW(Buffer.GetString()); 407 408 // we only care about values > 0 409 return (iResult > 0); 410 } 411 // CConfigParser 412 413 414 BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore) 415 { 416 WCHAR pszPathBuffer[MAX_PATH]; // buffer to store result 417 WCHAR pszPathCopy[MAX_PATH]; 418 419 if (!PathCanonicalizeW(pszPathCopy, pszPath)) 420 { 421 return FALSE; 422 } 423 424 PathRemoveBackslashW(pszPathCopy); 425 426 if (StringCchCopyW(pszPathBuffer, _countof(pszPathBuffer), pszPathCopy) != S_OK) 427 { 428 return FALSE; 429 } 430 431 if (!PathAppendW(pszPathBuffer, pszMore)) 432 { 433 return FALSE; 434 } 435 436 size_t PathLen; 437 if (StringCchLengthW(pszPathCopy, _countof(pszPathCopy), &PathLen) != S_OK) 438 { 439 return FALSE; 440 } 441 int CommonPrefixLen = PathCommonPrefixW(pszPathCopy, pszPathBuffer, NULL); 442 443 if ((unsigned int)CommonPrefixLen != PathLen) 444 { 445 // pszPathBuffer should be a file/folder under pszPath. 446 // but now common prefix len is smaller than length of pszPathCopy 447 // hacking use ".." ? 448 return FALSE; 449 } 450 451 if (StringCchCopyW(pszPath, MAX_PATH, pszPathBuffer) != S_OK) 452 { 453 return FALSE; 454 } 455 456 return TRUE; 457 } 458 459 460 461 BOOL IsSystem64Bit() 462 { 463 if (bIsSys64ResultCached) 464 { 465 // just return cached result 466 return bIsSys64Result; 467 } 468 469 SYSTEM_INFO si; 470 typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO); 471 LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo"); 472 if (pGetNativeSystemInfo) 473 { 474 pGetNativeSystemInfo(&si); 475 if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) 476 { 477 bIsSys64Result = TRUE; 478 } 479 } 480 else 481 { 482 bIsSys64Result = FALSE; 483 } 484 485 bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result 486 return bIsSys64Result; 487 } 488 489 INT GetSystemColorDepth() 490 { 491 DEVMODEW pDevMode; 492 INT ColorDepth; 493 494 pDevMode.dmSize = sizeof(pDevMode); 495 pDevMode.dmDriverExtra = 0; 496 497 if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode)) 498 { 499 /* TODO: Error message */ 500 return ILC_COLOR; 501 } 502 503 switch (pDevMode.dmBitsPerPel) 504 { 505 case 32: ColorDepth = ILC_COLOR32; break; 506 case 24: ColorDepth = ILC_COLOR24; break; 507 case 16: ColorDepth = ILC_COLOR16; break; 508 case 8: ColorDepth = ILC_COLOR8; break; 509 case 4: ColorDepth = ILC_COLOR4; break; 510 default: ColorDepth = ILC_COLOR; break; 511 } 512 513 return ColorDepth; 514 } 515 516 void UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime) 517 { 518 // Note that LONGLONG is a 64-bit value 519 LONGLONG ll; 520 521 ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000; 522 pFileTime->dwLowDateTime = (DWORD)ll; 523 pFileTime->dwHighDateTime = ll >> 32; 524 } 525 526 BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) 527 { 528 if (!*szNeedle) 529 return TRUE; 530 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ 531 return StrStrIW(szHaystack, szNeedle) != NULL; 532 } 533