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