[CRT] Remove useless #undef abort from process.h
[reactos.git] / base / applications / rapps / misc.cpp
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::GetStringWorker(const ATL::CStringW& KeyName, PCWSTR Suffix, 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 + Suffix).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 + Suffix).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((ATL::CStringW(L"Section") + Suffix).GetString(),
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::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
393 {
394 /* First try */
395 if (GetStringWorker(KeyName, L"." CurrentArchitecture, ResultString))
396 {
397 return TRUE;
398 }
399
400 #ifndef _M_IX86
401 /* On non-x86 architecture we need the architecture specific URL */
402 if (KeyName == L"URLDownload")
403 {
404 return FALSE;
405 }
406 #endif
407
408 /* Fall back to default */
409 return GetStringWorker(KeyName, L"", ResultString);
410 }
411
412 BOOL CConfigParser::GetInt(const ATL::CStringW& KeyName, INT& iResult)
413 {
414 ATL::CStringW Buffer;
415
416 iResult = 0;
417
418 // grab the text version of our entry
419 if (!GetString(KeyName, Buffer))
420 return FALSE;
421
422 if (Buffer.IsEmpty())
423 return FALSE;
424
425 // convert it to an actual integer
426 iResult = StrToIntW(Buffer.GetString());
427
428 // we only care about values > 0
429 return (iResult > 0);
430 }
431 // CConfigParser
432
433
434 BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore)
435 {
436 WCHAR pszPathBuffer[MAX_PATH]; // buffer to store result
437 WCHAR pszPathCopy[MAX_PATH];
438
439 if (!PathCanonicalizeW(pszPathCopy, pszPath))
440 {
441 return FALSE;
442 }
443
444 PathRemoveBackslashW(pszPathCopy);
445
446 if (StringCchCopyW(pszPathBuffer, _countof(pszPathBuffer), pszPathCopy) != S_OK)
447 {
448 return FALSE;
449 }
450
451 if (!PathAppendW(pszPathBuffer, pszMore))
452 {
453 return FALSE;
454 }
455
456 size_t PathLen;
457 if (StringCchLengthW(pszPathCopy, _countof(pszPathCopy), &PathLen) != S_OK)
458 {
459 return FALSE;
460 }
461 int CommonPrefixLen = PathCommonPrefixW(pszPathCopy, pszPathBuffer, NULL);
462
463 if ((unsigned int)CommonPrefixLen != PathLen)
464 {
465 // pszPathBuffer should be a file/folder under pszPath.
466 // but now common prefix len is smaller than length of pszPathCopy
467 // hacking use ".." ?
468 return FALSE;
469 }
470
471 if (StringCchCopyW(pszPath, MAX_PATH, pszPathBuffer) != S_OK)
472 {
473 return FALSE;
474 }
475
476 return TRUE;
477 }
478
479
480
481 BOOL IsSystem64Bit()
482 {
483 if (bIsSys64ResultCached)
484 {
485 // just return cached result
486 return bIsSys64Result;
487 }
488
489 SYSTEM_INFO si;
490 typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO);
491 LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo");
492 if (pGetNativeSystemInfo)
493 {
494 pGetNativeSystemInfo(&si);
495 if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
496 {
497 bIsSys64Result = TRUE;
498 }
499 }
500 else
501 {
502 bIsSys64Result = FALSE;
503 }
504
505 bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result
506 return bIsSys64Result;
507 }
508
509 INT GetSystemColorDepth()
510 {
511 DEVMODEW pDevMode;
512 INT ColorDepth;
513
514 pDevMode.dmSize = sizeof(pDevMode);
515 pDevMode.dmDriverExtra = 0;
516
517 if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
518 {
519 /* TODO: Error message */
520 return ILC_COLOR;
521 }
522
523 switch (pDevMode.dmBitsPerPel)
524 {
525 case 32: ColorDepth = ILC_COLOR32; break;
526 case 24: ColorDepth = ILC_COLOR24; break;
527 case 16: ColorDepth = ILC_COLOR16; break;
528 case 8: ColorDepth = ILC_COLOR8; break;
529 case 4: ColorDepth = ILC_COLOR4; break;
530 default: ColorDepth = ILC_COLOR; break;
531 }
532
533 return ColorDepth;
534 }
535
536 void UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime)
537 {
538 // Note that LONGLONG is a 64-bit value
539 LONGLONG ll;
540
541 ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000;
542 pFileTime->dwLowDateTime = (DWORD)ll;
543 pFileTime->dwHighDateTime = ll >> 32;
544 }
545
546 BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
547 {
548 if (!*szNeedle)
549 return TRUE;
550 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
551 return StrStrIW(szHaystack, szNeedle) != NULL;
552 }