xref: /reactos/dll/cpl/sysdm/general.c (revision ea6e7740)
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, hDCLogo, hDCMask;
66     HBITMAP hMask = NULL, hLogo = NULL;
67     HBITMAP hAlphaLogo = NULL;
68     COLORREF *pBits;
69     INT line, column;
70 
71     hDC = GetDC(hwndDlg);
72     hDCLogo = CreateCompatibleDC(NULL);
73     hDCMask = CreateCompatibleDC(NULL);
74 
75     if (hDC == NULL || hDCLogo == NULL || hDCMask == NULL)
76         goto Cleanup;
77 
78     ZeroMemory(pImgInfo, sizeof(*pImgInfo));
79     ZeroMemory(&bmpi, sizeof(bmpi));
80 
81     hLogo = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSBMP), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
82     hMask = (HBITMAP)LoadImageW(hApplet, MAKEINTRESOURCEW(IDB_ROSMASK), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
83 
84     if (hLogo == NULL || hMask == NULL)
85         goto Cleanup;
86 
87     GetObject(hLogo, sizeof(logoBitmap), &logoBitmap);
88     GetObject(hMask, sizeof(maskBitmap), &maskBitmap);
89 
90     if (logoBitmap.bmHeight != maskBitmap.bmHeight || logoBitmap.bmWidth != maskBitmap.bmWidth)
91         goto Cleanup;
92 
93     bmpi.bmiHeader.biSize = sizeof(BITMAPINFO);
94     bmpi.bmiHeader.biWidth = logoBitmap.bmWidth;
95     bmpi.bmiHeader.biHeight = logoBitmap.bmHeight;
96     bmpi.bmiHeader.biPlanes = 1;
97     bmpi.bmiHeader.biBitCount = 32;
98     bmpi.bmiHeader.biCompression = BI_RGB;
99     bmpi.bmiHeader.biSizeImage = 4 * logoBitmap.bmWidth * logoBitmap.bmHeight;
100 
101     /* Create a premultiplied bitmap */
102     hAlphaLogo = CreateDIBSection(hDC, &bmpi, DIB_RGB_COLORS, (PVOID*)&pBits, 0, 0);
103     if (!hAlphaLogo)
104         goto Cleanup;
105 
106     SelectObject(hDCLogo, hLogo);
107     SelectObject(hDCMask, hMask);
108 
109     for (line = logoBitmap.bmHeight - 1; line >= 0; line--)
110     {
111         for (column = 0; column < logoBitmap.bmWidth; column++)
112         {
113             COLORREF alpha = GetPixel(hDCMask, column, line) & 0xFF;
114             COLORREF Color = GetPixel(hDCLogo, column, line);
115             DWORD r, g, b;
116 
117             r = GetRValue(Color) * alpha / 255;
118             g = GetGValue(Color) * alpha / 255;
119             b = GetBValue(Color) * alpha / 255;
120 
121             *pBits++ = b | (g << 8) | (r << 16) | (alpha << 24);
122         }
123     }
124 
125     pImgInfo->hBitmap = hAlphaLogo;
126     pImgInfo->cxSource = logoBitmap.bmWidth;
127     pImgInfo->cySource = logoBitmap.bmHeight;
128     pImgInfo->iBits = logoBitmap.bmBitsPixel;
129     pImgInfo->iPlanes = logoBitmap.bmPlanes;
130 
131 Cleanup:
132     if (hMask != NULL) DeleteObject(hMask);
133     if (hLogo != NULL) DeleteObject(hLogo);
134     if (hDCMask != NULL) DeleteDC(hDCMask);
135     if (hDCLogo != NULL) DeleteDC(hDCLogo);
136     if (hDC != NULL) ReleaseDC(hwndDlg, hDC);
137 }
138 
139 LRESULT CALLBACK RosImageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
140 {
141     static UINT timerid = 0, top = 0, offset;
142     static HBITMAP hCreditsBitmap;
143 
144     switch (uMsg)
145     {
146         case WM_LBUTTONDBLCLK:
147             if (wParam & (MK_CONTROL | MK_SHIFT))
148             {
149                 if (timerid == 0)
150                 {
151                     HDC hDC;
152                     HDC hCreditsDC = NULL, hLogoDC = NULL;
153                     HFONT hFont = NULL;
154                     NONCLIENTMETRICS ncm;
155                     RECT rcCredits;
156                     TCHAR szCredits[2048];
157                     INT iDevsHeight;
158 
159                     hDC = GetDC(NULL);
160                     if (hDC == NULL)
161                         goto Cleanup;
162 
163                     top = 0;
164                     offset = 0;
165 
166                     hCreditsDC = CreateCompatibleDC(hDC);
167                     hLogoDC = CreateCompatibleDC(hCreditsDC);
168 
169                     if (hCreditsDC == NULL || hLogoDC == NULL)
170                         goto Cleanup;
171 
172                     SetRect(&rcCredits, 0, 0, 0, 0);
173 
174                     ncm.cbSize = sizeof(NONCLIENTMETRICS);
175                     SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
176 
177                     hFont = CreateFontIndirect(&ncm.lfMessageFont);
178                     if (!hFont)
179                         goto Cleanup;
180                     SelectObject(hCreditsDC, hFont);
181 
182                     LoadString(hApplet, IDS_DEVS, szCredits, sizeof(szCredits) / sizeof(TCHAR));
183                     DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CALCRECT);
184 
185                     iDevsHeight = rcCredits.bottom - rcCredits.top;
186 
187                     hCreditsBitmap = CreateBitmap(pImgInfo->cxSource, (2 * pImgInfo->cySource) + iDevsHeight + 1, pImgInfo->iPlanes, pImgInfo->iBits, NULL);
188                     if (!hCreditsBitmap)
189                         goto Cleanup;
190 
191                     SelectObject(hLogoDC, pImgInfo->hBitmap);
192                     SelectObject(hCreditsDC, hCreditsBitmap);
193 
194                     offset += pImgInfo->cySource;
195 
196                     SetRect(&rcCredits, 0, 0, pImgInfo->cxSource, (2 * pImgInfo->cySource) + iDevsHeight + 1);
197                     FillRect(hCreditsDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
198 
199                     SetRect(&rcCredits, 0, offset, pImgInfo->cxSource, offset + iDevsHeight + 1);
200                     SetBkMode(hCreditsDC, TRANSPARENT);
201 
202                     OffsetRect(&rcCredits, 1, 1);
203                     SetTextColor(hCreditsDC, GetSysColor(COLOR_BTNSHADOW));
204                     DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CENTER);
205 
206                     OffsetRect(&rcCredits, -1, -1);
207                     SetTextColor(hCreditsDC, GetSysColor(COLOR_WINDOWTEXT));
208                     DrawText(hCreditsDC, szCredits, -1, &rcCredits, DT_CENTER);
209 
210                     offset += iDevsHeight;
211 
212                     AlphaBlend(hCreditsDC, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, hLogoDC, 0,  0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
213                     AlphaBlend(hCreditsDC, 0, offset, pImgInfo->cxSource, pImgInfo->cySource, hLogoDC, 0,  0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
214 
215                     timerid = SetTimer(hwnd, 1, ANIM_TIME, NULL);
216 
217 Cleanup:
218                     if (hFont != NULL) DeleteObject(hFont);
219                     if (hLogoDC != NULL) DeleteDC(hLogoDC);
220                     if (hCreditsDC != NULL) DeleteDC(hCreditsDC);
221                     if (hDC != NULL) ReleaseDC(NULL, hDC);
222                 }
223             }
224             break;
225         case WM_LBUTTONDOWN:
226             if (timerid)
227             {
228                 RECT rcCredits;
229                 HDC hDC = GetDC(hwnd);
230                 if (hDC != NULL)
231                 {
232                     GetClientRect(hwnd, &rcCredits);
233                     SetRect(&rcCredits, 0, 0, rcCredits.right, pImgInfo->cySource);
234                     FillRect(hDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
235                     ReleaseDC(hwnd, hDC);
236                 }
237                 KillTimer(hwnd, timerid);
238                 if (hCreditsBitmap != NULL)
239                     DeleteObject(hCreditsBitmap);
240 
241                 InvalidateRect(hwnd, NULL, FALSE);
242                 top = 0;
243                 timerid = 0;
244             }
245             break;
246         case WM_TIMER:
247             top += ANIM_STEP;
248 
249             if (top > offset)
250             {
251                 RECT rcCredits;
252                 HDC hDC = GetDC(hwnd);
253                 if (hDC != NULL)
254                 {
255                     GetClientRect(hwnd, &rcCredits);
256                     SetRect(&rcCredits, 0, 0, rcCredits.right, pImgInfo->cySource);
257                     FillRect(hDC, &rcCredits, GetSysColorBrush(COLOR_3DFACE));
258                     ReleaseDC(hwnd, hDC);
259                 }
260                 KillTimer(hwnd, timerid);
261                 if (hCreditsBitmap != NULL)
262                     DeleteObject(hCreditsBitmap);
263 
264                 top = 0;
265                 timerid = 0;
266             }
267 
268             InvalidateRect(hwnd, NULL, FALSE);
269             break;
270         case WM_PAINT:
271         {
272             PAINTSTRUCT PS;
273             HDC hdcMem, hdc;
274             LONG left;
275 
276             hdc = wParam != 0 ? (HDC)wParam : BeginPaint(hwnd, &PS);
277 
278             GetClientRect(hwnd, &PS.rcPaint);
279 
280             /* Position image in center of dialog */
281             left = (PS.rcPaint.right - pImgInfo->cxSource) / 2;
282             hdcMem = CreateCompatibleDC(hdc);
283 
284             if (hdcMem != NULL)
285             {
286                 if(timerid != 0)
287                 {
288                     SelectObject(hdcMem, hCreditsBitmap);
289                     BitBlt(hdc, left, PS.rcPaint.top, PS.rcPaint.right - PS.rcPaint.left, PS.rcPaint.top + pImgInfo->cySource, hdcMem, 0, top, SRCCOPY);
290                 }
291                 else
292                 {
293                     SelectObject(hdcMem, pImgInfo->hBitmap);
294                     AlphaBlend(hdc, left, PS.rcPaint.top, pImgInfo->cxSource, pImgInfo->cySource, hdcMem, 0, 0, pImgInfo->cxSource, pImgInfo->cySource, BlendFunc);
295                 }
296 
297                 DeleteDC(hdcMem);
298             }
299 
300             if (wParam == 0)
301                 EndPaint(hwnd,&PS);
302             break;
303         }
304     }
305     return TRUE;
306 }
307 
308 static VOID SetRegTextData(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
309 {
310     LPTSTR lpBuf = NULL;
311     DWORD BufSize = 0;
312     DWORD Type;
313 
314     if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
315     {
316         lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
317 
318         if (!lpBuf)
319             return;
320 
321         if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
322             SetDlgItemText(hwnd, uID, lpBuf);
323 
324         HeapFree(GetProcessHeap(), 0, lpBuf);
325     }
326 }
327 
328 static INT SetProcNameString(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID1, UINT uID2)
329 {
330     LPTSTR lpBuf = NULL;
331     DWORD BufSize = 0;
332     DWORD Type;
333     INT Ret = 0;
334     TCHAR szBuf[31];
335     TCHAR* szLastSpace;
336     INT LastSpace = 0;
337 
338     if (RegQueryValueEx(hKey, Value, NULL, &Type, NULL, &BufSize) == ERROR_SUCCESS)
339     {
340         lpBuf = HeapAlloc(GetProcessHeap(), 0, BufSize);
341 
342         if (!lpBuf)
343             return 0;
344 
345         if (RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)lpBuf, &BufSize) == ERROR_SUCCESS)
346         {
347             if (BufSize > ((30 + 1) * sizeof(TCHAR)))
348             {
349                 /* Wrap the Processor Name String like XP does:                           *
350                 *   - Take the first 30 characters and look for the last space.          *
351                 *     Then wrap the string after this space.                             *
352                 *   - If no space is found, wrap the string after character 30.          *
353                 *                                                                        *
354                 * For example the Processor Name String of a Pentium 4 is right-aligned. *
355                 * With this wrapping the first line looks centered.                      */
356 
357                 _tcsncpy(szBuf, lpBuf, 30);
358                 szBuf[30] = 0;
359                 szLastSpace = _tcsrchr(szBuf, ' ');
360 
361                 if (szLastSpace == 0)
362                 {
363                     LastSpace = 30;
364                 }
365                 else
366                 {
367                     LastSpace = (szLastSpace - szBuf);
368                     szBuf[LastSpace] = 0;
369                 }
370 
371                 _tcsncpy(szBuf, lpBuf, LastSpace);
372 
373                 SetDlgItemText(hwnd, uID1, szBuf);
374                 SetDlgItemText(hwnd, uID2, lpBuf+LastSpace+1);
375 
376                 /* Return the number of used lines */
377                 Ret = 2;
378             }
379             else
380             {
381                 SetDlgItemText(hwnd, uID1, lpBuf);
382                 Ret = 1;
383             }
384         }
385 
386         HeapFree(GetProcessHeap(), 0, lpBuf);
387     }
388 
389     return Ret;
390 }
391 
392 static VOID MakeFloatValueString(DOUBLE* dFloatValue, LPTSTR szOutput, LPTSTR szAppend)
393 {
394     TCHAR szDecimalSeparator[4];
395 
396     /* Get the decimal separator for the current locale */
397     if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSeparator, sizeof(szDecimalSeparator) / sizeof(TCHAR)) > 0)
398     {
399         UCHAR uDecimals;
400         UINT uIntegral;
401 
402         /* Show the value with two decimals */
403         uIntegral = (UINT)*dFloatValue;
404         uDecimals = (UCHAR)((UINT)(*dFloatValue * 100) - uIntegral * 100);
405 
406         wsprintf(szOutput, _T("%u%s%02u %s"), uIntegral, szDecimalSeparator, uDecimals, szAppend);
407     }
408 }
409 
410 static VOID SetProcSpeed(HWND hwnd, HKEY hKey, LPTSTR Value, UINT uID)
411 {
412     TCHAR szBuf[64], szHz[16];
413     DWORD BufSize = sizeof(DWORD);
414     DWORD Type = REG_SZ;
415     PROCESSOR_POWER_INFORMATION ppi;
416 
417     ZeroMemory(&ppi, sizeof(ppi));
418 
419     if ((CallNtPowerInformation(ProcessorInformation,
420                                 NULL,
421                                 0,
422                                 (PVOID)&ppi,
423                                 sizeof(ppi)) == STATUS_SUCCESS &&
424          ppi.CurrentMhz != 0) || RegQueryValueEx(hKey, Value, NULL, &Type, (PBYTE)&ppi.CurrentMhz, &BufSize) == ERROR_SUCCESS)
425     {
426         if (ppi.CurrentMhz < 1000)
427         {
428             if (!LoadString(hApplet, IDS_MEGAHERTZ, szHz, _countof(szHz)))
429             {
430                 return;
431             }
432             StringCchPrintf(szBuf, _countof(szBuf), _T("%lu %s"), ppi.CurrentMhz, szHz);
433         }
434         else
435         {
436             double flt = ppi.CurrentMhz / 1000.0;
437             if (!LoadString(hApplet, IDS_GIGAHERTZ, szHz, _countof(szHz)))
438             {
439                 return;
440             }
441             MakeFloatValueString(&flt, szBuf, szHz);
442         }
443 
444         SetDlgItemText(hwnd, uID, szBuf);
445     }
446 }
447 
448 static VOID GetSystemInformation(HWND hwnd)
449 {
450     HKEY hKey;
451     TCHAR SysKey[] = _T("HARDWARE\\DESCRIPTION\\System");
452     TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
453     MEMORYSTATUSEX MemStat;
454     TCHAR Buf[32];
455     WCHAR SMBiosName[96];
456     INT CurMachineLine = IDC_MACHINELINE1;
457 
458     /*
459      * Get hardware device name or motherboard name
460      * using information from raw SMBIOS data
461      */
462     if (GetSystemName(SMBiosName, _countof(SMBiosName)))
463     {
464         SetDlgItemText(hwnd, CurMachineLine, SMBiosName);
465         CurMachineLine++;
466     }
467     else
468     {
469         /* If SMBIOS is not available, use System Identifier */
470         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, SysKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
471         {
472             SetRegTextData(hwnd, hKey, _T("Identifier"), CurMachineLine);
473             CurMachineLine++;
474             RegCloseKey(hKey);
475         }
476     }
477     /*
478      * Get Processor information
479      * although undocumented, this information is being pulled
480      * directly out of the registry instead of via setupapi as it
481      * contains all the info we need, and should remain static
482      */
483     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ProcKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
484     {
485         INT PrevMachineLine;
486 
487         SetRegTextData(hwnd, hKey, _T("VendorIdentifier"), CurMachineLine);
488         CurMachineLine++;
489 
490         PrevMachineLine = CurMachineLine;
491         CurMachineLine += SetProcNameString(hwnd,
492                                             hKey,
493                                             _T("ProcessorNameString"),
494                                             CurMachineLine,
495                                             CurMachineLine + 1);
496 
497         if (CurMachineLine == PrevMachineLine)
498         {
499             /* TODO: Try obtaining CPU name from WMI (i.e. CIM_Processor) */
500 
501             /* Brand String is not available, use Identifier instead */
502             CurMachineLine += SetProcNameString(hwnd,
503                                                 hKey,
504                                                 _T("Identifier"),
505                                                 CurMachineLine,
506                                                 CurMachineLine + 1);
507         }
508 
509         SetProcSpeed(hwnd, hKey, _T("~MHz"), CurMachineLine);
510         CurMachineLine++;
511         RegCloseKey(hKey);
512     }
513 
514     /* Get total physical RAM */
515     MemStat.dwLength = sizeof(MemStat);
516 
517     if (GlobalMemoryStatusEx(&MemStat))
518     {
519         TCHAR szStr[32];
520         double dTotalPhys;
521 
522         if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
523         {
524             UINT i = 0;
525             static const UINT uStrId[] = { IDS_GIGABYTE, IDS_TERABYTE, IDS_PETABYTE};
526 
527             // We're dealing with GBs or more
528             MemStat.ullTotalPhys /= 1024 * 1024;
529 
530             if (MemStat.ullTotalPhys > 1024 * 1024)
531             {
532                 // We're dealing with TBs or more
533                 MemStat.ullTotalPhys /= 1024;
534                 i++;
535 
536                 if (MemStat.ullTotalPhys > 1024 * 1024)
537                 {
538                     // We're dealing with PBs or more
539                     MemStat.ullTotalPhys /= 1024;
540                     i++;
541 
542                     dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
543                 }
544                 else
545                 {
546                     dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
547                 }
548             }
549             else
550             {
551                 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
552             }
553 
554             LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
555             MakeFloatValueString(&dTotalPhys, Buf, szStr);
556         }
557         else
558         {
559             // We're dealing with MBs, don't show any decimals
560             LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
561             wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
562         }
563 
564         SetDlgItemText(hwnd, CurMachineLine, Buf);
565     }
566 }
567 
568 static VOID GetSystemVersion(HWND hwnd)
569 {
570     HWND hRosVersion;
571     SIZE_T lenStr, lenVersion;
572     PCWSTR pwszVersion = L" " TEXT(KERNEL_VERSION_RC);
573     PWSTR pwszStr;
574 
575     lenVersion = wcslen(pwszVersion);
576     if (lenVersion == 0)
577     {
578         return;
579     }
580 
581     hRosVersion = GetDlgItem(hwnd, IDC_ROSVERSION);
582     if (!hRosVersion)
583     {
584         return;
585     }
586     lenStr = GetWindowTextLengthW(hRosVersion);
587     lenStr += lenVersion + 1;
588     pwszStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
589     if (!pwszStr)
590     {
591         return;
592     }
593     GetWindowText(hRosVersion, pwszStr, lenStr);
594 
595     StringCchCatW(pwszStr, lenStr, pwszVersion);
596     SetWindowText(hRosVersion, pwszStr);
597 
598     HeapFree(GetProcessHeap(), 0, pwszStr);
599 }
600 
601 ULONGLONG GetSecondsQPC(VOID)
602 {
603     LARGE_INTEGER Counter, Frequency;
604 
605     QueryPerformanceCounter(&Counter);
606     QueryPerformanceFrequency(&Frequency);
607 
608     return Counter.QuadPart / Frequency.QuadPart;
609 }
610 
611 ULONGLONG GetSeconds(VOID)
612 {
613     ULONGLONG (WINAPI * pGetTickCount64)(VOID);
614     ULONGLONG Ticks64;
615     HMODULE hModule = GetModuleHandleW(L"kernel32.dll");
616 
617     pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
618     if (pGetTickCount64)
619     {
620         return pGetTickCount64() / 1000;
621     }
622 
623     hModule = LoadLibraryW(L"kernel32_vista.dll");
624 
625     if (!hModule)
626     {
627         return GetSecondsQPC();
628     }
629 
630     pGetTickCount64 = (PVOID)GetProcAddress(hModule, "GetTickCount64");
631 
632     if (pGetTickCount64)
633     {
634         Ticks64 = pGetTickCount64() / 1000;
635     }
636     else
637     {
638         Ticks64 = GetSecondsQPC();
639     }
640 
641     FreeLibrary(hModule);
642     return Ticks64;
643 }
644 
645 VOID GetSystemUptime(HWND hwnd)
646 {
647     HWND hRosUptime;
648     WCHAR szBuf[64], szStr[64];
649     ULONG cSeconds;
650 
651     hRosUptime = GetDlgItem(hwnd, IDC_UPTIME);
652     if (!hRosUptime)
653     {
654         return;
655     }
656     if (!LoadStringW(hApplet, IDS_UPTIME_FORMAT, szStr, _countof(szStr)))
657     {
658         return;
659     }
660     cSeconds = GetSeconds();
661     StringCchPrintfW(szBuf, _countof(szBuf), szStr,
662                      cSeconds / (60*60*24),
663                      (cSeconds / (60*60)) % 24,
664                      (cSeconds / 60) % 60,
665                      cSeconds % 60);
666 
667     SetWindowTextW(hRosUptime, szBuf);
668 }
669 
670 /* Property page dialog callback */
671 INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
672 {
673     UNREFERENCED_PARAMETER(lParam);
674     UNREFERENCED_PARAMETER(wParam);
675 
676     switch (uMsg)
677     {
678         case WM_INITDIALOG:
679             pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
680             if (pImgInfo == NULL)
681             {
682                 EndDialog(hwndDlg, 0);
683                 return FALSE;
684             }
685 
686             InitLogo(hwndDlg);
687             SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWLP_WNDPROC, (LONG_PTR)RosImageProc);
688             GetSystemInformation(hwndDlg);
689             GetSystemVersion(hwndDlg);
690             GetSystemUptime(hwndDlg);
691             break;
692 
693         case WM_DESTROY:
694             HeapFree(GetProcessHeap(), 0, pImgInfo);
695             break;
696 
697         case WM_COMMAND:
698             if (LOWORD(wParam) == IDC_LICENCE)
699             {
700                 DialogBox(hApplet, MAKEINTRESOURCE(IDD_LICENCE), hwndDlg, LicenceDlgProc);
701 
702                 return TRUE;
703             }
704             break;
705 
706         case WM_DRAWITEM:
707         {
708             LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
709 
710             if (lpDrawItem->CtlID == IDC_ROSIMG)
711             {
712                 HDC hdcMem;
713                 LONG left;
714 
715                 /* Position image in centre of dialog */
716                 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
717 
718                 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
719                 if (hdcMem != NULL)
720                 {
721                     SelectObject(hdcMem, pImgInfo->hBitmap);
722                     BitBlt(lpDrawItem->hDC,
723                            left,
724                            lpDrawItem->rcItem.top,
725                            lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
726                            lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
727                            hdcMem,
728                            0,
729                            0,
730                            SRCCOPY);
731                     DeleteDC(hdcMem);
732                 }
733             }
734             return TRUE;
735         }
736 
737         case WM_NOTIFY:
738         {
739             NMHDR *nmhdr = (NMHDR *)lParam;
740 
741             if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
742             {
743                 PNMLINK nml = (PNMLINK)nmhdr;
744 
745                 ShellExecuteW(hwndDlg, L"open", nml->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
746             }
747             break;
748         }
749 
750     }
751 
752     return FALSE;
753 }
754