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