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