xref: /reactos/dll/cpl/sysdm/general.c (revision 45fd48bd)
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];
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             wsprintf(szBuf, _T("%lu MHz"), ppi.CurrentMhz);
409         }
410         else
411         {
412             double flt = ppi.CurrentMhz / 1000.0;
413             MakeFloatValueString(&flt, szBuf, _T("GHz"));
414         }
415 
416         SetDlgItemText(hwnd, uID, szBuf);
417     }
418 }
419 
420 static VOID GetSystemInformation(HWND hwnd)
421 {
422     HKEY hKey;
423     TCHAR ProcKey[] = _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
424     MEMORYSTATUSEX MemStat;
425     TCHAR Buf[32];
426     INT CurMachineLine = IDC_MACHINELINE1;
427 
428     /*
429      * Get Processor information
430      * although undocumented, this information is being pulled
431      * directly out of the registry instead of via setupapi as it
432      * contains all the info we need, and should remain static
433      */
434     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ProcKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
435     {
436         SetRegTextData(hwnd, hKey, _T("VendorIdentifier"), CurMachineLine);
437         CurMachineLine++;
438 
439         CurMachineLine += SetProcNameString(hwnd,
440                                             hKey,
441                                             _T("ProcessorNameString"),
442                                             CurMachineLine,
443                                             CurMachineLine + 1);
444 
445         SetProcSpeed(hwnd, hKey, _T("~MHz"), CurMachineLine);
446         CurMachineLine++;
447     }
448 
449     /* Get total physical RAM */
450     MemStat.dwLength = sizeof(MemStat);
451 
452     if (GlobalMemoryStatusEx(&MemStat))
453     {
454         TCHAR szStr[32];
455         double dTotalPhys;
456 
457         if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
458         {
459             UINT i = 0;
460             static const UINT uStrId[] = { IDS_GIGABYTE, IDS_TERABYTE, IDS_PETABYTE};
461 
462             // We're dealing with GBs or more
463             MemStat.ullTotalPhys /= 1024 * 1024;
464 
465             if (MemStat.ullTotalPhys > 1024 * 1024)
466             {
467                 // We're dealing with TBs or more
468                 MemStat.ullTotalPhys /= 1024;
469                 i++;
470 
471                 if (MemStat.ullTotalPhys > 1024 * 1024)
472                 {
473                     // We're dealing with PBs or more
474                     MemStat.ullTotalPhys /= 1024;
475                     i++;
476 
477                     dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
478                 }
479                 else
480                 {
481                     dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
482                 }
483             }
484             else
485             {
486                 dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
487             }
488 
489             LoadString(hApplet, uStrId[i], szStr, sizeof(szStr) / sizeof(TCHAR));
490             MakeFloatValueString(&dTotalPhys, Buf, szStr);
491         }
492         else
493         {
494             // We're dealing with MBs, don't show any decimals
495             LoadString(hApplet, IDS_MEGABYTE, szStr, sizeof(szStr) / sizeof(TCHAR));
496             wsprintf(Buf, _T("%u %s"), (UINT)MemStat.ullTotalPhys / 1024 / 1024, szStr);
497         }
498 
499         SetDlgItemText(hwnd, CurMachineLine, Buf);
500     }
501 }
502 
503 static VOID GetSystemVersion(HWND hwnd)
504 {
505     HWND hRosVersion;
506     SIZE_T lenStr, lenVersion;
507     PCWSTR pwszVersion = L" " TEXT(KERNEL_VERSION_RC);
508     PWSTR pwszStr;
509 
510     lenVersion = wcslen(pwszVersion);
511     if (lenVersion == 0)
512     {
513         return;
514     }
515 
516     hRosVersion = GetDlgItem(hwnd, IDC_ROSVERSION);
517     if (!hRosVersion)
518     {
519         return;
520     }
521     lenStr = GetWindowTextLengthW(hRosVersion);
522     lenStr += lenVersion + 1;
523     pwszStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
524     if (!pwszStr)
525     {
526         return;
527     }
528     GetWindowText(hRosVersion, pwszStr, lenStr);
529 
530     StringCchCatW(pwszStr, lenStr, pwszVersion);
531     SetWindowText(hRosVersion, pwszStr);
532 
533     HeapFree(GetProcessHeap(), 0, pwszStr);
534 }
535 
536 /* Property page dialog callback */
537 INT_PTR CALLBACK GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
538 {
539     UNREFERENCED_PARAMETER(lParam);
540     UNREFERENCED_PARAMETER(wParam);
541 
542     switch (uMsg)
543     {
544         case WM_INITDIALOG:
545             pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
546             if (pImgInfo == NULL)
547             {
548                 EndDialog(hwndDlg, 0);
549                 return FALSE;
550             }
551 
552             InitLogo(hwndDlg);
553             SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ROSIMG), GWLP_WNDPROC, (LONG_PTR)RosImageProc);
554             GetSystemInformation(hwndDlg);
555             GetSystemVersion(hwndDlg);
556             break;
557 
558         case WM_DESTROY:
559             HeapFree(GetProcessHeap(), 0, pImgInfo);
560             break;
561 
562         case WM_COMMAND:
563             if (LOWORD(wParam) == IDC_LICENCE)
564             {
565                 DialogBox(hApplet, MAKEINTRESOURCE(IDD_LICENCE), hwndDlg, LicenceDlgProc);
566 
567                 return TRUE;
568             }
569             break;
570 
571         case WM_DRAWITEM:
572         {
573             LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
574 
575             if (lpDrawItem->CtlID == IDC_ROSIMG)
576             {
577                 HDC hdcMem;
578                 LONG left;
579 
580                 /* Position image in centre of dialog */
581                 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
582 
583                 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
584                 if (hdcMem != NULL)
585                 {
586                     SelectObject(hdcMem, pImgInfo->hBitmap);
587                     BitBlt(lpDrawItem->hDC,
588                            left,
589                            lpDrawItem->rcItem.top,
590                            lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
591                            lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
592                            hdcMem,
593                            0,
594                            0,
595                            SRCCOPY);
596                     DeleteDC(hdcMem);
597                 }
598             }
599             return TRUE;
600         }
601 
602         case WM_NOTIFY:
603         {
604             NMHDR *nmhdr = (NMHDR *)lParam;
605 
606             if (nmhdr->idFrom == IDC_ROSHOMEPAGE_LINK && nmhdr->code == NM_CLICK)
607             {
608                 PNMLINK nml = (PNMLINK)nmhdr;
609 
610                 ShellExecuteW(hwndDlg, L"open", nml->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
611             }
612             break;
613         }
614 
615     }
616 
617     return FALSE;
618 }
619