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
CopyTextToClipboard(LPCWSTR lpszText)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
NothingDlgProc(HWND hDlg,UINT uMsg,WPARAM,LPARAM)43 NothingDlgProc(HWND hDlg, UINT uMsg, WPARAM, LPARAM)
44 {
45 return uMsg == WM_CLOSE ? DestroyWindow(hDlg) : FALSE;
46 }
47
48 VOID
EmulateDialogReposition(HWND hwnd)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
ShowPopupMenuEx(HWND hwnd,HWND hwndOwner,UINT MenuID,UINT DefaultItem,POINT * Point)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
StartProcess(const CStringW & Path,BOOL Wait)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
GetStorageDirectory(CStringW & Directory)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
InitLogs()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
FreeLogs()243 FreeLogs()
244 {
245 if (hLog)
246 {
247 DeregisterEventSource(hLog);
248 }
249 }
250
251 BOOL
WriteLogMessage(WORD wType,DWORD dwEventID,LPCWSTR lpMsg)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
GetInstalledVersion_WowUser(CStringW * szVersionResult,const CStringW & szRegName,BOOL IsUserKey,REGSAM keyWow)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
GetInstalledVersion(CStringW * pszVersion,const CStringW & szRegName)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
IsSystem64Bit()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
GetSystemColorDepth()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
UnixTimeToFileTime(DWORD dwUnixTime,LPFILETIME pFileTime)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
RegKeyHasValues(HKEY hKey,LPCWSTR Path,REGSAM wowsam)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
GetRegString(CRegKey & Key,LPCWSTR Name,CStringW & Value)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
ExpandEnvStrings(CStringW & Str)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
SearchPatternMatch(LPCWSTR szHaystack,LPCWSTR szNeedle)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
DeleteDirectoryTree(LPCWSTR Dir,HWND hwnd)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
CreateDirectoryTree(LPCWSTR Dir)459 CreateDirectoryTree(LPCWSTR Dir)
460 {
461 UINT err = SHCreateDirectory(NULL, Dir);
462 return err == ERROR_ALREADY_EXISTS ? 0 : err;
463 }
464
465 CStringW
SplitFileAndDirectory(LPCWSTR FullPath,CStringW * pDir)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
GetSpecialPath(UINT csidl,CStringW & Path,HWND hwnd)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
GetKnownPath(REFKNOWNFOLDERID kfid,CStringW & Path,DWORD Flags)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
GetProgramFilesPath(CStringW & Path,BOOL PerUser,HWND hwnd)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