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