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