xref: /reactos/base/applications/rapps/misc.cpp (revision ac0bcf4a)
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
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 VOID
43 ShowPopupMenuEx(HWND hwnd, HWND hwndOwner, UINT MenuID, UINT DefaultItem)
44 {
45     HMENU hMenu = NULL;
46     HMENU hPopupMenu;
47     MENUITEMINFO ItemInfo;
48     POINT pt;
49 
50     if (MenuID)
51     {
52         hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID));
53         hPopupMenu = GetSubMenu(hMenu, 0);
54     }
55     else
56     {
57         hPopupMenu = GetMenu(hwnd);
58     }
59 
60     ZeroMemory(&ItemInfo, sizeof(ItemInfo));
61     ItemInfo.cbSize = sizeof(ItemInfo);
62     ItemInfo.fMask = MIIM_STATE;
63 
64     GetMenuItemInfoW(hPopupMenu, DefaultItem, FALSE, &ItemInfo);
65 
66     if (!(ItemInfo.fState & MFS_GRAYED))
67     {
68         SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE);
69     }
70 
71     GetCursorPos(&pt);
72 
73     SetForegroundWindow(hwnd);
74     TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hwndOwner, NULL);
75 
76     if (hMenu)
77     {
78         DestroyMenu(hMenu);
79     }
80 }
81 
82 BOOL
83 StartProcess(const 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
142 GetStorageDirectory(CStringW &Directory)
143 {
144     static CStringW CachedDirectory;
145     static BOOL CachedDirectoryInitialized = FALSE;
146 
147     if (!CachedDirectoryInitialized)
148     {
149         LPWSTR DirectoryStr = CachedDirectory.GetBuffer(MAX_PATH);
150         BOOL bHasPath = SHGetSpecialFolderPathW(NULL, DirectoryStr, CSIDL_LOCAL_APPDATA, TRUE);
151         if (bHasPath)
152         {
153             PathAppendW(DirectoryStr, RAPPS_NAME);
154         }
155         CachedDirectory.ReleaseBuffer();
156 
157         if (bHasPath)
158         {
159             if (!CreateDirectoryW(CachedDirectory, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
160             {
161                 CachedDirectory.Empty();
162             }
163         }
164         else
165         {
166             CachedDirectory.Empty();
167         }
168 
169         CachedDirectoryInitialized = TRUE;
170     }
171 
172     Directory = CachedDirectory;
173     return !Directory.IsEmpty();
174 }
175 
176 VOID
177 InitLogs()
178 {
179     if (!SettingsInfo.bLogEnabled)
180     {
181         return;
182     }
183 
184     WCHAR szPath[MAX_PATH];
185     DWORD dwCategoryNum = 1;
186     DWORD dwDisp, dwData;
187     ATL::CRegKey key;
188 
189     if (key.Create(
190             HKEY_LOCAL_MACHINE,
191             L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager", REG_NONE,
192             REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &dwDisp) != ERROR_SUCCESS)
193     {
194         return;
195     }
196 
197     if (!GetModuleFileNameW(NULL, szPath, _countof(szPath)))
198     {
199         return;
200     }
201 
202     dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
203 
204     if ((key.SetStringValue(L"EventMessageFile", szPath, REG_EXPAND_SZ) == ERROR_SUCCESS) &&
205         (key.SetStringValue(L"CategoryMessageFile", szPath, REG_EXPAND_SZ) == ERROR_SUCCESS) &&
206         (key.SetDWORDValue(L"TypesSupported", dwData) == ERROR_SUCCESS) &&
207         (key.SetDWORDValue(L"CategoryCount", dwCategoryNum) == ERROR_SUCCESS))
208 
209     {
210         hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager");
211     }
212 }
213 
214 VOID
215 FreeLogs()
216 {
217     if (hLog)
218     {
219         DeregisterEventSource(hLog);
220     }
221 }
222 
223 BOOL
224 WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg)
225 {
226     if (!SettingsInfo.bLogEnabled)
227     {
228         return TRUE;
229     }
230 
231     if (!ReportEventW(hLog, wType, 0, dwEventID, NULL, 1, 0, &lpMsg, NULL))
232     {
233         return FALSE;
234     }
235 
236     return TRUE;
237 }
238 
239 BOOL
240 GetInstalledVersion_WowUser(CStringW *szVersionResult, const CStringW &szRegName, BOOL IsUserKey, REGSAM keyWow)
241 {
242     BOOL bHasSucceded = FALSE;
243     ATL::CRegKey key;
244     CStringW szVersion;
245     CStringW szPath = CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") + szRegName;
246 
247     if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szPath.GetString(), keyWow | KEY_READ) !=
248         ERROR_SUCCESS)
249     {
250         return FALSE;
251     }
252 
253     if (szVersionResult != NULL)
254     {
255         ULONG dwSize = MAX_PATH * sizeof(WCHAR);
256 
257         if (key.QueryStringValue(L"DisplayVersion", szVersion.GetBuffer(MAX_PATH), &dwSize) == ERROR_SUCCESS)
258         {
259             szVersion.ReleaseBuffer();
260             *szVersionResult = szVersion;
261             bHasSucceded = TRUE;
262         }
263         else
264         {
265             szVersion.ReleaseBuffer();
266         }
267     }
268     else
269     {
270         bHasSucceded = TRUE;
271     }
272 
273     return bHasSucceded;
274 }
275 
276 BOOL
277 GetInstalledVersion(CStringW *pszVersion, const CStringW &szRegName)
278 {
279     return (
280         !szRegName.IsEmpty() && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY) ||
281                                  GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY) ||
282                                  GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY) ||
283                                  GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY)));
284 }
285 
286 BOOL
287 IsSystem64Bit()
288 {
289 #ifdef _WIN64
290     return TRUE;
291 #else
292     static UINT cache = 0;
293     if (!cache)
294         cache = 1 + (IsOS(OS_WOW6432) != FALSE);
295     return cache - 1;
296 #endif
297 }
298 
299 INT
300 GetSystemColorDepth()
301 {
302     DEVMODEW pDevMode;
303     INT ColorDepth;
304 
305     pDevMode.dmSize = sizeof(pDevMode);
306     pDevMode.dmDriverExtra = 0;
307 
308     if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
309     {
310         /* TODO: Error message */
311         return ILC_COLOR;
312     }
313 
314     switch (pDevMode.dmBitsPerPel)
315     {
316         case 32:
317             ColorDepth = ILC_COLOR32;
318             break;
319         case 24:
320             ColorDepth = ILC_COLOR24;
321             break;
322         case 16:
323             ColorDepth = ILC_COLOR16;
324             break;
325         case 8:
326             ColorDepth = ILC_COLOR8;
327             break;
328         case 4:
329             ColorDepth = ILC_COLOR4;
330             break;
331         default:
332             ColorDepth = ILC_COLOR;
333             break;
334     }
335 
336     return ColorDepth;
337 }
338 
339 void
340 UnixTimeToFileTime(DWORD dwUnixTime, LPFILETIME pFileTime)
341 {
342     // Note that LONGLONG is a 64-bit value
343     LONGLONG ll;
344 
345     ll = Int32x32To64(dwUnixTime, 10000000) + 116444736000000000;
346     pFileTime->dwLowDateTime = (DWORD)ll;
347     pFileTime->dwHighDateTime = ll >> 32;
348 }
349 
350 HRESULT
351 RegKeyHasValues(HKEY hKey, LPCWSTR Path, REGSAM wowsam)
352 {
353     CRegKey key;
354     LONG err = key.Open(hKey, Path, KEY_QUERY_VALUE | wowsam);
355     if (err == ERROR_SUCCESS)
356     {
357         WCHAR name[1];
358         DWORD cchname = _countof(name), cbsize = 0;
359         err = RegEnumValueW(key, 0, name, &cchname, NULL, NULL, NULL, &cbsize);
360         if (err == ERROR_NO_MORE_ITEMS)
361             return S_FALSE;
362         if (err == ERROR_MORE_DATA)
363             err = ERROR_SUCCESS;
364     }
365     return HRESULT_FROM_WIN32(err);
366 }
367 
368 LPCWSTR
369 GetRegString(CRegKey &Key, LPCWSTR Name, CStringW &Value)
370 {
371     for (;;)
372     {
373         ULONG cb = 0, cch;
374         ULONG err = Key.QueryValue(Name, NULL, NULL, &cb);
375         if (err)
376             break;
377         cch = cb / sizeof(WCHAR);
378         LPWSTR p = Value.GetBuffer(cch + 1);
379         p[cch] = UNICODE_NULL;
380         err = Key.QueryValue(Name, NULL, (BYTE*)p, &cb);
381         if (err == ERROR_MORE_DATA)
382             continue;
383         if (err)
384             break;
385         Value.ReleaseBuffer();
386         return Value.GetString();
387     }
388     return NULL;
389 }
390 
391 bool
392 ExpandEnvStrings(CStringW &Str)
393 {
394     CStringW buf;
395     DWORD cch = ExpandEnvironmentStringsW(Str, NULL, 0);
396     if (cch)
397     {
398         if (ExpandEnvironmentStringsW(Str, buf.GetBuffer(cch), cch) == cch)
399         {
400             buf.ReleaseBuffer(cch - 1);
401             Str = buf;
402             return true;
403         }
404     }
405     return false;
406 }
407 
408 BOOL
409 SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
410 {
411     if (!*szNeedle)
412         return TRUE;
413     /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
414     return StrStrIW(szHaystack, szNeedle) != NULL;
415 }
416 
417 BOOL
418 DeleteDirectoryTree(LPCWSTR Dir, HWND hwnd)
419 {
420     CStringW from(Dir);
421     UINT cch = from.GetLength();
422     from.Append(L"00");
423     LPWSTR p = from.GetBuffer();
424     p[cch] = p[cch + 1] = L'\0'; // Double null-terminate
425     UINT fof = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
426     SHFILEOPSTRUCT shfos = { hwnd, FO_DELETE, p, NULL, (FILEOP_FLAGS)fof };
427     return SHFileOperationW(&shfos);
428 }
429 
430 UINT
431 CreateDirectoryTree(LPCWSTR Dir)
432 {
433     UINT err = SHCreateDirectory(NULL, Dir);
434     return err == ERROR_ALREADY_EXISTS ? 0 : err;
435 }
436 
437 CStringW
438 SplitFileAndDirectory(LPCWSTR FullPath, CStringW *pDir)
439 {
440     CPathW dir = FullPath;
441     //int win = dir.ReverseFind(L'\\'), nix = dir.ReverseFind(L'/'), sep = max(win, nix);
442     int sep = dir.FindFileName();
443     CStringW file = dir.m_strPath.Mid(sep);
444     if (pDir)
445         *pDir = sep == -1 ? L"" : dir.m_strPath.Left(sep - 1);
446     return file;
447 }
448 
449 HRESULT
450 GetSpecialPath(UINT csidl, CStringW &Path, HWND hwnd)
451 {
452     if (!SHGetSpecialFolderPathW(hwnd, Path.GetBuffer(MAX_PATH), csidl, TRUE))
453         return E_FAIL;
454     Path.ReleaseBuffer();
455     return S_OK;
456 }
457 
458 HRESULT
459 GetKnownPath(REFKNOWNFOLDERID kfid, CStringW &Path, DWORD Flags)
460 {
461     PWSTR p;
462     FARPROC f = GetProcAddress(LoadLibraryW(L"SHELL32"), "SHGetKnownFolderPath");
463     if (!f)
464         return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
465     HRESULT hr = ((HRESULT(WINAPI*)(REFKNOWNFOLDERID,UINT,HANDLE,PWSTR*))f)(kfid, Flags, NULL, &p);
466     if (FAILED(hr))
467         return hr;
468     Path = p;
469     CoTaskMemFree(p);
470     return hr;
471 }
472 
473 HRESULT
474 GetProgramFilesPath(CStringW &Path, BOOL PerUser, HWND hwnd)
475 {
476     if (!PerUser)
477         return GetSpecialPath(CSIDL_PROGRAM_FILES, Path, hwnd);
478 
479     HRESULT hr = GetKnownPath(FOLDERID_UserProgramFiles, Path);
480     if (FAILED(hr))
481     {
482         hr = GetSpecialPath(CSIDL_LOCAL_APPDATA, Path, hwnd);
483         // Use the correct path on NT6 (on NT5 the path becomes a bit long)
484         if (SUCCEEDED(hr) && LOBYTE(GetVersion()) >= 6)
485         {
486             Path = BuildPath(Path, L"Programs"); // Should not be localized
487         }
488     }
489     return hr;
490 }
491