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