xref: /reactos/base/applications/rapps/misc.cpp (revision f59c58d8)
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 static BOOL bIsSys64ResultCached = FALSE;
16 static BOOL bIsSys64Result = FALSE;
17 
18 VOID CopyTextToClipboard(LPCWSTR lpszText)
19 {
20     if (!OpenClipboard(NULL))
21     {
22         return;
23     }
24 
25     HRESULT hr;
26     HGLOBAL ClipBuffer;
27     LPWSTR Buffer;
28     DWORD cchBuffer;
29 
30     EmptyClipboard();
31     cchBuffer = wcslen(lpszText) + 1;
32     ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR));
33 
34     Buffer = (PWCHAR) GlobalLock(ClipBuffer);
35     hr = StringCchCopyW(Buffer, cchBuffer, lpszText);
36     GlobalUnlock(ClipBuffer);
37 
38     if (SUCCEEDED(hr))
39         SetClipboardData(CF_UNICODETEXT, ClipBuffer);
40 
41     CloseClipboard();
42 }
43 
44 VOID ShowPopupMenuEx(HWND hwnd, HWND hwndOwner, UINT MenuID, UINT DefaultItem)
45 {
46     HMENU hMenu = NULL;
47     HMENU hPopupMenu;
48     MENUITEMINFO ItemInfo;
49     POINT pt;
50 
51     if (MenuID)
52     {
53         hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID));
54         hPopupMenu = GetSubMenu(hMenu, 0);
55     }
56     else
57     {
58         hPopupMenu = GetMenu(hwnd);
59     }
60 
61     ZeroMemory(&ItemInfo, sizeof(ItemInfo));
62     ItemInfo.cbSize = sizeof(ItemInfo);
63     ItemInfo.fMask = MIIM_STATE;
64 
65     GetMenuItemInfoW(hPopupMenu, DefaultItem, FALSE, &ItemInfo);
66 
67     if (!(ItemInfo.fState & MFS_GRAYED))
68     {
69         SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE);
70     }
71 
72     GetCursorPos(&pt);
73 
74     SetForegroundWindow(hwnd);
75     TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hwndOwner, NULL);
76 
77     if (hMenu)
78     {
79         DestroyMenu(hMenu);
80     }
81 }
82 
83 BOOL StartProcess(const ATL::CStringW& Path, BOOL Wait)
84 {
85     PROCESS_INFORMATION pi;
86     STARTUPINFOW si;
87     DWORD dwRet;
88     MSG msg;
89 
90     ZeroMemory(&si, sizeof(si));
91     si.cb = sizeof(si);
92     si.dwFlags = STARTF_USESHOWWINDOW;
93     si.wShowWindow = SW_SHOW;
94 
95     // The Unicode version of CreateProcess can modify the contents of this string.
96     CStringW Tmp = Path;
97     BOOL fSuccess = CreateProcessW(NULL, Tmp.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
98     Tmp.ReleaseBuffer();
99     if (!fSuccess)
100     {
101         return FALSE;
102     }
103 
104     CloseHandle(pi.hThread);
105 
106     if (Wait)
107     {
108         EnableWindow(hMainWnd, FALSE);
109     }
110 
111     while (Wait)
112     {
113         dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
114         if (dwRet == WAIT_OBJECT_0 + 1)
115         {
116             while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
117             {
118                 TranslateMessage(&msg);
119                 DispatchMessageW(&msg);
120             }
121         }
122         else
123         {
124             if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED)
125                 break;
126         }
127     }
128 
129     CloseHandle(pi.hProcess);
130 
131     if (Wait)
132     {
133         EnableWindow(hMainWnd, TRUE);
134         SetForegroundWindow(hMainWnd);
135         SetFocus(hMainWnd);
136     }
137 
138     return TRUE;
139 }
140 
141 BOOL GetStorageDirectory(ATL::CStringW& Directory)
142 {
143     static CStringW CachedDirectory;
144     static BOOL CachedDirectoryInitialized = FALSE;
145 
146     if (!CachedDirectoryInitialized)
147     {
148         LPWSTR DirectoryStr = CachedDirectory.GetBuffer(MAX_PATH);
149         BOOL bHasPath = SHGetSpecialFolderPathW(NULL, DirectoryStr, CSIDL_LOCAL_APPDATA, TRUE);
150         if (bHasPath)
151         {
152             PathAppendW(DirectoryStr, L"rapps");
153         }
154         CachedDirectory.ReleaseBuffer();
155 
156         if (bHasPath)
157         {
158             if (!CreateDirectoryW(CachedDirectory, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
159             {
160                 CachedDirectory.Empty();
161             }
162         }
163         else
164         {
165             CachedDirectory.Empty();
166         }
167 
168         CachedDirectoryInitialized = TRUE;
169     }
170 
171     Directory = CachedDirectory;
172     return !Directory.IsEmpty();
173 }
174 
175 VOID InitLogs()
176 {
177     if (!SettingsInfo.bLogEnabled)
178     {
179         return;
180     }
181 
182     WCHAR szPath[MAX_PATH];
183     DWORD dwCategoryNum = 1;
184     DWORD dwDisp, dwData;
185     ATL::CRegKey key;
186 
187     if (key.Create(HKEY_LOCAL_MACHINE,
188                    L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager",
189                    REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &dwDisp) != ERROR_SUCCESS)
190     {
191         return;
192     }
193 
194     if (!GetModuleFileNameW(NULL, szPath, _countof(szPath)))
195     {
196         return;
197     }
198 
199     dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
200              EVENTLOG_INFORMATION_TYPE;
201 
202     if ((key.SetStringValue(L"EventMessageFile",
203                             szPath,
204                             REG_EXPAND_SZ) == ERROR_SUCCESS)
205         && (key.SetStringValue(L"CategoryMessageFile",
206                                szPath,
207                                REG_EXPAND_SZ) == ERROR_SUCCESS)
208         && (key.SetDWORDValue(L"TypesSupported",
209                               dwData) == ERROR_SUCCESS)
210         && (key.SetDWORDValue(L"CategoryCount",
211                               dwCategoryNum) == ERROR_SUCCESS))
212 
213     {
214         hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager");
215     }
216 }
217 
218 VOID FreeLogs()
219 {
220     if (hLog)
221     {
222         DeregisterEventSource(hLog);
223     }
224 }
225 
226 BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg)
227 {
228     if (!SettingsInfo.bLogEnabled)
229     {
230         return TRUE;
231     }
232 
233     if (!ReportEventW(hLog, wType, 0, dwEventID,
234                       NULL, 1, 0, &lpMsg, NULL))
235     {
236         return FALSE;
237     }
238 
239     return TRUE;
240 }
241 
242 BOOL GetInstalledVersion_WowUser(ATL::CStringW* szVersionResult,
243                                  const ATL::CStringW& szRegName,
244                                  BOOL IsUserKey,
245                                  REGSAM keyWow)
246 {
247     BOOL bHasSucceded = FALSE;
248     ATL::CRegKey key;
249     ATL::CStringW szVersion;
250     ATL::CStringW szPath = ATL::CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") + szRegName;
251 
252     if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
253                  szPath.GetString(),
254                  keyWow | KEY_READ) != ERROR_SUCCESS)
255     {
256         return FALSE;
257     }
258 
259     if (szVersionResult != NULL)
260     {
261         ULONG dwSize = MAX_PATH * sizeof(WCHAR);
262 
263         if (key.QueryStringValue(L"DisplayVersion",
264                                  szVersion.GetBuffer(MAX_PATH),
265                                  &dwSize) == ERROR_SUCCESS)
266         {
267             szVersion.ReleaseBuffer();
268             *szVersionResult = szVersion;
269             bHasSucceded = TRUE;
270         }
271         else
272         {
273             szVersion.ReleaseBuffer();
274         }
275     }
276     else
277     {
278         bHasSucceded = TRUE;
279         szVersion.ReleaseBuffer();
280     }
281 
282     return bHasSucceded;
283 }
284 
285 BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName)
286 {
287     return (!szRegName.IsEmpty()
288             && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY)
289                 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY)
290                 || GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY)
291                 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY)));
292 }
293 
294 BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore)
295 {
296     WCHAR pszPathBuffer[MAX_PATH]; // buffer to store result
297     WCHAR pszPathCopy[MAX_PATH];
298 
299     if (!PathCanonicalizeW(pszPathCopy, pszPath))
300     {
301         return FALSE;
302     }
303 
304     PathRemoveBackslashW(pszPathCopy);
305 
306     if (StringCchCopyW(pszPathBuffer, _countof(pszPathBuffer), pszPathCopy) != S_OK)
307     {
308         return FALSE;
309     }
310 
311     if (!PathAppendW(pszPathBuffer, pszMore))
312     {
313         return FALSE;
314     }
315 
316     size_t PathLen;
317     if (StringCchLengthW(pszPathCopy, _countof(pszPathCopy), &PathLen) != S_OK)
318     {
319         return FALSE;
320     }
321     int CommonPrefixLen = PathCommonPrefixW(pszPathCopy, pszPathBuffer, NULL);
322 
323     if ((unsigned int)CommonPrefixLen != PathLen)
324     {
325         // pszPathBuffer should be a file/folder under pszPath.
326         // but now common prefix len is smaller than length of pszPathCopy
327         // hacking use ".." ?
328         return FALSE;
329     }
330 
331     if (StringCchCopyW(pszPath, MAX_PATH, pszPathBuffer) != S_OK)
332     {
333         return FALSE;
334     }
335 
336     return TRUE;
337 }
338 
339 BOOL IsSystem64Bit()
340 {
341     if (bIsSys64ResultCached)
342     {
343         // just return cached result
344         return bIsSys64Result;
345     }
346 
347     SYSTEM_INFO si;
348     typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO);
349     LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo");
350     if (pGetNativeSystemInfo)
351     {
352         pGetNativeSystemInfo(&si);
353         if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
354         {
355             bIsSys64Result = TRUE;
356         }
357     }
358     else
359     {
360         bIsSys64Result = FALSE;
361     }
362 
363     bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result
364     return bIsSys64Result;
365 }
366 
367 INT GetSystemColorDepth()
368 {
369     DEVMODEW pDevMode;
370     INT ColorDepth;
371 
372     pDevMode.dmSize = sizeof(pDevMode);
373     pDevMode.dmDriverExtra = 0;
374 
375     if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
376     {
377         /* TODO: Error message */
378         return ILC_COLOR;
379     }
380 
381     switch (pDevMode.dmBitsPerPel)
382     {
383     case 32: ColorDepth = ILC_COLOR32; break;
384     case 24: ColorDepth = ILC_COLOR24; break;
385     case 16: ColorDepth = ILC_COLOR16; break;
386     case  8: ColorDepth = ILC_COLOR8;  break;
387     case  4: ColorDepth = ILC_COLOR4;  break;
388     default: ColorDepth = ILC_COLOR;   break;
389     }
390 
391     return ColorDepth;
392 }
393 
394 void UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime)
395 {
396     // Note that LONGLONG is a 64-bit value
397     LONGLONG ll;
398 
399     ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000;
400     pFileTime->dwLowDateTime = (DWORD)ll;
401     pFileTime->dwHighDateTime = ll >> 32;
402 }
403 
404 BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
405 {
406     if (!*szNeedle)
407         return TRUE;
408     /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
409     return StrStrIW(szHaystack, szNeedle) != NULL;
410 }
411