7a35440f4a6aad787e16941fc80f7ce5b07c13bf
[reactos.git] / reactos / base / applications / rapps / misc.cpp
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * FILE: base/applications/rapps/misc.cpp
5 * PURPOSE: Misc functions
6 * COPYRIGHT: Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org)
7 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
9 */
10 #include "defines.h"
11
12 #include "gui.h"
13 #include "misc.h"
14 #include "cabinet.h"
15
16 /* SESSION Operation */
17 #define EXTRACT_FILLFILELIST 0x00000001
18 #define EXTRACT_EXTRACTFILES 0x00000002
19
20 static HANDLE hLog = NULL;
21
22 INT GetWindowWidth(HWND hwnd)
23 {
24 RECT Rect;
25
26 GetWindowRect(hwnd, &Rect);
27 return (Rect.right - Rect.left);
28 }
29
30 INT GetWindowHeight(HWND hwnd)
31 {
32 RECT Rect;
33
34 GetWindowRect(hwnd, &Rect);
35 return (Rect.bottom - Rect.top);
36 }
37
38 INT GetClientWindowWidth(HWND hwnd)
39 {
40 RECT Rect;
41
42 GetClientRect(hwnd, &Rect);
43 return (Rect.right - Rect.left);
44 }
45
46 INT GetClientWindowHeight(HWND hwnd)
47 {
48 RECT Rect;
49
50 GetClientRect(hwnd, &Rect);
51 return (Rect.bottom - Rect.top);
52 }
53
54 VOID CopyTextToClipboard(LPCWSTR lpszText)
55 {
56 if (!OpenClipboard(NULL))
57 {
58 return;
59 }
60
61 HRESULT hr;
62 HGLOBAL ClipBuffer;
63 LPWSTR Buffer;
64 DWORD cchBuffer;
65
66 EmptyClipboard();
67 cchBuffer = wcslen(lpszText) + 1;
68 ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR));
69
70 Buffer = (PWCHAR) GlobalLock(ClipBuffer);
71 hr = StringCchCopyW(Buffer, cchBuffer, lpszText);
72 GlobalUnlock(ClipBuffer);
73
74 if (SUCCEEDED(hr))
75 SetClipboardData(CF_UNICODETEXT, ClipBuffer);
76
77 CloseClipboard();
78 }
79
80 VOID SetWelcomeText()
81 {
82 ATL::CStringW szText;
83
84 szText.LoadStringW(IDS_WELCOME_TITLE);
85 NewRichEditText(szText, CFE_BOLD);
86
87 szText.LoadStringW(IDS_WELCOME_TEXT);
88 InsertRichEditText(szText, 0);
89
90 szText.LoadStringW(IDS_WELCOME_URL);
91 InsertRichEditText(szText, CFM_LINK);
92 }
93
94 VOID ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem)
95 {
96 HMENU hMenu = NULL;
97 HMENU hPopupMenu;
98 MENUITEMINFO ItemInfo;
99 POINT pt;
100
101 if (MenuID)
102 {
103 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID));
104 hPopupMenu = GetSubMenu(hMenu, 0);
105 }
106 else
107 {
108 hPopupMenu = GetMenu(hwnd);
109 }
110
111 ZeroMemory(&ItemInfo, sizeof(ItemInfo));
112 ItemInfo.cbSize = sizeof(ItemInfo);
113 ItemInfo.fMask = MIIM_STATE;
114
115 GetMenuItemInfoW(hPopupMenu, DefaultItem, FALSE, &ItemInfo);
116
117 if (!(ItemInfo.fState & MFS_GRAYED))
118 {
119 SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE);
120 }
121
122 GetCursorPos(&pt);
123
124 SetForegroundWindow(hwnd);
125 TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hMainWnd, NULL);
126
127 if (hMenu)
128 {
129 DestroyMenu(hMenu);
130 }
131 }
132
133 BOOL StartProcess(ATL::CStringW &Path, BOOL Wait)
134 {
135 return StartProcess(const_cast<LPWSTR>(Path.GetString()), Wait);;
136 }
137
138 BOOL StartProcess(LPWSTR lpPath, BOOL Wait)
139 {
140 PROCESS_INFORMATION pi;
141 STARTUPINFOW si;
142 DWORD dwRet;
143 MSG msg;
144
145 ZeroMemory(&si, sizeof(si));
146 si.cb = sizeof(si);
147 si.dwFlags = STARTF_USESHOWWINDOW;
148 si.wShowWindow = SW_SHOW;
149
150 if (!CreateProcessW(NULL, lpPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
151 {
152 return FALSE;
153 }
154
155 CloseHandle(pi.hThread);
156
157 if (Wait)
158 {
159 EnableWindow(hMainWnd, FALSE);
160 }
161
162 while (Wait)
163 {
164 dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
165 if (dwRet == WAIT_OBJECT_0 + 1)
166 {
167 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
168 {
169 TranslateMessage(&msg);
170 DispatchMessageW(&msg);
171 }
172 }
173 else
174 {
175 if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED)
176 break;
177 }
178 }
179
180 CloseHandle(pi.hProcess);
181
182 if (Wait)
183 {
184 EnableWindow(hMainWnd, TRUE);
185 SetForegroundWindow(hMainWnd);
186 SetFocus(hMainWnd);
187 }
188
189 return TRUE;
190 }
191
192 BOOL GetStorageDirectory(ATL::CStringW& Directory)
193 {
194 if (!SHGetSpecialFolderPathW(NULL, Directory.GetBuffer(MAX_PATH), CSIDL_LOCAL_APPDATA, TRUE))
195 {
196 Directory.ReleaseBuffer();
197 return FALSE;
198 }
199
200 Directory.ReleaseBuffer();
201 Directory += L"\\rapps";
202
203 return (CreateDirectoryW(Directory.GetString(), NULL) || GetLastError() == ERROR_ALREADY_EXISTS);
204 }
205
206 BOOL ExtractFilesFromCab(const ATL::CStringW &CabName, const ATL::CStringW &OutputPath)
207 {
208 return ExtractFilesFromCab(CabName.GetString(), OutputPath.GetString());
209 }
210
211 BOOL ExtractFilesFromCab(LPCWSTR lpCabName, LPCWSTR lpOutputPath)
212 {
213 HINSTANCE hCabinetDll;
214 CHAR szCabName[MAX_PATH];
215 SESSION Dest;
216 HRESULT Result;
217 fnExtract pfnExtract;
218
219 hCabinetDll = LoadLibraryW(L"cabinet.dll");
220 if (hCabinetDll)
221 {
222 pfnExtract = (fnExtract) GetProcAddress(hCabinetDll, "Extract");
223 if (pfnExtract)
224 {
225 ZeroMemory(&Dest, sizeof(Dest));
226
227 WideCharToMultiByte(CP_ACP, 0, lpOutputPath, -1, Dest.Destination, MAX_PATH, NULL, NULL);
228 WideCharToMultiByte(CP_ACP, 0, lpCabName, -1, szCabName, MAX_PATH, NULL, NULL);
229 Dest.Operation = EXTRACT_FILLFILELIST;
230
231 Result = pfnExtract(&Dest, szCabName);
232 if (Result == S_OK)
233 {
234 Dest.Operation = EXTRACT_EXTRACTFILES;
235 CreateDirectoryW(lpOutputPath, NULL);
236
237 Result = pfnExtract(&Dest, szCabName);
238 if (Result == S_OK)
239 {
240 FreeLibrary(hCabinetDll);
241 return TRUE;
242 }
243 else
244 {
245 RemoveDirectoryW(lpOutputPath);
246 }
247 }
248 }
249 FreeLibrary(hCabinetDll);
250 }
251
252 return FALSE;
253 }
254
255 VOID InitLogs()
256 {
257 if (!SettingsInfo.bLogEnabled)
258 {
259 return;
260 }
261
262 WCHAR szPath[MAX_PATH];
263 DWORD dwCategoryNum = 1;
264 DWORD dwDisp, dwData;
265 ATL::CRegKey key;
266
267 if (key.Create(HKEY_LOCAL_MACHINE,
268 L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager",
269 REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &dwDisp) != ERROR_SUCCESS)
270 {
271 return;
272 }
273
274 if (!GetModuleFileNameW(NULL, szPath, _countof(szPath)))
275 {
276 return;
277 }
278
279 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
280 EVENTLOG_INFORMATION_TYPE;
281
282 if ((key.SetStringValue(L"EventMessageFile",
283 szPath,
284 REG_EXPAND_SZ) == ERROR_SUCCESS)
285 && (key.SetStringValue(L"CategoryMessageFile",
286 szPath,
287 REG_EXPAND_SZ) == ERROR_SUCCESS)
288 && (key.SetDWORDValue(L"TypesSupported",
289 dwData) == ERROR_SUCCESS)
290 && (key.SetDWORDValue(L"CategoryCount",
291 dwCategoryNum) == ERROR_SUCCESS))
292
293 {
294 hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager");
295 }
296
297 key.Close();
298 }
299
300
301 VOID FreeLogs()
302 {
303 if (hLog)
304 {
305 DeregisterEventSource(hLog);
306 }
307 }
308
309
310 BOOL WriteLogMessage(WORD wType, DWORD dwEventID, LPCWSTR lpMsg)
311 {
312 if (!SettingsInfo.bLogEnabled)
313 {
314 return TRUE;
315 }
316
317 if (!ReportEventW(hLog, wType, 0, dwEventID,
318 NULL, 1, 0, &lpMsg, NULL))
319 {
320 return FALSE;
321 }
322
323 return TRUE;
324 }
325
326 BOOL GetInstalledVersion_WowUser(ATL::CStringW* szVersionResult,
327 const ATL::CStringW& szRegName,
328 BOOL IsUserKey,
329 REGSAM keyWow)
330 {
331 BOOL bHasSucceded = FALSE;
332 ATL::CRegKey key;
333 ATL::CStringW szVersion;
334 ATL::CStringW szPath = ATL::CStringW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%ls") + szRegName;
335
336 if (key.Open(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
337 szPath.GetString(),
338 keyWow | KEY_READ) != ERROR_SUCCESS)
339 {
340 return FALSE;
341 }
342
343 if (szVersionResult != NULL)
344 {
345 ULONG dwSize = MAX_PATH * sizeof(WCHAR);
346
347 if (key.QueryStringValue(L"DisplayVersion",
348 szVersion.GetBuffer(MAX_PATH),
349 &dwSize) == ERROR_SUCCESS)
350 {
351 szVersion.ReleaseBuffer();
352 *szVersionResult = szVersion;
353 bHasSucceded = TRUE;
354 }
355 else
356 {
357 szVersion.ReleaseBuffer();
358 }
359 }
360 else
361 {
362 bHasSucceded = TRUE;
363 szVersion.ReleaseBuffer();
364 }
365 key.Close();
366
367 return bHasSucceded;
368 }
369
370 BOOL GetInstalledVersion(ATL::CStringW *pszVersion, const ATL::CStringW &szRegName)
371 {
372 return (!szRegName.IsEmpty()
373 && (GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_32KEY)
374 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_32KEY)
375 || GetInstalledVersion_WowUser(pszVersion, szRegName, TRUE, KEY_WOW64_64KEY)
376 || GetInstalledVersion_WowUser(pszVersion, szRegName, FALSE, KEY_WOW64_64KEY)));
377 }
378
379 // CConfigParser
380
381 CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName))
382 {
383 CacheINILocale();
384 }
385
386 ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName)
387 {
388 ATL::CStringW szDir;
389 ATL::CStringW szBuffer;
390
391 GetStorageDirectory(szDir);
392 szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString());
393
394 return szBuffer;
395 }
396
397 VOID CConfigParser::CacheINILocale()
398 {
399 // TODO: Set default locale if call fails
400 // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale
401 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
402 m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize);
403
404 m_szLocaleID.ReleaseBuffer();
405 m_szCachedINISectionLocale = L"Section." + m_szLocaleID;
406
407 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
408 m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale + m_szLocaleID.Right(2);
409 }
410
411 UINT CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
412 {
413 DWORD dwResult;
414
415 LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH);
416 // 1st - find localized strings (e.g. "Section.0c0a")
417 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocale.GetString(),
418 KeyName.GetString(),
419 NULL,
420 ResultStringBuffer,
421 MAX_PATH,
422 szConfigPath.GetString());
423
424 if (!dwResult)
425 {
426 // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a")
427 dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocaleNeutral.GetString(),
428 KeyName.GetString(),
429 NULL,
430 ResultStringBuffer,
431 MAX_PATH,
432 szConfigPath.GetString());
433 if (!dwResult)
434 {
435 // 3rd - if they weren't present fallback to standard english strings (just "Section")
436 dwResult = GetPrivateProfileStringW(L"Section",
437 KeyName.GetString(),
438 NULL,
439 ResultStringBuffer,
440 MAX_PATH,
441 szConfigPath.GetString());
442 }
443 }
444
445 ResultString.ReleaseBuffer();
446 return (dwResult != 0 ? TRUE : FALSE);
447 }
448
449 UINT CConfigParser::GetInt(const ATL::CStringW& KeyName)
450 {
451 ATL::CStringW Buffer;
452
453 // grab the text version of our entry
454 if (!GetString(KeyName, Buffer))
455 return FALSE;
456
457 if (Buffer.IsEmpty())
458 return FALSE;
459
460 // convert it to an actual integer
461 INT result = StrToIntW(Buffer.GetString());
462
463 return (UINT) (result <= 0) ? 0 : result;
464 }
465 // CConfigParser