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