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 10 #include "rapps.h" 11 #include "misc.h" 12 13 static HANDLE hLog = NULL; 14 15 VOID 16 CopyTextToClipboard(LPCWSTR lpszText) 17 { 18 if (!OpenClipboard(NULL)) 19 { 20 return; 21 } 22 23 HRESULT hr; 24 HGLOBAL ClipBuffer; 25 LPWSTR Buffer; 26 DWORD cchBuffer; 27 28 EmptyClipboard(); 29 cchBuffer = wcslen(lpszText) + 1; 30 ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR)); 31 32 Buffer = (PWCHAR)GlobalLock(ClipBuffer); 33 hr = StringCchCopyW(Buffer, cchBuffer, lpszText); 34 GlobalUnlock(ClipBuffer); 35 36 if (SUCCEEDED(hr)) 37 SetClipboardData(CF_UNICODETEXT, ClipBuffer); 38 39 CloseClipboard(); 40 } 41 42 VOID 43 ShowPopupMenuEx(HWND hwnd, HWND hwndOwner, UINT MenuID, UINT DefaultItem) 44 { 45 HMENU hMenu = NULL; 46 HMENU hPopupMenu; 47 MENUITEMINFO ItemInfo; 48 POINT pt; 49 50 if (MenuID) 51 { 52 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID)); 53 hPopupMenu = GetSubMenu(hMenu, 0); 54 } 55 else 56 { 57 hPopupMenu = GetMenu(hwnd); 58 } 59 60 ZeroMemory(&ItemInfo, sizeof(ItemInfo)); 61 ItemInfo.cbSize = sizeof(ItemInfo); 62 ItemInfo.fMask = MIIM_STATE; 63 64 GetMenuItemInfoW(hPopupMenu, DefaultItem, FALSE, &ItemInfo); 65 66 if (!(ItemInfo.fState & MFS_GRAYED)) 67 { 68 SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE); 69 } 70 71 GetCursorPos(&pt); 72 73 SetForegroundWindow(hwnd); 74 TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hwndOwner, NULL); 75 76 if (hMenu) 77 { 78 DestroyMenu(hMenu); 79 } 80 } 81 82 BOOL 83 StartProcess(const CStringW &Path, BOOL Wait) 84 { 85 PROCESS_INFORMATION pi; 86 STARTUPINFOW si; 87 DWORD dwRet; 88 MSG msg; 89 90 ZeroMemory(&si, sizeof(si)); 91 si.cb = sizeof(si); 92 si.dwFlags = STARTF_USESHOWWINDOW; 93 si.wShowWindow = SW_SHOW; 94 95 // The Unicode version of CreateProcess can modify the contents of this string. 96 CStringW Tmp = Path; 97 BOOL fSuccess = CreateProcessW(NULL, Tmp.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 98 Tmp.ReleaseBuffer(); 99 if (!fSuccess) 100 { 101 return FALSE; 102 } 103 104 CloseHandle(pi.hThread); 105 106 if (Wait) 107 { 108 EnableWindow(hMainWnd, FALSE); 109 } 110 111 while (Wait) 112 { 113 dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS); 114 if (dwRet == WAIT_OBJECT_0 + 1) 115 { 116 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 117 { 118 TranslateMessage(&msg); 119 DispatchMessageW(&msg); 120 } 121 } 122 else 123 { 124 if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED) 125 break; 126 } 127 } 128 129 CloseHandle(pi.hProcess); 130 131 if (Wait) 132 { 133 EnableWindow(hMainWnd, TRUE); 134 SetForegroundWindow(hMainWnd); 135 SetFocus(hMainWnd); 136 } 137 138 return TRUE; 139 } 140 141 BOOL 142 GetStorageDirectory(CStringW &Directory) 143 { 144 static CStringW CachedDirectory; 145 static BOOL CachedDirectoryInitialized = FALSE; 146 147 if (!CachedDirectoryInitialized) 148 { 149 LPWSTR DirectoryStr = CachedDirectory.GetBuffer(MAX_PATH); 150 BOOL bHasPath = SHGetSpecialFolderPathW(NULL, DirectoryStr, CSIDL_LOCAL_APPDATA, TRUE); 151 if (bHasPath) 152 { 153 PathAppendW(DirectoryStr, RAPPS_NAME); 154 } 155 CachedDirectory.ReleaseBuffer(); 156 157 if (bHasPath) 158 { 159 if (!CreateDirectoryW(CachedDirectory, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) 160 { 161 CachedDirectory.Empty(); 162 } 163 } 164 else 165 { 166 CachedDirectory.Empty(); 167 } 168 169 CachedDirectoryInitialized = TRUE; 170 } 171 172 Directory = CachedDirectory; 173 return !Directory.IsEmpty(); 174 } 175 176 VOID 177 InitLogs() 178 { 179 if (!SettingsInfo.bLogEnabled) 180 { 181 return; 182 } 183 184 WCHAR szPath[MAX_PATH]; 185 DWORD dwCategoryNum = 1; 186 DWORD dwDisp, dwData; 187 ATL::CRegKey key; 188 189 if (key.Create( 190 HKEY_LOCAL_MACHINE, 191 L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager", REG_NONE, 192 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &dwDisp) != ERROR_SUCCESS) 193 { 194 return; 195 } 196 197 if (!GetModuleFileNameW(NULL, szPath, _countof(szPath))) 198 { 199 return; 200 } 201 202 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; 203 204 if ((key.SetStringValue(L"EventMessageFile", szPath, REG_EXPAND_SZ) == ERROR_SUCCESS) && 205 (key.SetStringValue(L"CategoryMessageFile", szPath, REG_EXPAND_SZ) == ERROR_SUCCESS) && 206 (key.SetDWORDValue(L"TypesSupported", dwData) == ERROR_SUCCESS) && 207 (key.SetDWORDValue(L"CategoryCount", dwCategoryNum) == ERROR_SUCCESS)) 208 209 { 210 hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager"); 211 } 212 } 213 214 VOID 215 FreeLogs() 216 { 217 if (hLog) 218 { 219 DeregisterEventSource(hLog); 220 } 221 } 222 223 BOOL 224 WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg) 225 { 226 if (!SettingsInfo.bLogEnabled) 227 { 228 return TRUE; 229 } 230 231 if (!ReportEventW(hLog, wType, 0, dwEventID, NULL, 1, 0, &lpMsg, NULL)) 232 { 233 return FALSE; 234 } 235 236 return TRUE; 237 } 238 239 BOOL 240 GetInstalledVersion_WowUser(CStringW *szVersionResult, const CStringW &szRegName, BOOL IsUserKey, REGSAM keyWow) 241 { 242 BOOL bHasSucceded = FALSE; 243 ATL::CRegKey key; 244 CStringW szVersion; 245 CStringW szPath = CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") + szRegName; 246 247 if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szPath.GetString(), keyWow | KEY_READ) != 248 ERROR_SUCCESS) 249 { 250 return FALSE; 251 } 252 253 if (szVersionResult != NULL) 254 { 255 ULONG dwSize = MAX_PATH * sizeof(WCHAR); 256 257 if (key.QueryStringValue(L"DisplayVersion", szVersion.GetBuffer(MAX_PATH), &dwSize) == ERROR_SUCCESS) 258 { 259 szVersion.ReleaseBuffer(); 260 *szVersionResult = szVersion; 261 bHasSucceded = TRUE; 262 } 263 else 264 { 265 szVersion.ReleaseBuffer(); 266 } 267 } 268 else 269 { 270 bHasSucceded = TRUE; 271 } 272 273 return bHasSucceded; 274 } 275 276 BOOL 277 GetInstalledVersion(CStringW *pszVersion, const CStringW &szRegName) 278 { 279 return ( 280 !szRegName.IsEmpty() && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY) || 281 GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY) || 282 GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY) || 283 GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY))); 284 } 285 286 BOOL 287 IsSystem64Bit() 288 { 289 #ifdef _WIN64 290 return TRUE; 291 #else 292 static UINT cache = 0; 293 if (!cache) 294 cache = 1 + (IsOS(OS_WOW6432) != FALSE); 295 return cache - 1; 296 #endif 297 } 298 299 INT 300 GetSystemColorDepth() 301 { 302 DEVMODEW pDevMode; 303 INT ColorDepth; 304 305 pDevMode.dmSize = sizeof(pDevMode); 306 pDevMode.dmDriverExtra = 0; 307 308 if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode)) 309 { 310 /* TODO: Error message */ 311 return ILC_COLOR; 312 } 313 314 switch (pDevMode.dmBitsPerPel) 315 { 316 case 32: 317 ColorDepth = ILC_COLOR32; 318 break; 319 case 24: 320 ColorDepth = ILC_COLOR24; 321 break; 322 case 16: 323 ColorDepth = ILC_COLOR16; 324 break; 325 case 8: 326 ColorDepth = ILC_COLOR8; 327 break; 328 case 4: 329 ColorDepth = ILC_COLOR4; 330 break; 331 default: 332 ColorDepth = ILC_COLOR; 333 break; 334 } 335 336 return ColorDepth; 337 } 338 339 void 340 UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime) 341 { 342 // Note that LONGLONG is a 64-bit value 343 LONGLONG ll; 344 345 ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000; 346 pFileTime->dwLowDateTime = (DWORD)ll; 347 pFileTime->dwHighDateTime = ll >> 32; 348 } 349 350 HRESULT 351 RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam) 352 { 353 CRegKey key; 354 LONG err = key.Open(hKey, Path, KEY_QUERY_VALUE | wowsam); 355 if (err == ERROR_SUCCESS) 356 { 357 WCHAR name[1]; 358 DWORD cchname = _countof(name), cbsize = 0; 359 err = RegEnumValueW(key, 0, name, &cchname, NULL, NULL, NULL, &cbsize); 360 if (err == ERROR_NO_MORE_ITEMS) 361 return S_FALSE; 362 if (err == ERROR_MORE_DATA) 363 err = ERROR_SUCCESS; 364 } 365 return HRESULT_FROM_WIN32(err); 366 } 367 368 LPCWSTR 369 GetRegString(CRegKey &Key, LPCWSTR Name, CStringW &Value) 370 { 371 for (;;) 372 { 373 ULONG cb = 0, cch; 374 ULONG err = Key.QueryValue(Name, NULL, NULL, &cb); 375 if (err) 376 break; 377 cch = cb / sizeof(WCHAR); 378 LPWSTR p = Value.GetBuffer(cch + 1); 379 p[cch] = UNICODE_NULL; 380 err = Key.QueryValue(Name, NULL, (BYTE*)p, &cb); 381 if (err == ERROR_MORE_DATA) 382 continue; 383 if (err) 384 break; 385 Value.ReleaseBuffer(); 386 return Value.GetString(); 387 } 388 return NULL; 389 } 390 391 bool 392 ExpandEnvStrings(CStringW &Str) 393 { 394 CStringW buf; 395 DWORD cch = ExpandEnvironmentStringsW(Str, NULL, 0); 396 if (cch) 397 { 398 if (ExpandEnvironmentStringsW(Str, buf.GetBuffer(cch), cch) == cch) 399 { 400 buf.ReleaseBuffer(cch - 1); 401 Str = buf; 402 return true; 403 } 404 } 405 return false; 406 } 407 408 BOOL 409 SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) 410 { 411 if (!*szNeedle) 412 return TRUE; 413 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ 414 return StrStrIW(szHaystack, szNeedle) != NULL; 415 } 416 417 BOOL 418 DeleteDirectoryTree(LPCWSTR Dir, HWND hwnd) 419 { 420 CStringW from(Dir); 421 UINT cch = from.GetLength(); 422 from.Append(L"00"); 423 LPWSTR p = from.GetBuffer(); 424 p[cch] = p[cch + 1] = L'\0'; // Double null-terminate 425 UINT fof = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; 426 SHFILEOPSTRUCT shfos = { hwnd, FO_DELETE, p, NULL, (FILEOP_FLAGS)fof }; 427 return SHFileOperationW(&shfos); 428 } 429 430 UINT 431 CreateDirectoryTree(LPCWSTR Dir) 432 { 433 UINT err = SHCreateDirectory(NULL, Dir); 434 return err == ERROR_ALREADY_EXISTS ? 0 : err; 435 } 436 437 CStringW 438 SplitFileAndDirectory(LPCWSTR FullPath, CStringW *pDir) 439 { 440 CPathW dir = FullPath; 441 //int win = dir.ReverseFind(L'\\'), nix = dir.ReverseFind(L'/'), sep = max(win, nix); 442 int sep = dir.FindFileName(); 443 CStringW file = dir.m_strPath.Mid(sep); 444 if (pDir) 445 *pDir = sep == -1 ? L"" : dir.m_strPath.Left(sep - 1); 446 return file; 447 } 448 449 HRESULT 450 GetSpecialPath(UINT csidl, CStringW &Path, HWND hwnd) 451 { 452 if (!SHGetSpecialFolderPathW(hwnd, Path.GetBuffer(MAX_PATH), csidl, TRUE)) 453 return E_FAIL; 454 Path.ReleaseBuffer(); 455 return S_OK; 456 } 457 458 HRESULT 459 GetKnownPath(REFKNOWNFOLDERID kfid, CStringW &Path, DWORD Flags) 460 { 461 PWSTR p; 462 FARPROC f = GetProcAddress(LoadLibraryW(L"SHELL32"), "SHGetKnownFolderPath"); 463 if (!f) 464 return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); 465 HRESULT hr = ((HRESULT(WINAPI*)(REFKNOWNFOLDERID,UINT,HANDLE,PWSTR*))f)(kfid, Flags, NULL, &p); 466 if (FAILED(hr)) 467 return hr; 468 Path = p; 469 CoTaskMemFree(p); 470 return hr; 471 } 472 473 HRESULT 474 GetProgramFilesPath(CStringW &Path, BOOL PerUser, HWND hwnd) 475 { 476 if (!PerUser) 477 return GetSpecialPath(CSIDL_PROGRAM_FILES, Path, hwnd); 478 479 HRESULT hr = GetKnownPath(FOLDERID_UserProgramFiles, Path); 480 if (FAILED(hr)) 481 { 482 hr = GetSpecialPath(CSIDL_LOCAL_APPDATA, Path, hwnd); 483 // Use the correct path on NT6 (on NT5 the path becomes a bit long) 484 if (SUCCEEDED(hr) && LOBYTE(GetVersion()) >= 6) 485 { 486 Path = BuildPath(Path, L"Programs"); // Should not be localized 487 } 488 } 489 return hr; 490 } 491