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