xref: /reactos/base/applications/rapps/misc.cpp (revision 9393fc32)
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 
238 
239 VOID FreeLogs()
240 {
241     if (hLog)
242     {
243         DeregisterEventSource(hLog);
244     }
245 }
246 
247 
248 BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg)
249 {
250     if (!SettingsInfo.bLogEnabled)
251     {
252         return TRUE;
253     }
254 
255     if (!ReportEventW(hLog, wType, 0, dwEventID,
256                       NULL, 1, 0, &lpMsg, NULL))
257     {
258         return FALSE;
259     }
260 
261     return TRUE;
262 }
263 
264 BOOL GetInstalledVersion_WowUser(ATL::CStringW* szVersionResult,
265                                  const ATL::CStringW& szRegName,
266                                  BOOL IsUserKey,
267                                  REGSAM keyWow)
268 {
269     BOOL bHasSucceded = FALSE;
270     ATL::CRegKey key;
271     ATL::CStringW szVersion;
272     ATL::CStringW szPath = ATL::CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%ls") + szRegName;
273 
274     if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
275                  szPath.GetString(),
276                  keyWow | KEY_READ) != 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",
286                                  szVersion.GetBuffer(MAX_PATH),
287                                  &dwSize) == ERROR_SUCCESS)
288         {
289             szVersion.ReleaseBuffer();
290             *szVersionResult = szVersion;
291             bHasSucceded = TRUE;
292         }
293         else
294         {
295             szVersion.ReleaseBuffer();
296         }
297     }
298     else
299     {
300         bHasSucceded = TRUE;
301         szVersion.ReleaseBuffer();
302     }
303 
304     return bHasSucceded;
305 }
306 
307 BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName)
308 {
309     return (!szRegName.IsEmpty()
310             && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY)
311                 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY)
312                 || GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY)
313                 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY)));
314 }
315 
316 // CConfigParser
317 
318 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName))
319 {
320     CacheINILocale();
321 }
322 
323 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName)
324 {
325     ATL::CStringW szDir;
326     ATL::CStringW szBuffer;
327 
328     GetStorageDirectory(szDir);
329     szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString());
330 
331     return szBuffer;
332 }
333 
334 VOID CConfigParser::CacheINILocale()
335 {
336     // TODO: Set default locale if call fails
337     // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale
338     GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
339                     m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize);
340 
341     m_szLocaleID.ReleaseBuffer();
342     m_szCachedINISectionLocale = L"Section." + m_szLocaleID;
343 
344     // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
345     if (m_szLocaleID.GetLength() >= 2)
346         m_szCachedINISectionLocaleNeutral = L"Section." + m_szLocaleID.Right(2);
347     else
348         m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale;
349 }
350 
351 BOOL CConfigParser::GetStringWorker(const ATL::CStringW& KeyName, PCWSTR Suffix, ATL::CStringW& ResultString)
352 {
353     DWORD dwResult;
354 
355     LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH);
356     // 1st - find localized strings (e.g. "Section.0c0a")
357     dwResult = GetPrivateProfileStringW((m_szCachedINISectionLocale + Suffix).GetString(),
358                                         KeyName.GetString(),
359                                         NULL,
360                                         ResultStringBuffer,
361                                         MAX_PATH,
362                                         szConfigPath.GetString());
363 
364     if (!dwResult)
365     {
366         // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a")
367         dwResult = GetPrivateProfileStringW((m_szCachedINISectionLocaleNeutral + Suffix).GetString(),
368                                             KeyName.GetString(),
369                                             NULL,
370                                             ResultStringBuffer,
371                                             MAX_PATH,
372                                             szConfigPath.GetString());
373         if (!dwResult)
374         {
375             // 3rd - if they weren't present fallback to standard english strings (just "Section")
376             dwResult = GetPrivateProfileStringW((ATL::CStringW(L"Section") + Suffix).GetString(),
377                                                 KeyName.GetString(),
378                                                 NULL,
379                                                 ResultStringBuffer,
380                                                 MAX_PATH,
381                                                 szConfigPath.GetString());
382         }
383     }
384 
385     ResultString.ReleaseBuffer();
386     return (dwResult != 0 ? TRUE : FALSE);
387 }
388 
389 BOOL CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
390 {
391     /* First try */
392     if (GetStringWorker(KeyName, L"." CurrentArchitecture, ResultString))
393     {
394         return TRUE;
395     }
396 
397 #ifndef _M_IX86
398     /* On non-x86 architecture we need the architecture specific URL */
399     if (KeyName == L"URLDownload")
400     {
401         return FALSE;
402     }
403 #endif
404 
405     /* Fall back to default */
406     return GetStringWorker(KeyName, L"", ResultString);
407 }
408 
409 BOOL CConfigParser::GetInt(const ATL::CStringW& KeyName, INT& iResult)
410 {
411     ATL::CStringW Buffer;
412 
413     iResult = 0;
414 
415     // grab the text version of our entry
416     if (!GetString(KeyName, Buffer))
417         return FALSE;
418 
419     if (Buffer.IsEmpty())
420         return FALSE;
421 
422     // convert it to an actual integer
423     iResult = StrToIntW(Buffer.GetString());
424 
425     // we only care about values > 0
426     return (iResult > 0);
427 }
428 // CConfigParser
429 
430 
431 BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore)
432 {
433     WCHAR pszPathBuffer[MAX_PATH]; // buffer to store result
434     WCHAR pszPathCopy[MAX_PATH];
435 
436     if (!PathCanonicalizeW(pszPathCopy, pszPath))
437     {
438         return FALSE;
439     }
440 
441     PathRemoveBackslashW(pszPathCopy);
442 
443     if (StringCchCopyW(pszPathBuffer, _countof(pszPathBuffer), pszPathCopy) != S_OK)
444     {
445         return FALSE;
446     }
447 
448     if (!PathAppendW(pszPathBuffer, pszMore))
449     {
450         return FALSE;
451     }
452 
453     size_t PathLen;
454     if (StringCchLengthW(pszPathCopy, _countof(pszPathCopy), &PathLen) != S_OK)
455     {
456         return FALSE;
457     }
458     int CommonPrefixLen = PathCommonPrefixW(pszPathCopy, pszPathBuffer, NULL);
459 
460     if ((unsigned int)CommonPrefixLen != PathLen)
461     {
462         // pszPathBuffer should be a file/folder under pszPath.
463         // but now common prefix len is smaller than length of pszPathCopy
464         // hacking use ".." ?
465         return FALSE;
466     }
467 
468     if (StringCchCopyW(pszPath, MAX_PATH, pszPathBuffer) != S_OK)
469     {
470         return FALSE;
471     }
472 
473     return TRUE;
474 }
475 
476 
477 
478 BOOL IsSystem64Bit()
479 {
480     if (bIsSys64ResultCached)
481     {
482         // just return cached result
483         return bIsSys64Result;
484     }
485 
486     SYSTEM_INFO si;
487     typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO);
488     LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo");
489     if (pGetNativeSystemInfo)
490     {
491         pGetNativeSystemInfo(&si);
492         if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
493         {
494             bIsSys64Result = TRUE;
495         }
496     }
497     else
498     {
499         bIsSys64Result = FALSE;
500     }
501 
502     bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result
503     return bIsSys64Result;
504 }
505 
506 INT GetSystemColorDepth()
507 {
508     DEVMODEW pDevMode;
509     INT ColorDepth;
510 
511     pDevMode.dmSize = sizeof(pDevMode);
512     pDevMode.dmDriverExtra = 0;
513 
514     if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
515     {
516         /* TODO: Error message */
517         return ILC_COLOR;
518     }
519 
520     switch (pDevMode.dmBitsPerPel)
521     {
522     case 32: ColorDepth = ILC_COLOR32; break;
523     case 24: ColorDepth = ILC_COLOR24; break;
524     case 16: ColorDepth = ILC_COLOR16; break;
525     case  8: ColorDepth = ILC_COLOR8;  break;
526     case  4: ColorDepth = ILC_COLOR4;  break;
527     default: ColorDepth = ILC_COLOR;   break;
528     }
529 
530     return ColorDepth;
531 }
532 
533 void UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime)
534 {
535     // Note that LONGLONG is a 64-bit value
536     LONGLONG ll;
537 
538     ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000;
539     pFileTime->dwLowDateTime = (DWORD)ll;
540     pFileTime->dwHighDateTime = ll >> 32;
541 }
542 
543 BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
544 {
545     if (!*szNeedle)
546         return TRUE;
547     /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
548     return StrStrIW(szHaystack, szNeedle) != NULL;
549 }
550