159c739600eb7547ab1fbb1b9f1bd87132932cb1
[reactos.git] / reactos / base / applications / rapps / misc.cpp
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/rapps/misc.cpp
5 * PURPOSE: Misc functions
6 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
7 * Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 */
9
10 #include "rapps.h"
11 #include <atlsimpcoll.h>
12 #include <atlstr.h>
13
14 /* SESSION Operation */
15 #define EXTRACT_FILLFILELIST 0x00000001
16 #define EXTRACT_EXTRACTFILES 0x00000002
17
18 static HANDLE hLog = NULL;
19 WCHAR szCachedINISectionLocale[MAX_PATH] = L"Section.";
20 WCHAR szCachedINISectionLocaleNeutral[MAX_PATH] = {0};
21 BYTE bCachedSectionStatus = FALSE;
22
23 #define STR_VERSION_CURRENT L"CURRENT"
24
25 typedef struct
26 {
27 int erfOper;
28 int erfType;
29 BOOL fError;
30 } ERF, *PERF;
31
32 struct FILELIST
33 {
34 LPSTR FileName;
35 struct FILELIST *next;
36 BOOL DoExtract;
37 };
38
39 typedef struct
40 {
41 INT FileSize;
42 ERF Error;
43 struct FILELIST *FileList;
44 INT FileCount;
45 INT Operation;
46 CHAR Destination[MAX_PATH];
47 CHAR CurrentFile[MAX_PATH];
48 CHAR Reserved[MAX_PATH];
49 struct FILELIST *FilterList;
50 } SESSION;
51
52 typedef HRESULT(WINAPI *fnExtract)(SESSION *dest, LPCSTR szCabName);
53 fnExtract pfnExtract;
54
55
56 INT
57 GetSystemColorDepth(VOID)
58 {
59 DEVMODE pDevMode;
60 INT ColorDepth;
61
62 pDevMode.dmSize = sizeof(pDevMode);
63 pDevMode.dmDriverExtra = 0;
64
65 if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
66 {
67 /* TODO: Error message */
68 return ILC_COLOR;
69 }
70
71 switch (pDevMode.dmBitsPerPel)
72 {
73 case 32: ColorDepth = ILC_COLOR32; break;
74 case 24: ColorDepth = ILC_COLOR24; break;
75 case 16: ColorDepth = ILC_COLOR16; break;
76 case 8: ColorDepth = ILC_COLOR8; break;
77 case 4: ColorDepth = ILC_COLOR4; break;
78 default: ColorDepth = ILC_COLOR; break;
79 }
80
81 return ColorDepth;
82 }
83
84 int
85 GetWindowWidth(HWND hwnd)
86 {
87 RECT Rect;
88
89 GetWindowRect(hwnd, &Rect);
90 return (Rect.right - Rect.left);
91 }
92
93 int
94 GetWindowHeight(HWND hwnd)
95 {
96 RECT Rect;
97
98 GetWindowRect(hwnd, &Rect);
99 return (Rect.bottom - Rect.top);
100 }
101
102 int
103 GetClientWindowWidth(HWND hwnd)
104 {
105 RECT Rect;
106
107 GetClientRect(hwnd, &Rect);
108 return (Rect.right - Rect.left);
109 }
110
111 int
112 GetClientWindowHeight(HWND hwnd)
113 {
114 RECT Rect;
115
116 GetClientRect(hwnd, &Rect);
117 return (Rect.bottom - Rect.top);
118 }
119
120 VOID
121 CopyTextToClipboard(LPCWSTR lpszText)
122 {
123 HRESULT hr;
124
125 if (OpenClipboard(NULL))
126 {
127 HGLOBAL ClipBuffer;
128 WCHAR *Buffer;
129 DWORD cchBuffer;
130
131 EmptyClipboard();
132 cchBuffer = wcslen(lpszText) + 1;
133 ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR));
134 Buffer = (PWCHAR)GlobalLock(ClipBuffer);
135 hr = StringCchCopyW(Buffer, cchBuffer, lpszText);
136 GlobalUnlock(ClipBuffer);
137
138 if (SUCCEEDED(hr))
139 SetClipboardData(CF_UNICODETEXT, ClipBuffer);
140
141 CloseClipboard();
142 }
143 }
144
145 VOID
146 SetWelcomeText(VOID)
147 {
148 WCHAR szText[MAX_STR_LEN*3];
149
150 LoadStringW(hInst, IDS_WELCOME_TITLE, szText, _countof(szText));
151 NewRichEditText(szText, CFE_BOLD);
152
153 LoadStringW(hInst, IDS_WELCOME_TEXT, szText, _countof(szText));
154 InsertRichEditText(szText, 0);
155
156 LoadStringW(hInst, IDS_WELCOME_URL, szText, _countof(szText));
157 InsertRichEditText(szText, CFM_LINK);
158 }
159
160 VOID
161 ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem)
162 {
163 HMENU hMenu = NULL;
164 HMENU hPopupMenu;
165 MENUITEMINFO mii;
166 POINT pt;
167
168 if (MenuID)
169 {
170 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID));
171 hPopupMenu = GetSubMenu(hMenu, 0);
172 }
173 else
174 hPopupMenu = GetMenu(hwnd);
175
176 ZeroMemory(&mii, sizeof(mii));
177 mii.cbSize = sizeof(mii);
178 mii.fMask = MIIM_STATE;
179 GetMenuItemInfo(hPopupMenu, DefaultItem, FALSE, &mii);
180
181 if (!(mii.fState & MFS_GRAYED))
182 SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE);
183
184 GetCursorPos(&pt);
185
186 SetForegroundWindow(hwnd);
187 TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hMainWnd, NULL);
188
189 if (hMenu)
190 DestroyMenu(hMenu);
191 }
192
193 BOOL
194 StartProcess(LPWSTR lpPath, BOOL Wait)
195 {
196 PROCESS_INFORMATION pi;
197 STARTUPINFOW si;
198 DWORD dwRet;
199 MSG msg;
200
201 ZeroMemory(&si, sizeof(si));
202 si.cb = sizeof(si);
203 si.dwFlags = STARTF_USESHOWWINDOW;
204 si.wShowWindow = SW_SHOW;
205
206 if (!CreateProcessW(NULL, lpPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
207 {
208 return FALSE;
209 }
210
211 CloseHandle(pi.hThread);
212 if (Wait) EnableWindow(hMainWnd, FALSE);
213
214 while (Wait)
215 {
216 dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
217 if (dwRet == WAIT_OBJECT_0 + 1)
218 {
219 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
220 {
221 TranslateMessage(&msg);
222 DispatchMessage(&msg);
223 }
224 }
225 else
226 {
227 if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED)
228 break;
229 }
230 }
231
232 CloseHandle(pi.hProcess);
233
234 if (Wait)
235 {
236 EnableWindow(hMainWnd, TRUE);
237 SetForegroundWindow(hMainWnd);
238 SetFocus(hMainWnd);
239 }
240
241 return TRUE;
242 }
243
244 BOOL
245 GetStorageDirectory(PWCHAR lpDirectory, DWORD cch)
246 {
247 if (cch < MAX_PATH)
248 return FALSE;
249
250 if (!SHGetSpecialFolderPathW(NULL, lpDirectory, CSIDL_LOCAL_APPDATA, TRUE))
251 return FALSE;
252
253 if (FAILED(StringCchCatW(lpDirectory, cch, L"\\rapps")))
254 return FALSE;
255
256 if (!CreateDirectoryW(lpDirectory, NULL) &&
257 GetLastError() != ERROR_ALREADY_EXISTS)
258 {
259 return FALSE;
260 }
261
262 return TRUE;
263 }
264
265 BOOL
266 ExtractFilesFromCab(LPWSTR lpCabName, LPWSTR lpOutputPath)
267 {
268 HINSTANCE hCabinetDll;
269 CHAR szCabName[MAX_PATH];
270 SESSION Dest;
271 HRESULT Result;
272
273 hCabinetDll = LoadLibraryW(L"cabinet.dll");
274 if (hCabinetDll)
275 {
276 pfnExtract = (fnExtract) GetProcAddress(hCabinetDll, "Extract");
277 if (pfnExtract)
278 {
279 ZeroMemory(&Dest, sizeof(Dest));
280
281 WideCharToMultiByte(CP_ACP, 0, lpOutputPath, -1, Dest.Destination, MAX_PATH, NULL, NULL);
282 WideCharToMultiByte(CP_ACP, 0, lpCabName, -1, szCabName, MAX_PATH, NULL, NULL);
283 Dest.Operation = EXTRACT_FILLFILELIST;
284
285 Result = pfnExtract(&Dest, szCabName);
286 if (Result == S_OK)
287 {
288 Dest.Operation = EXTRACT_EXTRACTFILES;
289 Result = pfnExtract(&Dest, szCabName);
290 if (Result == S_OK)
291 {
292 FreeLibrary(hCabinetDll);
293 return TRUE;
294 }
295 }
296 }
297 FreeLibrary(hCabinetDll);
298 }
299
300 return FALSE;
301 }
302
303 VOID
304 InitLogs(VOID)
305 {
306 WCHAR szBuf[MAX_PATH] = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager";
307 WCHAR szPath[MAX_PATH];
308 DWORD dwCategoryNum = 1;
309 DWORD dwDisp, dwData;
310 HKEY hKey;
311
312 if (!SettingsInfo.bLogEnabled) return;
313
314 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,
315 szBuf, 0, NULL,
316 REG_OPTION_NON_VOLATILE,
317 KEY_WRITE, NULL, &hKey, &dwDisp) != ERROR_SUCCESS)
318 {
319 return;
320 }
321
322 if (!GetModuleFileNameW(NULL, szPath, _countof(szPath)))
323 return;
324
325 if (RegSetValueExW(hKey,
326 L"EventMessageFile",
327 0,
328 REG_EXPAND_SZ,
329 (LPBYTE)szPath,
330 (DWORD)(wcslen(szPath) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
331 {
332 RegCloseKey(hKey);
333 return;
334 }
335
336 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
337 EVENTLOG_INFORMATION_TYPE;
338
339 if (RegSetValueExW(hKey,
340 L"TypesSupported",
341 0,
342 REG_DWORD,
343 (LPBYTE)&dwData,
344 sizeof(DWORD)) != ERROR_SUCCESS)
345 {
346 RegCloseKey(hKey);
347 return;
348 }
349
350 if (RegSetValueExW(hKey,
351 L"CategoryMessageFile",
352 0,
353 REG_EXPAND_SZ,
354 (LPBYTE)szPath,
355 (DWORD)(wcslen(szPath) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
356 {
357 RegCloseKey(hKey);
358 return;
359 }
360
361 if (RegSetValueExW(hKey,
362 L"CategoryCount",
363 0,
364 REG_DWORD,
365 (LPBYTE)&dwCategoryNum,
366 sizeof(DWORD)) != ERROR_SUCCESS)
367 {
368 RegCloseKey(hKey);
369 return;
370 }
371
372 RegCloseKey(hKey);
373
374 hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager");
375 }
376
377
378 VOID
379 FreeLogs(VOID)
380 {
381 if (hLog) DeregisterEventSource(hLog);
382 }
383
384
385 BOOL
386 WriteLogMessage(WORD wType, DWORD dwEventID, LPWSTR lpMsg)
387 {
388 if (!SettingsInfo.bLogEnabled) return TRUE;
389
390 if (!ReportEventW(hLog,
391 wType,
392 0,
393 dwEventID,
394 NULL,
395 1,
396 0,
397 (LPCWSTR*)&lpMsg,
398 NULL))
399 {
400 return FALSE;
401 }
402
403 return TRUE;
404 }
405
406
407 LPWSTR GetINIFullPath(LPCWSTR lpFileName)
408 {
409 WCHAR szDir[MAX_PATH];
410 static WCHAR szBuffer[MAX_PATH];
411
412 GetStorageDirectory(szDir, _countof(szDir));
413 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%ls\\rapps\\%ls", szDir, lpFileName);
414
415 return szBuffer;
416 }
417
418
419 UINT ParserGetString(LPCWSTR lpKeyName, LPWSTR lpReturnedString, UINT nSize, LPCWSTR lpFileName)
420 {
421 PWSTR lpFullFileName = GetINIFullPath(lpFileName);
422 DWORD dwResult;
423
424 /* we don't have cached section strings for the current system language, create them */
425 if(bCachedSectionStatus == FALSE)
426 {
427 WCHAR szLocale[4 + 1];
428 DWORD len;
429
430 /* find out what is the current system lang code (e.g. "0a") and append it to SectionLocale */
431 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
432 szLocale, _countof(szLocale));
433
434 StringCbCatW(szCachedINISectionLocale, sizeof(szCachedINISectionLocale), szLocale);
435
436 /* copy the locale-dependent string into the buffer of the future neutral one */
437 StringCbCopyW(szCachedINISectionLocaleNeutral,
438 sizeof(szCachedINISectionLocaleNeutral),
439 szCachedINISectionLocale);
440
441 /* turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part */
442 len = wcslen(szCachedINISectionLocale);
443
444 memmove((szCachedINISectionLocaleNeutral + len) - 4,
445 (szCachedINISectionLocaleNeutral + len) - 2,
446 (2 * sizeof(WCHAR)) + sizeof(UNICODE_NULL));
447
448 /* finally, mark us as cache-friendly for the next time */
449 bCachedSectionStatus = TRUE;
450 }
451
452 /* 1st - find localized strings (e.g. "Section.0c0a") */
453 dwResult = GetPrivateProfileStringW(szCachedINISectionLocale,
454 lpKeyName,
455 NULL,
456 lpReturnedString,
457 nSize,
458 lpFullFileName);
459
460 if (dwResult != 0)
461 return TRUE;
462
463 /* 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a") */
464 dwResult = GetPrivateProfileStringW(szCachedINISectionLocaleNeutral,
465 lpKeyName,
466 NULL,
467 lpReturnedString,
468 nSize,
469 lpFullFileName);
470
471 if (dwResult != 0)
472 return TRUE;
473
474 /* 3rd - if they weren't present fallback to standard english strings (just "Section") */
475 dwResult = GetPrivateProfileStringW(L"Section",
476 lpKeyName,
477 NULL,
478 lpReturnedString,
479 nSize,
480 lpFullFileName);
481
482 return (dwResult != 0 ? TRUE : FALSE);
483 }
484
485 UINT ParserGetInt(LPCWSTR lpKeyName, LPCWSTR lpFileName)
486 {
487 WCHAR Buffer[30];
488 UNICODE_STRING BufferW;
489 ULONG Result;
490
491 /* grab the text version of our entry */
492 if (!ParserGetString(lpKeyName, Buffer, _countof(Buffer), lpFileName))
493 return FALSE;
494
495 if (!Buffer[0])
496 return FALSE;
497
498 /* convert it to an actual integer */
499 RtlInitUnicodeString(&BufferW, Buffer);
500 RtlUnicodeStringToInteger(&BufferW, 0, &Result);
501
502 return (UINT)Result;
503 }
504
505 template<typename XCHAR>
506 inline BOOL IsCharNumeric(XCHAR ch)
507 {
508 return IsCharAlphaNumeric(ch) && !IsCharAlpha(ch);
509 }
510
511
512 //Parses version string that can be formatted as 1.2.3-4b (or CURRENT)
513 //Returns int buffer and it's size
514 BOOL
515 ParseVersion(_In_z_ LPCWSTR szVersion, _Outptr_ PVERSION_INFO Version)
516 {
517 ATL::CStringW szVersionSingleInt = L"";
518 BOOL bHasParsed = TRUE;
519 INT VersionCharCount = 0;
520 INT VersionLength = lstrlenW(szVersion);
521 StringCbCopyW(Version->szVersion, VersionLength * sizeof(szVersion), szVersion);
522 //CURRENT
523 if (!StrCmpW(szVersion, STR_VERSION_CURRENT))
524 {
525 Version->VersionSize = NULL;
526 return bHasParsed;
527 }
528
529 Version->VersionSize = 0;
530 //int expected every iteration, quit the loop if its not a number
531 while (Version->VersionSize < MAX_VERSION
532 && IsCharNumeric(szVersion[VersionCharCount])
533 && VersionCharCount < VersionLength)
534 {
535 for (; IsCharNumeric(szVersion[VersionCharCount]) && VersionCharCount < VersionLength; ++VersionCharCount)
536 {
537 szVersionSingleInt += szVersion[VersionCharCount];
538 }
539 if (szVersionSingleInt.IsEmpty())
540 {
541 bHasParsed = FALSE;
542 continue;
543 }
544 INT IntResult = StrToIntW(szVersionSingleInt.GetBuffer());
545 Version->arrVersion[Version->VersionSize] = IntResult;
546 ++Version->VersionSize;
547 szVersionSingleInt.Empty();
548 ++VersionCharCount;
549 }
550
551 if (IsCharAlphaW(szVersion[VersionCharCount]))
552 {
553 Version->cVersionSuffix = szVersion[VersionCharCount];
554 }
555 else
556 Version->cVersionSuffix = NULL;
557 return bHasParsed;
558 }
559
560 //Compares versions
561 //In: Zero terminated strings of versions
562 //Out: TRUE if first is bigger than second, FALSE if else
563 BOOL
564 CompareVersionsStrings(_In_z_ LPCWSTR sczVersionLeft, _In_z_ LPCWSTR sczVersionRight)
565 {
566 VERSION_INFO LeftVersion, RightVersion;
567
568 if (!ParseVersion(sczVersionLeft, &LeftVersion)
569 || !ParseVersion(sczVersionRight, &RightVersion))
570 {
571 return FALSE;
572 }
573
574 return CompareVersions(&LeftVersion, &RightVersion);
575 }
576
577 BOOL
578 CompareVersions(_In_ PVERSION_INFO LeftVersion, _In_ PVERSION_INFO RightVersion)
579 {
580 //CURRENT
581 if (!LeftVersion->VersionSize || !RightVersion->VersionSize)
582 {
583 return FALSE;
584 }
585 //1.2.3 > 1.2
586 INT SizeDiff = LeftVersion->VersionSize - RightVersion->VersionSize;
587 if (SizeDiff > 0)
588 {
589 return TRUE;
590 }
591 if (SizeDiff < 0)
592 {
593 return FALSE;
594 }
595 //2.0.0 > 1.9.9
596 for (INT i = 0; i < LeftVersion->VersionSize && i < RightVersion->VersionSize && i < MAX_VERSION; ++i)
597 {
598 if (LeftVersion->arrVersion[i] > RightVersion->arrVersion[i])
599 {
600 return TRUE;
601 }
602 if (LeftVersion->arrVersion[i] < RightVersion->arrVersion[i])
603 {
604 return FALSE;
605 }
606 }
607 //1.2.3b > 1.2.3
608 if (LeftVersion->cVersionSuffix > RightVersion->cVersionSuffix)
609 {
610 return TRUE;
611 }
612
613 return FALSE;
614 }