[SYSDM] Fix version string cutoff CORE-17429
[reactos.git] / dll / cpl / sysdm / general.c
1 /*
2 * PROJECT: ReactOS System Control Panel Applet
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/sysdm/general.c
5 * PURPOSE: General System Information
6 * COPYRIGHT: Copyright Thomas Weidenmueller <w3seek@reactos.org>
7 * Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8 * Copyright 2006-2007 Colin Finck <mail@colinfinck.de>
9 *
10 */
11
12 #include "precomp.h"
13
14 #include <winnls.h>
15 #include <powrprof.h>
16 #include <buildno.h>
17 #include <strsafe.h>
18
19 #define ANIM_STEP 2
20 #define ANIM_TIME 50
21
22 typedef struct _IMGINFO
23 {
24 HBITMAP hBitmap;
25 INT cxSource;
26 INT cySource;
27 INT iPlanes;
28 INT iBits;
29 } IMGINFO, *PIMGINFO;
30
31 static PIMGINFO pImgInfo;
32 static const BLENDFUNCTION BlendFunc = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
33
34 VOID ShowLastWin32Error(HWND hWndOwner)
35 {
36 LPTSTR lpMsg;
37 DWORD LastError;
38
39 LastError = GetLastError();
40 if (LastError == ERROR_SUCCESS)
41 return;
42
43 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
44 FORMAT_MESSAGE_FROM_SYSTEM |
45 FORMAT_MESSAGE_IGNORE_INSERTS,
46 NULL,
47 LastError,
48 LANG_USER_DEFAULT,
49 (LPTSTR)&lpMsg,
50 0, NULL))
51 {
52 return;
53 }
54
55 MessageBox(hWndOwner, lpMsg, NULL, MB_OK | MB_ICONERROR);
56 LocalFree(lpMsg);
57 }
58
59
60 static VOID InitLogo(HWND hwndDlg)
61 {
62 BITMAP logoBitmap;
63 BITMAP maskBitmap;
64 BITMAPINFO bmpi;
65 HDC hDC = GetDC(hwndDlg);
66 HDC hDCLogo = CreateCompatibleDC(NULL);
67 HDC hDCMask = CreateCompatibleDC(NULL);
68 HBITMAP hMask, hLogo, hAlphaLogo = NULL;
69 COLORREF *pBits;
70 INT line, column;
71
72 if (hDC == NULL || hDCLogo == NULL || hDCMask == NULL)
73 goto Cleanup;
74
75 ZeroMemory(pImgInfo, sizeof(*pImgInfo));
76 ZeroMemory(&bmpi, sizeof(bmpi));
77
78 hLogo = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSBMP), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
79 hMask = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSMASK), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
80
81 if (hLogo != NULL && hMask != NULL)
82 {
83 GetObject(hLogo, sizeof(logoBitmap), &logoBitmap);
84 GetObject(hMask, sizeof(maskBitmap), &maskBitmap);
85
86 if (logoBitmap.bmHeight != maskBitmap.bmHeight || logoBitmap.bmWidth != maskBitmap.bmWidth)
87 goto Cleanup;
88
89 bmpi.bmiHeader.biSize = sizeof(BITMAPINFO);
90 bmpi.bmiHeader.biWidth = logoBitmap.bmWidth;
91 bmpi.bmiHeader.biHeight = logoBitmap.bmHeight;
92 bmpi.bmiHeader.biPlanes = 1;
93 bmpi.bmiHeader.biBitCount = 32;
94 bmpi.bmiHeader.biCompression = BI_RGB;
95 bmpi.bmiHeader.biSizeImage = 4 * logoBitmap.bmWidth * logoBitmap.bmHeight;
96
97 /* Create a premultiplied bitmap */
98 hAlphaLogo = CreateDIBSection(hDC, &bmpi, DIB_RGB_COLORS, (PVOID*)&pBits, 0, 0);
99 if (!hAlphaLogo)
100 goto Cleanup;
101
102 SelectObject(hDCLogo, hLogo);
103 SelectObject(hDCMask, hMask);
104
105 for (line = logoBitmap.bmHeight - 1; line >= 0; line--)
106 {
107 for (column = 0; column < logoBitmap.bmWidth; column++)
108 {
109 COLORREF alpha = GetPixel(hDCMask, column, line) & 0xFF;
110 COLORREF Color = GetPixel(hDCLogo, column, line);
111 DWORD r, g, b;
112
113 r = GetRValue(Color) * alpha / 255;
114 g = GetGValue(Color) * alpha / 255;
115 b = GetBValue(Color) * alpha / 255;
116
117 *pBits++ = b | (g << 8) | (r << 16) | (alpha << 24);
118 }
119 }
120
121 pImgInfo->hBitmap = hAlphaLogo;
122 pImgInfo->cxSource = logoBitmap.bmWidth;
123 pImgInfo->cySource = logoBitmap.bmHeight;
124 pImgInfo->iBits = logoBitmap.bmBitsPixel;
125 pImgInfo->iPlanes = logoBitmap.bmPlanes;
126 }
127
128 Cleanup:
129 if (hMask != NULL) DeleteObject(hMask);
130 if (hLogo != NULL) DeleteObject(hLogo);
131 if (hDCMask != NULL) DeleteDC(hDCMask);
132 if (hDCLogo != NULL) DeleteDC(hDCLogo);
133 if (hDC != NULL) ReleaseDC(hwndDlg, hDC);
134 }
135
136 LRESULT CALLBACK RosImageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
137 {
138 switch (uMsg)
139 {
140 case WM_PAINT:
141 {
142 PAINTSTRUCT PS;
143 HDC hdcMem, hdc;
144 LONG left;
145
146 hdc = wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &PS);
147
148 GetClientRect(hwnd, &PS.rcPaint);
149
150 /* Position image in center of dialog */
151 left = (PS.rcPaint.right - pImgInfo->cxSource) / 2;
152 hdcMem = CreateCompatibleDC(hdc);
153
154 if (hdcMem != NULL)
155 {
156 SelectObject(hdcMem, pImgInfo->hBitmap);
157 /* FIXME: We should not rely on AlphaBlend and we should not import MSIMG32 solely for that function */
158 AlphaBlend(hdc, left, PS.rcPaint.top, pImgInfo->cxSource, pImgInfo->cySource, hdcMem, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
159 DeleteDC(hdcMem);
160 }
161
162 if (wParam == 0)
163 EndPaint(hwnd,&PS);
164 break;
165 }
166 }
167 return TRUE;
168 }
169
170 static VOID SetRegTextData(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
171 {
172 LPTSTR lpBuf = NULL;
173 DWORD BufSize = 0;
174 DWORD Type;
175
176 if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
177 {
178 lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
179
180 if (!lpBuf)
181 return;
182
183 if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
184 SetDlgItemText(hwnd, uID, lpBuf);
185
186 HeapFree(GetProcessHeap(), 0, lpBuf);
187 }
188 }
189
190 static INT SetProcNameString(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID1, UINT uID2)
191 {
192 LPTSTR lpBuf = NULL;
193 DWORD BufSize = 0;
194 DWORD Type;
195 INT Ret = 0;
196 TCHAR szBuf[31];
197 TCHAR* szLastSpace;
198 INT LastSpace = 0;
199
200 if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
201 {
202 lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
203
204 if (!lpBuf)
205 return 0;
206
207 if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
208 {
209 if (BufSize > ((30 + 1) * sizeof(TCHAR)))
210 {
211 /* Wrap the Processor Name String like XP does: *
212 * - Take the first 30 characters and look for the last space. *
213 * Then wrap the string after this space. *
214 * - If no space is found, wrap the string after character 30. *
215 * *
216 * For example the Processor Name String of a Pentium 4 is right-aligned. *
217 * With this wrapping the first line looks centered. */
218
219 _tcsncpy(szBuf, lpBuf, 30);
220 szBuf[30] = 0;
221 szLastSpace = _tcsrchr(szBuf, ' ');
222
223 if (szLastSpace == 0)
224 {
225 LastSpace = 30;
226 }
227 else
228 {
229 LastSpace = (szLastSpace - szBuf);
230 szBuf[LastSpace] = 0;
231 }
232
233 _tcsncpy(szBuf, lpBuf, LastSpace);
234
235 SetDlgItemText(hwnd, uID1, szBuf);
236 SetDlgItemText(hwnd, uID2, lpBuf+LastSpace+1);
237
238 /* Return the number of used lines */
239 Ret = 2;
240 }
241 else
242 {
243 SetDlgItemText(hwnd, uID1, lpBuf);
244 Ret = 1;
245 }
246 }
247
248 HeapFree(GetProcessHeap(), 0, lpBuf);
249 }
250
251 return Ret;
252 }
253
254 static VOID MakeFloatValueString(DOUBLE* dFloatValue, LPTSTR szOutput, LPTSTR szAppend)
255 {
256 TCHAR szDecimalSeparator[4];
257
258 /* Get the decimal separator for the current locale */
259 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSeparator, sizeof(szDecimalSeparator) / sizeof(TCHAR)) > 0)
260 {
261 UCHAR uDecimals;
262 UINT uIntegral;
263
264 /* Show the value with two decimals */
265 uIntegral = (UINT)*dFloatValue;
266 uDecimals = (UCHAR)((UINT)(*dFloatValue * 100) - uIntegral * 100);
267
268 wsprintf(szOutput, _T("%u%s%02u %s"), uIntegral, szDecimalSeparator, uDecimals, szAppend);
269 }
270 }
271
272 static VOID SetProcSpeed(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
273 {
274 TCHAR szBuf[64], szHz[16];
275 DWORD BufSize = sizeof(DWORD);
276 DWORD Type = REG_SZ;
277 PROCESSOR_POWER_INFORMATION ppi;
278
279 ZeroMemory(&ppi, sizeof(ppi));
280
281 if ((CallNtPowerInformation(ProcessorInformation,
282 NULL,
283 0,
284 (PVOID)&ppi,
285 sizeof(ppi)) == STATUS_SUCCESS &&
286 ppi.CurrentMhz != 0) || RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)&ppi.CurrentMhz, &BufSize) == ERROR_SUCCESS)
287 {
288 if (ppi.CurrentMhz < 1000)
289 {
290 if (!LoadString(hApplet, IDS_MEGAHERTZ, szHz, _countof(szHz)))
291 {
292 return;
293 }
294 StringCchPrintf(szBuf, _countof(szBuf), _T("%lu %s"), ppi.CurrentMhz, szHz);
295 }
296 else
297 {
298 double flt = ppi.CurrentMhz / 1000.0;
299 if (!LoadString(hApplet, IDS_GIGAHERTZ, szHz, _countof(szHz)))
300 {
301 return;
302 }
303 MakeFloatValueString(&flt, szBuf, szHz);
304 }
305
306 SetDlgItemText(hwnd, uID, szBuf);
307 }
308 }
309
310 static VOID GetSystemInformation(HWND hwnd)
311 {
312 HKEY hKey;
313 TCHAR SysKey[] = _T("HARDWARE\\DESCRIPTION\\System");
314 TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
315 MEMORYSTATUSEX MemStat;
316 TCHAR Buf[32];
317 WCHAR SMBiosName[96];
318 INT CurMachineLine = IDC_MACHINELINE1;
319
320 /*
321 * Get hardware device name or motherboard name
322 * using information from raw SMBIOS data
323 */
324 if (GetSystemName(SMBiosName, _countof(SMBiosName)))
325 {
326 SetDlgItemText(hwnd, CurMachineLine, SMBiosName);
327 CurMachineLine++;
328 }
329 else
330 {
331 /* If SMBIOS is not available, use System Identifier */
332 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SysKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
333 {
334 SetRegTextData(hwnd, hKey, _T("Identifier"), CurMachineLine);
335 CurMachineLine++;
336 RegCloseKey(hKey);
337 }
338 }
339 /*
340 * Get Processor information
341 * although undocumented, this information is being pulled
342 * directly out of the registry instead of via setupapi as it
343 * contains all the info we need, and should remain static
344 */
345 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ProcKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
346 {
347 INT PrevMachineLine;
348
349 SetRegTextData(hwnd, hKey, _T("VendorIdentifier"), CurMachineLine);
350 CurMachineLine++;
351
352 PrevMachineLine = CurMachineLine;
353 CurMachineLine += SetProcNameString(hwnd,
354 hKey,
355 _T("ProcessorNameString"),
356 CurMachineLine,
357 CurMachineLine + 1);
358
359 if (CurMachineLine == PrevMachineLine)
360 {
361 /* TODO: Try obtaining CPU name from WMI (i.e. CIM_Processor) */
362
363 /* Brand String is not available, use Identifier instead */
364 CurMachineLine += SetProcNameString(hwnd,
365 hKey,
366 _T("Identifier"),
367 CurMachineLine,
368 CurMachineLine + 1);
369 }
370
371 SetProcSpeed(hwnd, hKey, _T("~MHz"), CurMachineLine);
372 CurMachineLine++;
373 RegCloseKey(hKey);
374 }
375
376 /* Get total physical RAM */
377 MemStat.dwLength = sizeof(MemStat);
378
379 if (GlobalMemoryStatusEx(&MemStat))
380 {
381 TCHAR szStr[32];
382 double dTotalPhys;
383
384 if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
385 {
386 UINT i = 0;
387 static const UINT uStrId[] = { IDS_GIGABYTE, IDS_TERABYTE, IDS_PETABYTE};
388
389 // We're dealing with GBs or more
390 MemStat.ullTotalPhys /= 1024 * 1024;
391
392 if (MemStat.ullTotalPhys > 1024 * 1024)
393 {
394 // We're dealing with TBs or more
395 MemStat.ullTotalPhys /= 1024;
396 i++;
397
398 if (MemStat.ullTotalPhys > 1024 * 1024)
399 {
400 // We're dealing with PBs or more
401 MemStat.ullTotalPhys /= 1024;
402 i++;
403
404 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
405 }
406 else
407 {
408 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
409 }
410 }
411 else
412 {
413 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
414 }
415
416 LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
417 MakeFloatValueString(&dTotalPhys, Buf, szStr);
418 }
419 else
420 {
421 // We're dealing with MBs, don't show any decimals
422 LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
423 wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
424 }
425
426 SetDlgItemText(hwnd, CurMachineLine, Buf);
427 }
428 }
429
430 static VOID GetSystemVersion(HWND hwnd)
431 {
432 HWND hRosVersion;
433 SIZE_T lenStr, lenVersion;
434 PCWSTR pwszVersion = L" " TEXT(KERNEL_VERSION_RC);
435 PWSTR pwszStr;
436
437 lenVersion = wcslen(pwszVersion);
438 if (lenVersion == 0)
439 {
440 return;
441 }
442
443 hRosVersion = GetDlgItem(hwnd, IDC_ROSVERSION);
444 if (!hRosVersion)
445 {
446 return;
447 }
448 lenStr = GetWindowTextLengthW(hRosVersion);
449 lenStr += lenVersion + 1;
450 pwszStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
451 if (!pwszStr)
452 {
453 return;
454 }
455 GetWindowText(hRosVersion, pwszStr, lenStr);
456
457 StringCchCatW(pwszStr, lenStr, pwszVersion);
458 SetWindowText(hRosVersion, pwszStr);
459
460 HeapFree(GetProcessHeap(), 0, pwszStr);
461 }
462
463 ULONGLONG GetSecondsQPC(VOID)
464 {
465 LARGE_INTEGER Counter, Frequency;
466
467 QueryPerformanceCounter(&Counter);
468 QueryPerformanceFrequency(&Frequency);
469
470 return Counter.QuadPart / Frequency.QuadPart;
471 }
472
473 ULONGLONG GetSeconds(VOID)
474 {
475 ULONGLONG (WINAPI * pGetTickCount64)(VOID);
476 ULONGLONG Ticks64;
477 HMODULE hModule = GetModuleHandleW(L"kernel32.dll");
478
479 pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
480 if (pGetTickCount64)
481 {
482 return pGetTickCount64() / 1000;
483 }
484
485 hModule = LoadLibraryW(L"kernel32_vista.dll");
486
487 if (!hModule)
488 {
489 return GetSecondsQPC();
490 }
491
492 pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
493
494 if (pGetTickCount64)
495 {
496 Ticks64 = pGetTickCount64() / 1000;
497 }
498 else
499 {
500 Ticks64 = GetSecondsQPC();
501 }
502
503 FreeLibrary(hModule);
504 return Ticks64;
505 }
506
507 VOID GetSystemUptime(HWND hwnd)
508 {
509 HWND hRosUptime;
510 WCHAR szBuf[64], szStr[64];
511 ULONG cSeconds;
512
513 hRosUptime = GetDlgItem(hwnd, IDC_UPTIME);
514 if (!hRosUptime)
515 {
516 return;
517 }
518 if (!LoadStringW(hApplet, IDS_UPTIME_FORMAT, szStr, _countof(szStr)))
519 {
520 return;
521 }
522 cSeconds = GetSeconds();
523 StringCchPrintfW(szBuf, _countof(szBuf), szStr,
524 cSeconds / (60*60*24),
525 (cSeconds / (60*60)) % 24,
526 (cSeconds / 60) % 60,
527 cSeconds % 60);
528
529 SetWindowTextW(hRosUptime, szBuf);
530 }
531
532 /* Property page dialog callback */
533 INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
534 {
535 UNREFERENCED_PARAMETER(lParam);
536 UNREFERENCED_PARAMETER(wParam);
537
538 switch (uMsg)
539 {
540 case WM_INITDIALOG:
541 pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
542 if (pImgInfo == NULL)
543 {
544 EndDialog(hwndDlg, 0);
545 return FALSE;
546 }
547
548 InitLogo(hwndDlg);
549 SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWLP_WNDPROC, (LONG_PTR)RosImageProc);
550 GetSystemInformation(hwndDlg);
551 GetSystemVersion(hwndDlg);
552 GetSystemUptime(hwndDlg);
553 break;
554
555 case WM_DESTROY:
556 HeapFree(GetProcessHeap(), 0, pImgInfo);
557 break;
558
559 case WM_COMMAND:
560 if (LOWORD(wParam) == IDC_LICENCE)
561 {
562 DialogBox(hApplet, MAKEINTRESOURCE(IDD_LICENCE), hwndDlg, LicenceDlgProc);
563
564 return TRUE;
565 }
566 break;
567
568 case WM_DRAWITEM:
569 {
570 LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
571
572 if (lpDrawItem->CtlID == IDC_ROSIMG)
573 {
574 HDC hdcMem;
575 LONG left;
576
577 /* Position image in centre of dialog */
578 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
579
580 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
581 if (hdcMem != NULL)
582 {
583 SelectObject(hdcMem, pImgInfo->hBitmap);
584 BitBlt(lpDrawItem->hDC,
585 left,
586 lpDrawItem->rcItem.top,
587 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
588 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
589 hdcMem,
590 0,
591 0,
592 SRCCOPY);
593 DeleteDC(hdcMem);
594 }
595 }
596 return TRUE;
597 }
598
599 case WM_NOTIFY:
600 {
601 NMHDR *nmhdr = (NMHDR *)lParam;
602
603 if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
604 {
605 PNMLINK nml = (PNMLINK)nmhdr;
606
607 ShellExecuteW(hwndDlg, L"open", nml->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
608 }
609 break;
610 }
611
612 }
613
614 return FALSE;
615 }