xref: /reactos/dll/win32/syssetup/wizard.c (revision 22577aed)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         System setup
4  * FILE:            dll/win32/syssetup/wizard.c
5  * PURPOSE:         GUI controls
6  * PROGRAMMERS:     Eric Kohl
7  *                  Pierre Schweitzer <heis_spiter@hotmail.com>
8  *                  Ismael Ferreras Morezuelas <swyterzone+ros@gmail.com>
9  *                  Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  *                  Oleg Dubinskiy <oleg.dubinskij30@gmail.com>
11  */
12 
13 /* INCLUDES *****************************************************************/
14 
15 #include "precomp.h"
16 
17 #include <stdlib.h>
18 #include <time.h>
19 #include <winnls.h>
20 #include <windowsx.h>
21 #include <wincon.h>
22 #include <shlobj.h>
23 #include <shlwapi.h>
24 #include <tzlib.h>
25 #include <strsafe.h>
26 
27 #define NDEBUG
28 #include <debug.h>
29 
30 typedef struct _REGISTRATIONDATA
31 {
32     HWND hwndDlg;
33     ULONG DllCount;
34     ULONG Registered;
35     PVOID DefaultContext;
36     PREGISTRATIONNOTIFY pNotify;
37 } REGISTRATIONDATA, *PREGISTRATIONDATA;
38 
39 typedef struct _TIMEZONE_ENTRY
40 {
41     struct _TIMEZONE_ENTRY *Prev;
42     struct _TIMEZONE_ENTRY *Next;
43     WCHAR Description[128]; /* 'Display' */
44     WCHAR StandardName[32]; /* 'Std' */
45     WCHAR DaylightName[32]; /* 'Dlt' */
46     REG_TZI_FORMAT TimezoneInfo; /* 'TZI' */
47     ULONG Index;
48 } TIMEZONE_ENTRY, *PTIMEZONE_ENTRY;
49 
50 
51 /* FUNCTIONS ****************************************************************/
52 
53 extern void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
54 
55 
56 static VOID
57 CenterWindow(HWND hWnd)
58 {
59     HWND hWndParent;
60     RECT rcParent;
61     RECT rcWindow;
62 
63     hWndParent = GetParent(hWnd);
64     if (hWndParent == NULL)
65         hWndParent = GetDesktopWindow();
66 
67     GetWindowRect(hWndParent, &rcParent);
68     GetWindowRect(hWnd, &rcWindow);
69 
70     SetWindowPos(hWnd,
71                  HWND_TOP,
72                  ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2,
73                  ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2,
74                  0,
75                  0,
76                  SWP_NOSIZE);
77 }
78 
79 
80 static HFONT
81 CreateTitleFont(VOID)
82 {
83     LOGFONTW LogFont = {0};
84     HDC hdc;
85     HFONT hFont;
86 
87     LogFont.lfWeight = FW_BOLD;
88     wcscpy(LogFont.lfFaceName, L"MS Shell Dlg");
89 
90     hdc = GetDC(NULL);
91     LogFont.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
92 
93     hFont = CreateFontIndirectW(&LogFont);
94 
95     ReleaseDC(NULL, hdc);
96 
97     return hFont;
98 }
99 
100 
101 static HFONT
102 CreateBoldFont(VOID)
103 {
104     LOGFONTW tmpFont = {0};
105     HFONT hBoldFont;
106     HDC hDc;
107 
108     /* Grabs the Drawing Context */
109     hDc = GetDC(NULL);
110 
111     tmpFont.lfHeight = -MulDiv(8, GetDeviceCaps(hDc, LOGPIXELSY), 72);
112     tmpFont.lfWeight = FW_BOLD;
113     wcscpy(tmpFont.lfFaceName, L"MS Shell Dlg");
114 
115     hBoldFont = CreateFontIndirectW(&tmpFont);
116 
117     ReleaseDC(NULL, hDc);
118 
119     return hBoldFont;
120 }
121 
122 static INT_PTR CALLBACK
123 GplDlgProc(HWND hwndDlg,
124            UINT uMsg,
125            WPARAM wParam,
126            LPARAM lParam)
127 {
128     HRSRC GplTextResource;
129     HGLOBAL GplTextMem;
130     PVOID GplTextLocked;
131     PCHAR GplText;
132     DWORD Size;
133 
134 
135     switch (uMsg)
136     {
137         case WM_INITDIALOG:
138             GplTextResource = FindResourceW(hDllInstance, MAKEINTRESOURCE(IDR_GPL), L"RT_TEXT");
139             if (NULL == GplTextResource)
140             {
141                 break;
142             }
143             Size = SizeofResource(hDllInstance, GplTextResource);
144             if (0 == Size)
145             {
146                 break;
147             }
148             GplText = HeapAlloc(GetProcessHeap(), 0, Size + 1);
149             if (NULL == GplText)
150             {
151                 break;
152             }
153             GplTextMem = LoadResource(hDllInstance, GplTextResource);
154             if (NULL == GplTextMem)
155             {
156                 HeapFree(GetProcessHeap(), 0, GplText);
157                 break;
158             }
159             GplTextLocked = LockResource(GplTextMem);
160             if (NULL == GplTextLocked)
161             {
162                 HeapFree(GetProcessHeap(), 0, GplText);
163                 break;
164             }
165             memcpy(GplText, GplTextLocked, Size);
166             GplText[Size] = '\0';
167             SendMessageA(GetDlgItem(hwndDlg, IDC_GPL_TEXT), WM_SETTEXT, 0, (LPARAM) GplText);
168             HeapFree(GetProcessHeap(), 0, GplText);
169             SetFocus(GetDlgItem(hwndDlg, IDOK));
170             return FALSE;
171 
172         case WM_CLOSE:
173             EndDialog(hwndDlg, IDCANCEL);
174             break;
175 
176         case WM_COMMAND:
177             if (HIWORD(wParam) == BN_CLICKED && IDOK == LOWORD(wParam))
178             {
179                 EndDialog(hwndDlg, IDOK);
180             }
181             break;
182 
183         default:
184             break;
185     }
186 
187     return FALSE;
188 }
189 
190 
191 static INT_PTR CALLBACK
192 WelcomeDlgProc(HWND hwndDlg,
193                UINT uMsg,
194                WPARAM wParam,
195                LPARAM lParam)
196 {
197     PSETUPDATA pSetupData;
198 
199     pSetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
200 
201     switch (uMsg)
202     {
203         case WM_INITDIALOG:
204         {
205             HWND hwndControl;
206             DWORD dwStyle;
207 
208             /* Get pointer to the global setup data */
209             pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
210             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pSetupData);
211 
212             hwndControl = GetParent(hwndDlg);
213 
214             /* Center the wizard window */
215             CenterWindow (hwndControl);
216 
217             /* Hide the system menu */
218             dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
219             SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
220 
221             /* Hide and disable the 'Cancel' button */
222             hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
223             ShowWindow (hwndControl, SW_HIDE);
224             EnableWindow (hwndControl, FALSE);
225 
226             /* Set title font */
227             SendDlgItemMessage(hwndDlg,
228                                IDC_WELCOMETITLE,
229                                WM_SETFONT,
230                                (WPARAM)pSetupData->hTitleFont,
231                                (LPARAM)TRUE);
232         }
233         break;
234 
235 
236         case WM_NOTIFY:
237         {
238             LPNMHDR lpnm = (LPNMHDR)lParam;
239 
240             switch (lpnm->code)
241             {
242                 case PSN_SETACTIVE:
243                     LogItem(L"BEGIN", L"WelcomePage");
244                     /* Enable the Next button */
245                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
246                     if (pSetupData->UnattendSetup)
247                     {
248                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_ACKPAGE);
249                         return TRUE;
250                     }
251                     break;
252 
253                 case PSN_WIZNEXT:
254                     LogItem(L"END", L"WelcomePage");
255                     break;
256 
257                 case PSN_WIZBACK:
258                     pSetupData->UnattendSetup = FALSE;
259                     break;
260 
261                 default:
262                     break;
263             }
264         }
265         break;
266 
267         default:
268             break;
269     }
270 
271     return FALSE;
272 }
273 
274 
275 static INT_PTR CALLBACK
276 AckPageDlgProc(HWND hwndDlg,
277                UINT uMsg,
278                WPARAM wParam,
279                LPARAM lParam)
280 {
281     LPNMHDR lpnm;
282     PWCHAR Projects;
283     PWCHAR End, CurrentProject;
284     INT ProjectsSize, ProjectsCount;
285     PSETUPDATA pSetupData;
286 
287     pSetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
288 
289     switch (uMsg)
290     {
291         case WM_INITDIALOG:
292         {
293             pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
294             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pSetupData);
295 
296             Projects = NULL;
297             ProjectsSize = 256;
298             while (TRUE)
299             {
300                 Projects = HeapAlloc(GetProcessHeap(), 0, ProjectsSize * sizeof(WCHAR));
301                 if (NULL == Projects)
302                 {
303                     return FALSE;
304                 }
305                 ProjectsCount =  LoadStringW(hDllInstance, IDS_ACKPROJECTS, Projects, ProjectsSize);
306                 if (0 == ProjectsCount)
307                 {
308                     HeapFree(GetProcessHeap(), 0, Projects);
309                     return FALSE;
310                 }
311                 if (ProjectsCount < ProjectsSize - 1)
312                 {
313                     break;
314                 }
315                 HeapFree(GetProcessHeap(), 0, Projects);
316                 ProjectsSize *= 2;
317             }
318 
319             CurrentProject = Projects;
320             while (*CurrentProject != L'\0')
321             {
322                 End = wcschr(CurrentProject, L'\n');
323                 if (NULL != End)
324                 {
325                     *End = L'\0';
326                 }
327                 (void)ListBox_AddString(GetDlgItem(hwndDlg, IDC_PROJECTS), CurrentProject);
328                 if (NULL != End)
329                 {
330                     CurrentProject = End + 1;
331                 }
332                 else
333                 {
334                     CurrentProject += wcslen(CurrentProject);
335                 }
336             }
337             HeapFree(GetProcessHeap(), 0, Projects);
338         }
339         break;
340 
341         case WM_COMMAND:
342             if (HIWORD(wParam) == BN_CLICKED && IDC_VIEWGPL == LOWORD(wParam))
343             {
344                 DialogBox(hDllInstance, MAKEINTRESOURCE(IDD_GPL), NULL, GplDlgProc);
345                 SetForegroundWindow(GetParent(hwndDlg));
346             }
347             break;
348 
349         case WM_NOTIFY:
350         {
351             lpnm = (LPNMHDR)lParam;
352 
353             switch (lpnm->code)
354             {
355                 case PSN_SETACTIVE:
356                     /* Enable the Back and Next buttons */
357                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
358                     if (pSetupData->UnattendSetup)
359                     {
360                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_PRODUCT);
361                         return TRUE;
362                     }
363                     break;
364 
365                 case PSN_WIZBACK:
366                     pSetupData->UnattendSetup = FALSE;
367                     break;
368 
369                 default:
370                     break;
371             }
372         }
373         break;
374 
375         default:
376             break;
377     }
378 
379     return FALSE;
380 }
381 
382 static const WCHAR s_szProductOptions[] = L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions";
383 static const WCHAR s_szRosVersion[] = L"SYSTEM\\CurrentControlSet\\Control\\ReactOS\\Settings\\Version";
384 static const WCHAR s_szControlWindows[] = L"SYSTEM\\CurrentControlSet\\Control\\Windows";
385 static const WCHAR s_szWinlogon[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon";
386 static const WCHAR s_szDefaultSoundEvents[] = L"AppEvents\\Schemes\\Apps\\.Default";
387 static const WCHAR s_szExplorerSoundEvents[] = L"AppEvents\\Schemes\\Apps\\Explorer";
388 
389 typedef struct _PRODUCT_OPTION_DATA
390 {
391     LPCWSTR ProductSuite;
392     LPCWSTR ProductType;
393     DWORD ReportAsWorkstation;
394     DWORD CSDVersion;
395     DWORD LogonType;
396 } PRODUCT_OPTION_DATA;
397 
398 static const PRODUCT_OPTION_DATA s_ProductOptionData[] =
399 {
400     { L"Terminal Server\0", L"ServerNT", 0, 0x200, 0 },
401     { L"\0", L"WinNT", 1, 0x300, 1 }
402 };
403 
404 static const WCHAR* s_DefaultSoundEvents[][2] =
405 {
406     { L".Default", L"%SystemRoot%\\Media\\ReactOS_Default.wav" },
407     { L"AppGPFault", L"" },
408     { L"Close", L"" },
409     { L"CriticalBatteryAlarm", L"%SystemRoot%\\Media\\ReactOS_Battery_Critical.wav" },
410     { L"DeviceConnect",  L"%SystemRoot%\\Media\\ReactOS_Hardware_Insert.wav" },
411     { L"DeviceDisconnect", L"%SystemRoot%\\Media\\ReactOS_Hardware_Remove.wav" },
412     { L"DeviceFail", L"%SystemRoot%\\Media\\ReactOS_Hardware_Fail.wav" },
413     { L"LowBatteryAlarm", L"%SystemRoot%\\Media\\ReactOS_Battery_Low.wav" },
414     { L"MailBeep", L"%SystemRoot%\\Media\\ReactOS_Notify.wav" },
415     { L"Maximize", L"%SystemRoot%\\Media\\ReactOS_Restore.wav" },
416     { L"MenuCommand", L"%SystemRoot%\\Media\\ReactOS_Menu_Command.wav" },
417     { L"MenuPopup", L"" },
418     { L"Minimize", L"%SystemRoot%\\Media\\ReactOS_Minimize.wav" },
419     { L"Open", L"" },
420     { L"PrintComplete", L"%SystemRoot%\\Media\\ReactOS_Print_Complete.wav" },
421     { L"RestoreDown", L"" },
422     { L"RestoreUp", L"" },
423     { L"SystemAsterisk", L"%SystemRoot%\\Media\\ReactOS_Ding.wav" },
424     { L"SystemExclamation", L"%SystemRoot%\\Media\\ReactOS_Exclamation.wav" },
425     { L"SystemExit", L"%SystemRoot%\\Media\\ReactOS_Shutdown.wav" },
426     { L"SystemHand", L"%SystemRoot%\\Media\\ReactOS_Critical_Stop.wav" },
427     { L"SystemNotification", L"%SystemRoot%\\Media\\ReactOS_Balloon.wav" },
428     { L"SystemQuestion", L"%SystemRoot%\\Media\\ReactOS_Ding.wav" },
429     { L"SystemStart", L"%SystemRoot%\\Media\\ReactOS_Startup.wav" },
430     { L"WindowsLogoff", L"%SystemRoot%\\Media\\ReactOS_LogOff.wav" }
431 /* Logon sound is already set by default for both Server and Workstation */
432 };
433 
434 static const WCHAR* s_ExplorerSoundEvents[][2] =
435 {
436     { L"EmptyRecycleBin", L"%SystemRoot%\\Media\\ReactOS_Recycle.wav" },
437     { L"Navigating", L"%SystemRoot%\\Media\\ReactOS_Start.wav" }
438 };
439 
440 static BOOL
441 DoWriteSoundEvents(HKEY hKey,
442                    LPCWSTR lpSubkey,
443                    LPCWSTR lpEventsArray[][2],
444                    DWORD dwSize)
445 {
446     HKEY hRootKey, hEventKey, hDefaultKey;
447     LONG error;
448     ULONG i;
449     WCHAR szDest[MAX_PATH];
450     DWORD dwAttribs;
451     DWORD cbData;
452 
453     /* Open the sound events key */
454     error = RegOpenKeyExW(hKey, lpSubkey, 0, KEY_READ, &hRootKey);
455     if (error)
456     {
457         DPRINT1("RegOpenKeyExW failed\n");
458         goto Error;
459     }
460 
461     /* Set each sound event */
462     for (i = 0; i < dwSize; i++)
463     {
464         /*
465          * Verify that the sound file exists and is an actual file.
466          */
467 
468         /* Expand the sound file path */
469         if (!ExpandEnvironmentStringsW(lpEventsArray[i][1], szDest, _countof(szDest)))
470         {
471             /* Failed to expand, continue with the next sound event */
472             continue;
473         }
474 
475         /* Check if the sound file exists and isn't a directory */
476         dwAttribs = GetFileAttributesW(szDest);
477         if ((dwAttribs == INVALID_FILE_ATTRIBUTES) ||
478             (dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
479         {
480             /* It does not, just continue with the next sound event */
481             continue;
482         }
483 
484         /*
485          * Create the sound event entry.
486          */
487 
488         /* Open the sound event subkey */
489         error = RegOpenKeyExW(hRootKey, lpEventsArray[i][0], 0, KEY_READ, &hEventKey);
490         if (error)
491         {
492             /* Failed to open, continue with next sound event */
493             continue;
494         }
495 
496         /* Open .Default subkey */
497         error = RegOpenKeyExW(hEventKey, L".Default", 0, KEY_WRITE, &hDefaultKey);
498         RegCloseKey(hEventKey);
499         if (error)
500         {
501             /* Failed to open, continue with next sound event */
502             continue;
503         }
504 
505         /* Associate the sound file to this sound event */
506         cbData = (lstrlenW(lpEventsArray[i][1]) + 1) * sizeof(WCHAR);
507         error = RegSetValueExW(hDefaultKey, NULL, 0, REG_EXPAND_SZ, (const BYTE *)lpEventsArray[i][1], cbData);
508         RegCloseKey(hDefaultKey);
509         if (error)
510         {
511             /* Failed to set the value, continue with next sound event */
512             continue;
513         }
514     }
515 
516 Error:
517     if (hRootKey)
518         RegCloseKey(hRootKey);
519 
520     return error == ERROR_SUCCESS;
521 }
522 
523 static BOOL
524 DoWriteProductOption(PRODUCT_OPTION nOption)
525 {
526     HKEY hKey;
527     LONG error;
528     LPCWSTR pszData;
529     DWORD dwValue, cbData;
530     const PRODUCT_OPTION_DATA *pData = &s_ProductOptionData[nOption];
531     ASSERT(0 <= nOption && nOption < _countof(s_ProductOptionData));
532 
533     /* open ProductOptions key */
534     error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szProductOptions, 0, KEY_WRITE, &hKey);
535     if (error)
536     {
537         DPRINT1("RegOpenKeyExW failed\n");
538         goto Error;
539     }
540 
541     /* write ProductSuite */
542     pszData = pData->ProductSuite;
543     cbData = (lstrlenW(pszData) + 2) * sizeof(WCHAR);
544     error = RegSetValueExW(hKey, L"ProductSuite", 0, REG_MULTI_SZ, (const BYTE *)pszData, cbData);
545     if (error)
546     {
547         DPRINT1("RegSetValueExW failed\n");
548         goto Error;
549     }
550 
551     /* write ProductType */
552     pszData = pData->ProductType;
553     cbData = (lstrlenW(pszData) + 1) * sizeof(WCHAR);
554     error = RegSetValueExW(hKey, L"ProductType", 0, REG_SZ, (const BYTE *)pszData, cbData);
555     if (error)
556     {
557         DPRINT1("RegSetValueExW failed\n");
558         goto Error;
559     }
560 
561     RegCloseKey(hKey);
562 
563     /* open ReactOS version key */
564     error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szRosVersion, 0, KEY_WRITE, &hKey);
565     if (error)
566     {
567         DPRINT1("RegOpenKeyExW failed\n");
568         goto Error;
569     }
570 
571     /* write ReportAsWorkstation */
572     dwValue = pData->ReportAsWorkstation;
573     cbData = sizeof(dwValue);
574     error = RegSetValueExW(hKey, L"ReportAsWorkstation", 0, REG_DWORD, (const BYTE *)&dwValue, cbData);
575     if (error)
576     {
577         DPRINT1("RegSetValueExW failed\n");
578         goto Error;
579     }
580 
581     RegCloseKey(hKey);
582 
583     /* open Control Windows key */
584     error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szControlWindows, 0, KEY_WRITE, &hKey);
585     if (error)
586     {
587         DPRINT1("RegOpenKeyExW failed\n");
588         goto Error;
589     }
590 
591     /* write Control Windows CSDVersion */
592     dwValue = pData->CSDVersion;
593     cbData = sizeof(dwValue);
594     error = RegSetValueExW(hKey, L"CSDVersion", 0, REG_DWORD, (const BYTE *)&dwValue, cbData);
595     if (error)
596     {
597         DPRINT1("RegSetValueExW failed\n");
598         goto Error;
599     }
600 
601     RegCloseKey(hKey);
602 
603     /* open Winlogon key */
604     error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szWinlogon, 0, KEY_WRITE, &hKey);
605     if (error)
606     {
607         DPRINT1("RegOpenKeyExW failed\n");
608         goto Error;
609     }
610 
611     /* write LogonType */
612     dwValue = pData->LogonType;
613     cbData = sizeof(dwValue);
614     error = RegSetValueExW(hKey, L"LogonType", 0, REG_DWORD, (const BYTE *)&dwValue, cbData);
615     if (error)
616     {
617         DPRINT1("RegSetValueExW failed\n");
618         goto Error;
619     }
620 
621     if (nOption == PRODUCT_OPTION_WORKSTATION)
622     {
623         /* Write system sound events values for Workstation */
624         DoWriteSoundEvents(HKEY_CURRENT_USER, s_szDefaultSoundEvents, s_DefaultSoundEvents, _countof(s_DefaultSoundEvents));
625         DoWriteSoundEvents(HKEY_CURRENT_USER, s_szExplorerSoundEvents, s_ExplorerSoundEvents, _countof(s_ExplorerSoundEvents));
626     }
627 
628 Error:
629     if (hKey)
630         RegCloseKey(hKey);
631 
632     return error == ERROR_SUCCESS;
633 }
634 
635 static void
636 OnChooseOption(HWND hwndDlg, PRODUCT_OPTION nOption)
637 {
638     WCHAR szText[256];
639     ASSERT(0 <= nOption && nOption < _countof(s_ProductOptionData));
640 
641     switch (nOption)
642     {
643         case PRODUCT_OPTION_SERVER:
644             LoadStringW(hDllInstance, IDS_PRODUCTSERVERINFO, szText, _countof(szText));
645             break;
646 
647         case PRODUCT_OPTION_WORKSTATION:
648             LoadStringW(hDllInstance, IDS_PRODUCTWORKSTATIONINFO, szText, _countof(szText));
649             break;
650 
651         default:
652             return;
653     }
654 
655     SetDlgItemTextW(hwndDlg, IDC_PRODUCT_DESCRIPTION, szText);
656 }
657 
658 static INT_PTR CALLBACK
659 ProductPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
660 {
661     LPNMHDR lpnm;
662     PSETUPDATA pSetupData;
663     INT iItem;
664     WCHAR szText[64], szDefault[64];
665     HICON hIcon;
666 
667     pSetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
668 
669     switch (uMsg)
670     {
671         case WM_INITDIALOG:
672         {
673             pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
674             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pSetupData);
675 
676             LoadStringW(hDllInstance, IDS_DEFAULT, szDefault, _countof(szDefault));
677 
678             LoadStringW(hDllInstance, IDS_PRODUCTSERVERNAME, szText, _countof(szText));
679             if (PRODUCT_OPTION_DEFAULT == PRODUCT_OPTION_SERVER)
680             {
681                 StringCchCatW(szText, _countof(szText), L" ");
682                 StringCchCatW(szText, _countof(szText), szDefault);
683             }
684             SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_OPTIONS, CB_ADDSTRING, 0, (LPARAM)szText);
685 
686             LoadStringW(hDllInstance, IDS_PRODUCTWORKSTATIONNAME, szText, _countof(szText));
687             if (PRODUCT_OPTION_DEFAULT == PRODUCT_OPTION_WORKSTATION)
688             {
689                 StringCchCatW(szText, _countof(szText), L" ");
690                 StringCchCatW(szText, _countof(szText), szDefault);
691             }
692             SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_OPTIONS, CB_ADDSTRING, 0, (LPARAM)szText);
693 
694             SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_OPTIONS, CB_SETCURSEL, PRODUCT_OPTION_DEFAULT, 0);
695             OnChooseOption(hwndDlg, PRODUCT_OPTION_DEFAULT);
696 
697             hIcon = LoadIcon(NULL, IDI_WINLOGO);
698             SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_ICON, STM_SETICON, (WPARAM)hIcon, 0);
699             return TRUE;
700         }
701 
702         case WM_COMMAND:
703             if (HIWORD(wParam) == CBN_SELCHANGE && IDC_PRODUCT_OPTIONS == LOWORD(wParam))
704             {
705                 iItem = SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_OPTIONS, CB_GETCURSEL, 0, 0);
706                 OnChooseOption(hwndDlg, (PRODUCT_OPTION)iItem);
707             }
708             break;
709 
710         case WM_NOTIFY:
711         {
712             lpnm = (LPNMHDR)lParam;
713 
714             switch (lpnm->code)
715             {
716                 case PSN_SETACTIVE:
717                     /* Enable the Back and Next buttons */
718                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
719                     if (pSetupData->UnattendSetup)
720                     {
721                         OnChooseOption(hwndDlg, pSetupData->ProductOption);
722                         DoWriteProductOption(pSetupData->ProductOption);
723                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_LOCALEPAGE);
724                         return TRUE;
725                     }
726                     break;
727 
728                 case PSN_WIZNEXT:
729                     iItem = SendDlgItemMessageW(hwndDlg, IDC_PRODUCT_OPTIONS, CB_GETCURSEL, 0, 0);
730                     pSetupData->ProductOption = (PRODUCT_OPTION)iItem;
731                     DoWriteProductOption(pSetupData->ProductOption);
732                     break;
733 
734                 case PSN_WIZBACK:
735                     pSetupData->UnattendSetup = FALSE;
736                     break;
737 
738                 default:
739                     break;
740             }
741         }
742         break;
743 
744         default:
745             break;
746     }
747 
748     return FALSE;
749 }
750 
751 static
752 BOOL
753 WriteOwnerSettings(WCHAR * OwnerName,
754                    WCHAR * OwnerOrganization)
755 {
756     HKEY hKey;
757     LONG res;
758 
759     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
760                         L"Software\\Microsoft\\Windows NT\\CurrentVersion",
761                         0,
762                         KEY_ALL_ACCESS,
763                         &hKey);
764 
765     if (res != ERROR_SUCCESS)
766     {
767         return FALSE;
768     }
769 
770     res = RegSetValueExW(hKey,
771                          L"RegisteredOwner",
772                          0,
773                          REG_SZ,
774                          (LPBYTE)OwnerName,
775                          (wcslen(OwnerName) + 1) * sizeof(WCHAR));
776 
777     if (res != ERROR_SUCCESS)
778     {
779         RegCloseKey(hKey);
780         return FALSE;
781     }
782 
783     res = RegSetValueExW(hKey,
784                          L"RegisteredOrganization",
785                          0,
786                          REG_SZ,
787                          (LPBYTE)OwnerOrganization,
788                          (wcslen(OwnerOrganization) + 1) * sizeof(WCHAR));
789 
790     RegCloseKey(hKey);
791     return (res == ERROR_SUCCESS);
792 }
793 
794 static INT_PTR CALLBACK
795 OwnerPageDlgProc(HWND hwndDlg,
796                  UINT uMsg,
797                  WPARAM wParam,
798                  LPARAM lParam)
799 {
800     WCHAR OwnerName[51];
801     WCHAR OwnerOrganization[51];
802     WCHAR Title[64];
803     WCHAR ErrorName[256];
804     LPNMHDR lpnm;
805     PSETUPDATA pSetupData;
806 
807     pSetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
808 
809     switch (uMsg)
810     {
811         case WM_INITDIALOG:
812         {
813             pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
814             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pSetupData);
815 
816             /* set a localized ('Owner') placeholder string as default */
817             if (LoadStringW(hDllInstance, IDS_MACHINE_OWNER_NAME, OwnerName, _countof(OwnerName)))
818             {
819                 SendDlgItemMessage(hwndDlg, IDC_OWNERNAME, WM_SETTEXT, 0, (LPARAM)OwnerName);
820             }
821 
822             SendDlgItemMessage(hwndDlg, IDC_OWNERNAME, EM_LIMITTEXT, 50, 0);
823             SendDlgItemMessage(hwndDlg, IDC_OWNERORGANIZATION, EM_LIMITTEXT, 50, 0);
824 
825             /* Set focus to owner name */
826             SetFocus(GetDlgItem(hwndDlg, IDC_OWNERNAME));
827 
828             /* Select the default text to quickly overwrite it by typing */
829             SendDlgItemMessage(hwndDlg, IDC_OWNERNAME, EM_SETSEL, 0, -1);
830         }
831         break;
832 
833 
834         case WM_NOTIFY:
835         {
836             lpnm = (LPNMHDR)lParam;
837 
838             switch (lpnm->code)
839             {
840                 case PSN_SETACTIVE:
841                     /* Enable the Back and Next buttons */
842                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
843                     if (pSetupData->UnattendSetup)
844                     {
845                         SendMessage(GetDlgItem(hwndDlg, IDC_OWNERNAME), WM_SETTEXT, 0, (LPARAM)pSetupData->OwnerName);
846                         SendMessage(GetDlgItem(hwndDlg, IDC_OWNERORGANIZATION), WM_SETTEXT, 0, (LPARAM)pSetupData->OwnerOrganization);
847                         if (WriteOwnerSettings(pSetupData->OwnerName, pSetupData->OwnerOrganization))
848                         {
849                             SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_COMPUTERPAGE);
850                             return TRUE;
851                         }
852                     }
853                     break;
854 
855                 case PSN_WIZNEXT:
856                     OwnerName[0] = 0;
857                     if (GetDlgItemTextW(hwndDlg, IDC_OWNERNAME, OwnerName, 50) == 0)
858                     {
859                         if (0 == LoadStringW(hDllInstance, IDS_REACTOS_SETUP, Title, ARRAYSIZE(Title)))
860                         {
861                             wcscpy(Title, L"ReactOS Setup");
862                         }
863                         if (0 == LoadStringW(hDllInstance, IDS_WZD_NAME, ErrorName, ARRAYSIZE(ErrorName)))
864                         {
865                             wcscpy(ErrorName, L"Setup cannot continue until you enter your name.");
866                         }
867                         MessageBoxW(hwndDlg, ErrorName, Title, MB_ICONERROR | MB_OK);
868 
869                         SetFocus(GetDlgItem(hwndDlg, IDC_OWNERNAME));
870                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
871 
872                         return TRUE;
873                     }
874 
875                     OwnerOrganization[0] = 0;
876                     GetDlgItemTextW(hwndDlg, IDC_OWNERORGANIZATION, OwnerOrganization, 50);
877 
878                     if (!WriteOwnerSettings(OwnerName, OwnerOrganization))
879                     {
880                         SetFocus(GetDlgItem(hwndDlg, IDC_OWNERNAME));
881                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
882                         return TRUE;
883                     }
884 
885                 case PSN_WIZBACK:
886                     pSetupData->UnattendSetup = FALSE;
887                     break;
888 
889                 default:
890                     break;
891             }
892         }
893         break;
894 
895         default:
896             break;
897     }
898 
899     return FALSE;
900 }
901 
902 static
903 BOOL
904 WriteComputerSettings(WCHAR * ComputerName, HWND hwndDlg)
905 {
906     WCHAR Title[64];
907     WCHAR ErrorComputerName[256];
908     LONG lError;
909     HKEY hKey = NULL;
910 
911     if (!SetComputerNameW(ComputerName))
912     {
913         if (hwndDlg != NULL)
914         {
915             if (0 == LoadStringW(hDllInstance, IDS_REACTOS_SETUP, Title, ARRAYSIZE(Title)))
916             {
917                 wcscpy(Title, L"ReactOS Setup");
918             }
919             if (0 == LoadStringW(hDllInstance, IDS_WZD_SETCOMPUTERNAME, ErrorComputerName,
920                                  ARRAYSIZE(ErrorComputerName)))
921             {
922                 wcscpy(ErrorComputerName, L"Setup failed to set the computer name.");
923             }
924             MessageBoxW(hwndDlg, ErrorComputerName, Title, MB_ICONERROR | MB_OK);
925         }
926 
927         return FALSE;
928     }
929 
930     /* Set the physical DNS domain */
931     SetComputerNameExW(ComputerNamePhysicalDnsDomain, L"");
932 
933     /* Set the physical DNS hostname */
934     SetComputerNameExW(ComputerNamePhysicalDnsHostname, ComputerName);
935 
936     /* Set the accounts domain name */
937     SetAccountsDomainSid(NULL, ComputerName);
938 
939     /* Now we need to set the Hostname */
940     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
941                            L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
942                            0,
943                            KEY_SET_VALUE,
944                            &hKey);
945     if (lError != ERROR_SUCCESS)
946     {
947         DPRINT1("RegOpenKeyExW for Tcpip\\Parameters failed (%08lX)\n", lError);
948         return TRUE;
949     }
950 
951     lError = RegSetValueEx(hKey,
952                            L"Hostname",
953                            0,
954                            REG_SZ,
955                            (LPBYTE)ComputerName,
956                            (wcslen(ComputerName) + 1) * sizeof(WCHAR));
957     if (lError != ERROR_SUCCESS)
958     {
959         DPRINT1("RegSetValueEx(\"Hostname\") failed (%08lX)\n", lError);
960     }
961 
962     RegCloseKey(hKey);
963 
964     return TRUE;
965 }
966 
967 
968 static
969 BOOL
970 WriteDefaultLogonData(LPWSTR Domain)
971 {
972     WCHAR szAdministratorName[256];
973     HKEY hKey = NULL;
974     LONG lError;
975 
976     if (LoadStringW(hDllInstance,
977                     IDS_ADMINISTRATOR_NAME,
978                     szAdministratorName,
979                     ARRAYSIZE(szAdministratorName)) == 0)
980     {
981         wcscpy(szAdministratorName, L"Administrator");
982     }
983 
984     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
985                            L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
986                            0,
987                            KEY_SET_VALUE,
988                            &hKey);
989     if (lError != ERROR_SUCCESS)
990         return FALSE;
991 
992     lError = RegSetValueEx(hKey,
993                            L"DefaultDomainName",
994                            0,
995                            REG_SZ,
996                            (LPBYTE)Domain,
997                            (wcslen(Domain)+ 1) * sizeof(WCHAR));
998     if (lError != ERROR_SUCCESS)
999     {
1000         DPRINT1("RegSetValueEx(\"DefaultDomainName\") failed!\n");
1001     }
1002 
1003     lError = RegSetValueEx(hKey,
1004                            L"DefaultUserName",
1005                            0,
1006                            REG_SZ,
1007                            (LPBYTE)szAdministratorName,
1008                            (wcslen(szAdministratorName)+ 1) * sizeof(WCHAR));
1009     if (lError != ERROR_SUCCESS)
1010     {
1011         DPRINT1("RegSetValueEx(\"DefaultUserName\") failed!\n");
1012     }
1013 
1014     RegCloseKey(hKey);
1015 
1016     return TRUE;
1017 }
1018 
1019 
1020 /* lpBuffer will be filled with a 15-char string (plus the null terminator) */
1021 static void
1022 GenerateComputerName(LPWSTR lpBuffer)
1023 {
1024     static const WCHAR Chars[] = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1025     static const unsigned cChars = sizeof(Chars) / sizeof(WCHAR) - 1;
1026     unsigned i;
1027 
1028     wcscpy(lpBuffer, L"REACTOS-");
1029 
1030     srand(GetTickCount());
1031 
1032     /* fill in 7 characters */
1033     for (i = 8; i < 15; i++)
1034         lpBuffer[i] = Chars[rand() % cChars];
1035 
1036     lpBuffer[15] = UNICODE_NULL; /* NULL-terminate */
1037 }
1038 
1039 static INT_PTR CALLBACK
1040 ComputerPageDlgProc(HWND hwndDlg,
1041                     UINT uMsg,
1042                     WPARAM wParam,
1043                     LPARAM lParam)
1044 {
1045     WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
1046     WCHAR Password1[128];
1047     WCHAR Password2[128];
1048     PWCHAR Password;
1049     WCHAR Title[64];
1050     WCHAR EmptyComputerName[256], NotMatchPassword[256], WrongPassword[256];
1051     LPNMHDR lpnm;
1052     PSETUPDATA pSetupData;
1053 
1054     pSetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
1055 
1056     if (0 == LoadStringW(hDllInstance, IDS_REACTOS_SETUP, Title, ARRAYSIZE(Title)))
1057     {
1058         wcscpy(Title, L"ReactOS Setup");
1059     }
1060 
1061     switch (uMsg)
1062     {
1063         case WM_INITDIALOG:
1064             pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1065             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pSetupData);
1066 
1067             /* Generate a new pseudo-random computer name */
1068             GenerateComputerName(ComputerName);
1069 
1070             /* Display current computer name */
1071             SetDlgItemTextW(hwndDlg, IDC_COMPUTERNAME, ComputerName);
1072 
1073             /* Set text limits */
1074             SendDlgItemMessage(hwndDlg, IDC_COMPUTERNAME, EM_LIMITTEXT, MAX_COMPUTERNAME_LENGTH, 0);
1075             SendDlgItemMessage(hwndDlg, IDC_ADMINPASSWORD1, EM_LIMITTEXT, 127, 0);
1076             SendDlgItemMessage(hwndDlg, IDC_ADMINPASSWORD2, EM_LIMITTEXT, 127, 0);
1077 
1078             /* Set focus to computer name */
1079             SetFocus(GetDlgItem(hwndDlg, IDC_COMPUTERNAME));
1080             if (pSetupData->UnattendSetup)
1081             {
1082                 SendMessage(GetDlgItem(hwndDlg, IDC_COMPUTERNAME), WM_SETTEXT, 0, (LPARAM)pSetupData->ComputerName);
1083                 SendMessage(GetDlgItem(hwndDlg, IDC_ADMINPASSWORD1), WM_SETTEXT, 0, (LPARAM)pSetupData->AdminPassword);
1084                 SendMessage(GetDlgItem(hwndDlg, IDC_ADMINPASSWORD2), WM_SETTEXT, 0, (LPARAM)pSetupData->AdminPassword);
1085                 WriteComputerSettings(pSetupData->ComputerName, NULL);
1086                 SetAdministratorPassword(pSetupData->AdminPassword);
1087             }
1088 
1089             /* Store the administrator account name as the default user name */
1090             WriteDefaultLogonData(pSetupData->ComputerName);
1091             break;
1092 
1093 
1094         case WM_NOTIFY:
1095         {
1096             lpnm = (LPNMHDR)lParam;
1097 
1098             switch (lpnm->code)
1099             {
1100                 case PSN_SETACTIVE:
1101                     /* Enable the Back and Next buttons */
1102                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
1103                     if (pSetupData->UnattendSetup && WriteComputerSettings(pSetupData->ComputerName, hwndDlg))
1104                     {
1105                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_THEMEPAGE);
1106                         return TRUE;
1107                     }
1108                     break;
1109 
1110                 case PSN_WIZNEXT:
1111                     if (0 == GetDlgItemTextW(hwndDlg, IDC_COMPUTERNAME, ComputerName, MAX_COMPUTERNAME_LENGTH + 1))
1112                     {
1113                         if (0 == LoadStringW(hDllInstance, IDS_WZD_COMPUTERNAME, EmptyComputerName,
1114                                              ARRAYSIZE(EmptyComputerName)))
1115                         {
1116                             wcscpy(EmptyComputerName, L"Setup cannot continue until you enter the name of your computer.");
1117                         }
1118                         MessageBoxW(hwndDlg, EmptyComputerName, Title, MB_ICONERROR | MB_OK);
1119                         SetFocus(GetDlgItem(hwndDlg, IDC_COMPUTERNAME));
1120                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
1121                         return TRUE;
1122                     }
1123 
1124                     /* No need to check computer name for invalid characters,
1125                      * SetComputerName() will do it for us */
1126 
1127                     if (!WriteComputerSettings(ComputerName, hwndDlg))
1128                     {
1129                         SetFocus(GetDlgItem(hwndDlg, IDC_COMPUTERNAME));
1130                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
1131                         return TRUE;
1132                     }
1133 
1134 #ifdef PASSWORDS_MANDATORY
1135                     /* Check if admin passwords have been entered */
1136                     if ((GetDlgItemText(hwndDlg, IDC_ADMINPASSWORD1, Password1, 128) == 0) ||
1137                         (GetDlgItemText(hwndDlg, IDC_ADMINPASSWORD2, Password2, 128) == 0))
1138                     {
1139                         if (0 == LoadStringW(hDllInstance, IDS_WZD_PASSWORDEMPTY, EmptyPassword,
1140                                              ARRAYSIZE(EmptyPassword)))
1141                         {
1142                             wcscpy(EmptyPassword, L"You must enter a password !");
1143                         }
1144                         MessageBoxW(hwndDlg, EmptyPassword, Title, MB_ICONERROR | MB_OK);
1145                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
1146                         return TRUE;
1147                     }
1148 #else
1149                     GetDlgItemTextW(hwndDlg, IDC_ADMINPASSWORD1, Password1, 128);
1150                     GetDlgItemTextW(hwndDlg, IDC_ADMINPASSWORD2, Password2, 128);
1151 #endif
1152                     /* Check if passwords match */
1153                     if (wcscmp(Password1, Password2))
1154                     {
1155                         if (0 == LoadStringW(hDllInstance, IDS_WZD_PASSWORDMATCH, NotMatchPassword,
1156                                              ARRAYSIZE(NotMatchPassword)))
1157                         {
1158                             wcscpy(NotMatchPassword, L"The passwords you entered do not match. Please enter the desired password again.");
1159                         }
1160                         MessageBoxW(hwndDlg, NotMatchPassword, Title, MB_ICONERROR | MB_OK);
1161                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
1162                         return TRUE;
1163                     }
1164 
1165                     /* Check password for invalid characters */
1166                     Password = (PWCHAR)Password1;
1167                     while (*Password)
1168                     {
1169                         if (!isprint(*Password))
1170                         {
1171                             if (0 == LoadStringW(hDllInstance, IDS_WZD_PASSWORDCHAR, WrongPassword,
1172                                                  ARRAYSIZE(WrongPassword)))
1173                             {
1174                                 wcscpy(WrongPassword, L"The password you entered contains invalid characters. Please enter a cleaned password.");
1175                             }
1176                             MessageBoxW(hwndDlg, WrongPassword, Title, MB_ICONERROR | MB_OK);
1177                             SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
1178                             return TRUE;
1179                         }
1180                         Password++;
1181                     }
1182 
1183                     /* Set admin password */
1184                     SetAdministratorPassword(Password1);
1185                     break;
1186 
1187                 case PSN_WIZBACK:
1188                     pSetupData->UnattendSetup = FALSE;
1189                     break;
1190 
1191                 default:
1192                     break;
1193             }
1194         }
1195         break;
1196 
1197         default:
1198             break;
1199     }
1200 
1201     return FALSE;
1202 }
1203 
1204 
1205 static VOID
1206 SetUserLocaleName(HWND hwnd)
1207 {
1208     WCHAR CurLocale[256] = L"";
1209     WCHAR CurGeo[256] = L"";
1210     WCHAR ResText[256] = L"";
1211     WCHAR LocaleText[256 * 2];
1212 
1213     GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_SLANGUAGE, CurLocale, ARRAYSIZE(CurLocale));
1214     GetGeoInfoW(GetUserGeoID(GEOCLASS_NATION), GEO_FRIENDLYNAME, CurGeo, ARRAYSIZE(CurGeo), GetThreadLocale());
1215 
1216     LoadStringW(hDllInstance, IDS_LOCALETEXT, ResText, ARRAYSIZE(ResText));
1217     StringCchPrintfW(LocaleText, ARRAYSIZE(LocaleText), ResText, CurLocale, CurGeo);
1218 
1219     SetWindowTextW(hwnd, LocaleText);
1220 }
1221 
1222 static VOID
1223 SetKeyboardLayoutName(HWND hwnd)
1224 {
1225     HKL hkl;
1226     BOOL LayoutSpecial = FALSE;
1227     WCHAR LayoutPath[256];
1228     WCHAR LocaleName[32];
1229     WCHAR SpecialId[5] = L"";
1230     WCHAR ResText[256] = L"";
1231     DWORD dwValueSize;
1232     HKEY hKey;
1233     UINT i;
1234 
1235     /* Get the default input language and method */
1236     if (!SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, (LPDWORD)&hkl, 0))
1237     {
1238         hkl = GetKeyboardLayout(0);
1239     }
1240 
1241     if ((HIWORD(hkl) & 0xF000) == 0xF000)
1242     {
1243         /* Process keyboard layout with special id */
1244         StringCchPrintfW(SpecialId, ARRAYSIZE(SpecialId), L"%04x", (HIWORD(hkl) & 0x0FFF));
1245         LayoutSpecial = TRUE;
1246     }
1247 
1248 #define MAX_LAYOUTS_PER_LANGID 0x10000
1249     for (i = 0; i < (LayoutSpecial ? MAX_LAYOUTS_PER_LANGID : 1); i++)
1250     {
1251         /* Generate a hexadecimal identifier for keyboard layout registry key */
1252         StringCchPrintfW(LocaleName, ARRAYSIZE(LocaleName), L"%08lx", (i << 16) | LOWORD(hkl));
1253 
1254         StringCchCopyW(LayoutPath, ARRAYSIZE(LayoutPath), L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\");
1255         StringCchCatW(LayoutPath, ARRAYSIZE(LayoutPath), LocaleName);
1256         *LocaleName = UNICODE_NULL;
1257 
1258         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1259                           LayoutPath,
1260                           0,
1261                           KEY_ALL_ACCESS,
1262                           &hKey) == ERROR_SUCCESS)
1263         {
1264             /* Make sure the keyboard layout key we opened is the one we need.
1265              * If the layout has no special id, just pass this check. */
1266             dwValueSize = sizeof(LocaleName);
1267             if (!LayoutSpecial ||
1268                 ((RegQueryValueExW(hKey,
1269                                    L"Layout Id",
1270                                    NULL,
1271                                    NULL,
1272                                    (PVOID)&LocaleName,
1273                                    &dwValueSize) == ERROR_SUCCESS) &&
1274                 (wcscmp(LocaleName, SpecialId) == 0)))
1275             {
1276                 *LocaleName = UNICODE_NULL;
1277                 dwValueSize = sizeof(LocaleName);
1278                 RegQueryValueExW(hKey,
1279                                  L"Layout Text",
1280                                  NULL,
1281                                  NULL,
1282                                  (PVOID)&LocaleName,
1283                                  &dwValueSize);
1284                 /* Let the loop know where to stop */
1285                 i = MAX_LAYOUTS_PER_LANGID;
1286             }
1287             RegCloseKey(hKey);
1288         }
1289         else
1290         {
1291             /* Keyboard layout registry keys are expected to go in order without gaps */
1292             break;
1293         }
1294     }
1295 #undef MAX_LAYOUTS_PER_LANGID
1296 
1297     LoadStringW(hDllInstance, IDS_LAYOUTTEXT, ResText, ARRAYSIZE(ResText));
1298     StringCchPrintfW(LayoutPath, ARRAYSIZE(LayoutPath), ResText, LocaleName);
1299 
1300     SetWindowTextW(hwnd, LayoutPath);
1301 }
1302 
1303 
1304 static BOOL
1305 RunControlPanelApplet(HWND hwnd, PCWSTR pwszCPLParameters)
1306 {
1307     MSG msg;
1308     HWND MainWindow = GetParent(hwnd);
1309     STARTUPINFOW StartupInfo;
1310     PROCESS_INFORMATION ProcessInformation;
1311     WCHAR CmdLine[MAX_PATH] = L"rundll32.exe shell32.dll,Control_RunDLL ";
1312 
1313     if (!pwszCPLParameters)
1314     {
1315         MessageBoxW(hwnd, L"Error: Failed to launch the Control Panel Applet.", NULL, MB_ICONERROR);
1316         return FALSE;
1317     }
1318 
1319     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
1320     StartupInfo.cb = sizeof(StartupInfo);
1321     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
1322 
1323     ASSERT(_countof(CmdLine) > wcslen(CmdLine) + wcslen(pwszCPLParameters));
1324     wcscat(CmdLine, pwszCPLParameters);
1325 
1326     if (!CreateProcessW(NULL,
1327                         CmdLine,
1328                         NULL,
1329                         NULL,
1330                         FALSE,
1331                         0,
1332                         NULL,
1333                         NULL,
1334                         &StartupInfo,
1335                         &ProcessInformation))
1336     {
1337         MessageBoxW(hwnd, L"Error: Failed to launch the Control Panel Applet.", NULL, MB_ICONERROR);
1338         return FALSE;
1339     }
1340 
1341     /* Disable the Back and Next buttons and the main window
1342      * while we're interacting with the control panel applet */
1343     PropSheet_SetWizButtons(MainWindow, 0);
1344     EnableWindow(MainWindow, FALSE);
1345 
1346     while ((MsgWaitForMultipleObjects(1, &ProcessInformation.hProcess, FALSE, INFINITE, QS_ALLINPUT|QS_ALLPOSTMESSAGE )) != WAIT_OBJECT_0)
1347     {
1348        /* We still need to process main window messages to avoid freeze */
1349        while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
1350        {
1351            TranslateMessage(&msg);
1352            DispatchMessageW(&msg);
1353        }
1354     }
1355     CloseHandle(ProcessInformation.hThread);
1356     CloseHandle(ProcessInformation.hProcess);
1357 
1358     /* Enable the Back and Next buttons and the main window again */
1359     PropSheet_SetWizButtons(MainWindow, PSWIZB_BACK | PSWIZB_NEXT);
1360     EnableWindow(MainWindow, TRUE);
1361 
1362     return TRUE;
1363 }
1364 
1365 static VOID
1366 WriteUserLocale(VOID)
1367 {
1368     HKEY hKey;
1369     LCID lcid;
1370     WCHAR Locale[12];
1371 
1372     lcid = GetSystemDefaultLCID();
1373 
1374     if (GetLocaleInfoW(MAKELCID(lcid, SORT_DEFAULT), LOCALE_ILANGUAGE, Locale, ARRAYSIZE(Locale)) != 0)
1375     {
1376         if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Control Panel\\International",
1377                             0, NULL, REG_OPTION_NON_VOLATILE,
1378                             KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS)
1379         {
1380             RegSetValueExW(hKey, L"Locale", 0, REG_SZ, (LPBYTE)Locale, (wcslen(Locale) + 1) * sizeof(WCHAR));
1381             RegCloseKey(hKey);
1382         }
1383     }
1384 }
1385 
1386 static INT_PTR CALLBACK
1387 LocalePageDlgProc(HWND hwndDlg,
1388                   UINT uMsg,
1389                   WPARAM wParam,
1390                   LPARAM lParam)
1391 {
1392     PSETUPDATA SetupData;
1393 
1394     /* Retrieve pointer to the global setup data */
1395     SetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1396 
1397     switch (uMsg)
1398     {
1399         case WM_INITDIALOG:
1400         {
1401             /* Save pointer to the global setup data */
1402             SetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1403             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)SetupData);
1404             WriteUserLocale();
1405 
1406             SetUserLocaleName(GetDlgItem(hwndDlg, IDC_LOCALETEXT));
1407             SetKeyboardLayoutName(GetDlgItem(hwndDlg, IDC_LAYOUTTEXT));
1408         }
1409         break;
1410 
1411         case WM_COMMAND:
1412             if (HIWORD(wParam) == BN_CLICKED)
1413             {
1414                 switch (LOWORD(wParam))
1415                 {
1416                     case IDC_CUSTOMLOCALE:
1417                         RunControlPanelApplet(hwndDlg, L"intl.cpl,,5");
1418                         SetUserLocaleName(GetDlgItem(hwndDlg, IDC_LOCALETEXT));
1419                         break;
1420 
1421                     case IDC_CUSTOMLAYOUT:
1422                         RunControlPanelApplet(hwndDlg, L"input.dll,@1");
1423                         SetKeyboardLayoutName(GetDlgItem(hwndDlg, IDC_LAYOUTTEXT));
1424                         break;
1425                 }
1426             }
1427             break;
1428 
1429         case WM_NOTIFY:
1430         {
1431             LPNMHDR lpnm = (LPNMHDR)lParam;
1432 
1433             switch (lpnm->code)
1434             {
1435                 case PSN_SETACTIVE:
1436                     /* Enable the Back and Next buttons */
1437                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
1438                     if (SetupData->UnattendSetup)
1439                     {
1440                         // if (!*SetupData->SourcePath)
1441                         {
1442                             RunControlPanelApplet(hwndDlg, L"intl.cpl,,/f:\"$winnt$.inf\""); // Should be in System32
1443                         }
1444 
1445                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_OWNERPAGE);
1446                         return TRUE;
1447                     }
1448                     break;
1449 
1450                 case PSN_WIZNEXT:
1451                     break;
1452 
1453                 case PSN_WIZBACK:
1454                     SetupData->UnattendSetup = FALSE;
1455                     break;
1456 
1457                 default:
1458                     break;
1459             }
1460         }
1461         break;
1462 
1463         default:
1464             break;
1465     }
1466 
1467     return FALSE;
1468 }
1469 
1470 
1471 static PTIMEZONE_ENTRY
1472 GetLargerTimeZoneEntry(PSETUPDATA SetupData, DWORD Index)
1473 {
1474     PTIMEZONE_ENTRY Entry;
1475 
1476     Entry = SetupData->TimeZoneListHead;
1477     while (Entry != NULL)
1478     {
1479         if (Entry->Index >= Index)
1480             return Entry;
1481 
1482         Entry = Entry->Next;
1483     }
1484 
1485     return NULL;
1486 }
1487 
1488 static LONG
1489 RetrieveTimeZone(
1490     IN HKEY hZoneKey,
1491     IN PVOID Context)
1492 {
1493     LONG lError;
1494     PSETUPDATA SetupData = (PSETUPDATA)Context;
1495     PTIMEZONE_ENTRY Entry;
1496     PTIMEZONE_ENTRY Current;
1497     ULONG DescriptionSize;
1498     ULONG StandardNameSize;
1499     ULONG DaylightNameSize;
1500 
1501     Entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEZONE_ENTRY));
1502     if (Entry == NULL)
1503     {
1504         return ERROR_NOT_ENOUGH_MEMORY;
1505     }
1506 
1507     DescriptionSize  = sizeof(Entry->Description);
1508     StandardNameSize = sizeof(Entry->StandardName);
1509     DaylightNameSize = sizeof(Entry->DaylightName);
1510 
1511     lError = QueryTimeZoneData(hZoneKey,
1512                                &Entry->Index,
1513                                &Entry->TimezoneInfo,
1514                                Entry->Description,
1515                                &DescriptionSize,
1516                                Entry->StandardName,
1517                                &StandardNameSize,
1518                                Entry->DaylightName,
1519                                &DaylightNameSize);
1520     if (lError != ERROR_SUCCESS)
1521     {
1522         HeapFree(GetProcessHeap(), 0, Entry);
1523         return lError;
1524     }
1525 
1526     if (SetupData->TimeZoneListHead == NULL &&
1527         SetupData->TimeZoneListTail == NULL)
1528     {
1529         Entry->Prev = NULL;
1530         Entry->Next = NULL;
1531         SetupData->TimeZoneListHead = Entry;
1532         SetupData->TimeZoneListTail = Entry;
1533     }
1534     else
1535     {
1536         Current = GetLargerTimeZoneEntry(SetupData, Entry->Index);
1537         if (Current != NULL)
1538         {
1539             if (Current == SetupData->TimeZoneListHead)
1540             {
1541                 /* Prepend to head */
1542                 Entry->Prev = NULL;
1543                 Entry->Next = SetupData->TimeZoneListHead;
1544                 SetupData->TimeZoneListHead->Prev = Entry;
1545                 SetupData->TimeZoneListHead = Entry;
1546             }
1547             else
1548             {
1549                 /* Insert before current */
1550                 Entry->Prev = Current->Prev;
1551                 Entry->Next = Current;
1552                 Current->Prev->Next = Entry;
1553                 Current->Prev = Entry;
1554             }
1555         }
1556         else
1557         {
1558             /* Append to tail */
1559             Entry->Prev = SetupData->TimeZoneListTail;
1560             Entry->Next = NULL;
1561             SetupData->TimeZoneListTail->Next = Entry;
1562             SetupData->TimeZoneListTail = Entry;
1563         }
1564     }
1565 
1566     return ERROR_SUCCESS;
1567 }
1568 
1569 static VOID
1570 CreateTimeZoneList(PSETUPDATA SetupData)
1571 {
1572     EnumerateTimeZoneList(RetrieveTimeZone, SetupData);
1573 }
1574 
1575 static VOID
1576 DestroyTimeZoneList(PSETUPDATA SetupData)
1577 {
1578     PTIMEZONE_ENTRY Entry;
1579 
1580     while (SetupData->TimeZoneListHead != NULL)
1581     {
1582         Entry = SetupData->TimeZoneListHead;
1583 
1584         SetupData->TimeZoneListHead = Entry->Next;
1585         if (SetupData->TimeZoneListHead != NULL)
1586         {
1587             SetupData->TimeZoneListHead->Prev = NULL;
1588         }
1589 
1590         HeapFree(GetProcessHeap(), 0, Entry);
1591     }
1592 
1593     SetupData->TimeZoneListTail = NULL;
1594 }
1595 
1596 
1597 static VOID
1598 ShowTimeZoneList(HWND hwnd, PSETUPDATA SetupData, DWORD dwEntryIndex)
1599 {
1600     PTIMEZONE_ENTRY Entry;
1601     DWORD dwIndex = 0;
1602     DWORD dwCount;
1603 
1604     GetTimeZoneListIndex(&dwEntryIndex);
1605 
1606     Entry = SetupData->TimeZoneListHead;
1607     while (Entry != NULL)
1608     {
1609         dwCount = SendMessage(hwnd,
1610                               CB_ADDSTRING,
1611                               0,
1612                               (LPARAM)Entry->Description);
1613 
1614         if (dwEntryIndex != 0 && dwEntryIndex == Entry->Index)
1615             dwIndex = dwCount;
1616 
1617         Entry = Entry->Next;
1618     }
1619 
1620     SendMessage(hwnd,
1621                 CB_SETCURSEL,
1622                 (WPARAM)dwIndex,
1623                 0);
1624 }
1625 
1626 
1627 static VOID
1628 SetLocalTimeZone(HWND hwnd, PSETUPDATA SetupData)
1629 {
1630     TIME_ZONE_INFORMATION TimeZoneInformation;
1631     PTIMEZONE_ENTRY Entry;
1632     DWORD dwIndex;
1633     DWORD i;
1634 
1635     dwIndex = SendMessage(hwnd,
1636                           CB_GETCURSEL,
1637                           0,
1638                           0);
1639 
1640     i = 0;
1641     Entry = SetupData->TimeZoneListHead;
1642     while (i < dwIndex)
1643     {
1644         if (Entry == NULL)
1645             return;
1646 
1647         i++;
1648         Entry = Entry->Next;
1649     }
1650 
1651     wcscpy(TimeZoneInformation.StandardName,
1652            Entry->StandardName);
1653     wcscpy(TimeZoneInformation.DaylightName,
1654            Entry->DaylightName);
1655 
1656     TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
1657     TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
1658     TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
1659 
1660     memcpy(&TimeZoneInformation.StandardDate,
1661            &Entry->TimezoneInfo.StandardDate,
1662            sizeof(SYSTEMTIME));
1663     memcpy(&TimeZoneInformation.DaylightDate,
1664            &Entry->TimezoneInfo.DaylightDate,
1665            sizeof(SYSTEMTIME));
1666 
1667     /* Set time zone information */
1668     SetTimeZoneInformation(&TimeZoneInformation);
1669 }
1670 
1671 
1672 static BOOL
1673 GetLocalSystemTime(HWND hwnd, PSETUPDATA SetupData)
1674 {
1675     SYSTEMTIME Date;
1676     SYSTEMTIME Time;
1677 
1678     if (DateTime_GetSystemtime(GetDlgItem(hwnd, IDC_DATEPICKER), &Date) != GDT_VALID)
1679     {
1680         return FALSE;
1681     }
1682 
1683     if (DateTime_GetSystemtime(GetDlgItem(hwnd, IDC_TIMEPICKER), &Time) != GDT_VALID)
1684     {
1685         return FALSE;
1686     }
1687 
1688     SetupData->SystemTime.wYear = Date.wYear;
1689     SetupData->SystemTime.wMonth = Date.wMonth;
1690     SetupData->SystemTime.wDayOfWeek = Date.wDayOfWeek;
1691     SetupData->SystemTime.wDay = Date.wDay;
1692     SetupData->SystemTime.wHour = Time.wHour;
1693     SetupData->SystemTime.wMinute = Time.wMinute;
1694     SetupData->SystemTime.wSecond = Time.wSecond;
1695     SetupData->SystemTime.wMilliseconds = Time.wMilliseconds;
1696 
1697     return TRUE;
1698 }
1699 
1700 
1701 static BOOL
1702 SetSystemLocalTime(HWND hwnd, PSETUPDATA SetupData)
1703 {
1704     BOOL Ret = FALSE;
1705 
1706     /*
1707      * Call SetLocalTime twice to ensure correct results
1708      */
1709     Ret = SetLocalTime(&SetupData->SystemTime) &&
1710           SetLocalTime(&SetupData->SystemTime);
1711 
1712     return Ret;
1713 }
1714 
1715 
1716 static VOID
1717 UpdateLocalSystemTime(HWND hwnd, SYSTEMTIME LocalTime)
1718 {
1719     DateTime_SetSystemtime(GetDlgItem(hwnd, IDC_DATEPICKER), GDT_VALID, &LocalTime);
1720     DateTime_SetSystemtime(GetDlgItem(hwnd, IDC_TIMEPICKER), GDT_VALID, &LocalTime);
1721 }
1722 
1723 
1724 static BOOL
1725 WriteDateTimeSettings(HWND hwndDlg, PSETUPDATA SetupData)
1726 {
1727     WCHAR Title[64];
1728     WCHAR ErrorLocalTime[256];
1729 
1730     GetLocalSystemTime(hwndDlg, SetupData);
1731     SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST),
1732                      SetupData);
1733 
1734     SetAutoDaylight(SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT,
1735                                        BM_GETCHECK, 0, 0) != BST_UNCHECKED);
1736     if (!SetSystemLocalTime(hwndDlg, SetupData))
1737     {
1738         if (0 == LoadStringW(hDllInstance, IDS_REACTOS_SETUP, Title, ARRAYSIZE(Title)))
1739         {
1740             wcscpy(Title, L"ReactOS Setup");
1741         }
1742         if (0 == LoadStringW(hDllInstance, IDS_WZD_LOCALTIME, ErrorLocalTime,
1743                              ARRAYSIZE(ErrorLocalTime)))
1744         {
1745             wcscpy(ErrorLocalTime, L"Setup was unable to set the local time.");
1746         }
1747         MessageBoxW(hwndDlg, ErrorLocalTime, Title, MB_ICONWARNING | MB_OK);
1748         return FALSE;
1749     }
1750 
1751     return TRUE;
1752 }
1753 
1754 
1755 static INT_PTR CALLBACK
1756 DateTimePageDlgProc(HWND hwndDlg,
1757                     UINT uMsg,
1758                     WPARAM wParam,
1759                     LPARAM lParam)
1760 {
1761     PSETUPDATA SetupData;
1762 
1763     /* Retrieve pointer to the global setup data */
1764     SetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1765 
1766     switch (uMsg)
1767     {
1768         case WM_INITDIALOG:
1769         {
1770             /* Save pointer to the global setup data */
1771             SetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1772             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)SetupData);
1773 
1774             CreateTimeZoneList(SetupData);
1775 
1776             if (SetupData->UnattendSetup)
1777             {
1778                 ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST),
1779                                  SetupData, SetupData->TimeZoneIndex);
1780 
1781                 if (!SetupData->DisableAutoDaylightTimeSet)
1782                 {
1783                     SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
1784                 }
1785             }
1786             else
1787             {
1788                 ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST),
1789                                  SetupData, -1);
1790 
1791                 SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
1792             }
1793             break;
1794         }
1795 
1796         case WM_TIMER:
1797         {
1798             SYSTEMTIME LocalTime;
1799 
1800             GetLocalTime(&LocalTime);
1801             UpdateLocalSystemTime(hwndDlg, LocalTime);
1802 
1803             // Reset timeout.
1804             SetTimer(hwndDlg, 1, 1000 - LocalTime.wMilliseconds, NULL);
1805             break;
1806         }
1807 
1808         case WM_NOTIFY:
1809             switch (((LPNMHDR)lParam)->code)
1810             {
1811                 case PSN_SETACTIVE:
1812                 {
1813                     SYSTEMTIME LocalTime;
1814 
1815                     GetLocalTime(&LocalTime);
1816                     UpdateLocalSystemTime(hwndDlg, LocalTime);
1817 
1818                     /* Enable the Back and Next buttons */
1819                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
1820 
1821                     if (SetupData->UnattendSetup && WriteDateTimeSettings(hwndDlg, SetupData))
1822                     {
1823                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, SetupData->uFirstNetworkWizardPage);
1824                         return TRUE;
1825                     }
1826 
1827                     SetTimer(hwndDlg, 1, 1000 - LocalTime.wMilliseconds, NULL);
1828                     break;
1829                 }
1830 
1831                 case PSN_KILLACTIVE:
1832                 case DTN_DATETIMECHANGE:
1833                     // NB: Not re-set until changing page (PSN_SETACTIVE).
1834                     KillTimer(hwndDlg, 1);
1835                     break;
1836 
1837                 case PSN_WIZNEXT:
1838                     WriteDateTimeSettings(hwndDlg, SetupData);
1839                     break;
1840 
1841                 case PSN_WIZBACK:
1842                     SetupData->UnattendSetup = FALSE;
1843                     break;
1844 
1845                 default:
1846                     break;
1847             }
1848             break;
1849 
1850         case WM_DESTROY:
1851             DestroyTimeZoneList(SetupData);
1852             break;
1853 
1854         default:
1855             break;
1856     }
1857 
1858     return FALSE;
1859 }
1860 
1861 static struct ThemeInfo
1862 {
1863     LPCWSTR PreviewBitmap;
1864     UINT DisplayName;
1865     LPCWSTR ThemeFile;
1866 
1867 } Themes[] = {
1868     { MAKEINTRESOURCE(IDB_CLASSIC), IDS_CLASSIC, NULL },
1869     { MAKEINTRESOURCE(IDB_LAUTUS), IDS_LAUTUS, L"themes\\lautus\\lautus.msstyles" },
1870     { MAKEINTRESOURCE(IDB_LUNAR), IDS_LUNAR, L"themes\\lunar\\lunar.msstyles" },
1871     { MAKEINTRESOURCE(IDB_MIZU), IDS_MIZU, L"themes\\mizu\\mizu.msstyles"},
1872 };
1873 
1874 static INT_PTR CALLBACK
1875 ThemePageDlgProc(HWND hwndDlg,
1876                     UINT uMsg,
1877                     WPARAM wParam,
1878                     LPARAM lParam)
1879 {
1880     PSETUPDATA SetupData;
1881     LPNMLISTVIEW pnmv;
1882 
1883     /* Retrieve pointer to the global setup data */
1884     SetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1885 
1886     switch (uMsg)
1887     {
1888         case WM_INITDIALOG:
1889         {
1890             HWND hListView;
1891             HIMAGELIST himl;
1892             DWORD n;
1893             LVITEM lvi = {0};
1894 
1895             /* Save pointer to the global setup data */
1896             SetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
1897             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)SetupData);
1898 
1899             hListView = GetDlgItem(hwndDlg, IDC_THEMEPICKER);
1900 
1901             /* Common */
1902             himl = ImageList_Create(180, 163, ILC_COLOR32 | ILC_MASK, ARRAYSIZE(Themes), 1);
1903             lvi.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE;
1904 
1905             for (n = 0; n < ARRAYSIZE(Themes); ++n)
1906             {
1907                 WCHAR DisplayName[100] = {0};
1908                 /* Load the bitmap */
1909                 HANDLE image = LoadImageW(hDllInstance, Themes[n].PreviewBitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
1910                 ImageList_AddMasked(himl, image, RGB(255,0,255));
1911 
1912                 /* Load the string */
1913                 LoadStringW(hDllInstance, Themes[n].DisplayName, DisplayName, ARRAYSIZE(DisplayName));
1914                 DisplayName[ARRAYSIZE(DisplayName)-1] = UNICODE_NULL;
1915 
1916                 /* Add the listview item */
1917                 lvi.iItem  = n;
1918                 lvi.iImage = n;
1919                 lvi.pszText = DisplayName;
1920                 ListView_InsertItem(hListView, &lvi);
1921             }
1922 
1923             /* Register the imagelist */
1924             ListView_SetImageList(hListView, himl, LVSIL_NORMAL);
1925             /* Transparant background */
1926             ListView_SetBkColor(hListView, CLR_NONE);
1927             ListView_SetTextBkColor(hListView, CLR_NONE);
1928             /* Reduce the size between the items */
1929             ListView_SetIconSpacing(hListView, 190, 173);
1930             break;
1931         }
1932         case WM_NOTIFY:
1933             switch (((LPNMHDR)lParam)->code)
1934             {
1935                 //case LVN_ITEMCHANGING:
1936                 case LVN_ITEMCHANGED:
1937                     pnmv = (LPNMLISTVIEW)lParam;
1938                     if ((pnmv->uChanged & LVIF_STATE) && (pnmv->uNewState & LVIS_SELECTED))
1939                     {
1940                         int iTheme = pnmv->iItem;
1941                         DPRINT1("Selected theme: %u\n", Themes[iTheme].DisplayName);
1942 
1943                         if (Themes[iTheme].ThemeFile)
1944                         {
1945                             WCHAR wszParams[1024];
1946                             WCHAR wszTheme[MAX_PATH];
1947                             WCHAR* format = L"desk.cpl,,2 /Action:ActivateMSTheme /file:\"%s\"";
1948 
1949                             SHGetFolderPathAndSubDirW(0, CSIDL_RESOURCES, NULL, SHGFP_TYPE_DEFAULT, Themes[iTheme].ThemeFile, wszTheme);
1950                             swprintf(wszParams, format, wszTheme);
1951                             RunControlPanelApplet(hwndDlg, wszParams);
1952                         }
1953                         else
1954                         {
1955                             RunControlPanelApplet(hwndDlg, L"desk.cpl,,2 /Action:ActivateMSTheme");
1956                         }
1957                     }
1958                     break;
1959                 case PSN_SETACTIVE:
1960                     /* Enable the Back and Next buttons */
1961                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
1962                     if (SetupData->UnattendSetup)
1963                     {
1964                         SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, SetupData->uFirstNetworkWizardPage);
1965                         return TRUE;
1966                     }
1967                     break;
1968 
1969                 case PSN_WIZNEXT:
1970                     break;
1971 
1972                 case PSN_WIZBACK:
1973                     SetupData->UnattendSetup = FALSE;
1974                     break;
1975 
1976                 default:
1977                     break;
1978             }
1979             break;
1980 
1981         default:
1982             break;
1983     }
1984 
1985     return FALSE;
1986 }
1987 
1988 static UINT CALLBACK
1989 RegistrationNotificationProc(PVOID Context,
1990                              UINT Notification,
1991                              UINT_PTR Param1,
1992                              UINT_PTR Param2)
1993 {
1994     PREGISTRATIONDATA RegistrationData;
1995     PSP_REGISTER_CONTROL_STATUSW StatusInfo;
1996     UINT MessageID;
1997 
1998     RegistrationData = (PREGISTRATIONDATA)Context;
1999 
2000     if (Notification == SPFILENOTIFY_STARTREGISTRATION ||
2001         Notification == SPFILENOTIFY_ENDREGISTRATION)
2002     {
2003         StatusInfo = (PSP_REGISTER_CONTROL_STATUSW) Param1;
2004         RegistrationData->pNotify->CurrentItem = wcsrchr(StatusInfo->FileName, L'\\');
2005         if (RegistrationData->pNotify->CurrentItem == NULL)
2006         {
2007             RegistrationData->pNotify->CurrentItem = StatusInfo->FileName;
2008         }
2009         else
2010         {
2011             RegistrationData->pNotify->CurrentItem++;
2012         }
2013 
2014         if (Notification == SPFILENOTIFY_STARTREGISTRATION)
2015         {
2016             DPRINT("Received SPFILENOTIFY_STARTREGISTRATION notification for %S\n",
2017                    StatusInfo->FileName);
2018             RegistrationData->pNotify->Progress = RegistrationData->Registered;
2019 
2020             DPRINT("RegisterDll: Start step %ld\n", RegistrationData->pNotify->Progress);
2021             SendMessage(RegistrationData->hwndDlg, PM_STEP_START, 0, (LPARAM)RegistrationData->pNotify);
2022         }
2023         else
2024         {
2025             DPRINT("Received SPFILENOTIFY_ENDREGISTRATION notification for %S\n",
2026                    StatusInfo->FileName);
2027             DPRINT("Win32Error %u FailureCode %u\n", StatusInfo->Win32Error,
2028                    StatusInfo->FailureCode);
2029             if (StatusInfo->FailureCode != SPREG_SUCCESS)
2030             {
2031                 switch (StatusInfo->FailureCode)
2032                 {
2033                     case SPREG_LOADLIBRARY:
2034                         MessageID = IDS_LOADLIBRARY_FAILED;
2035                         break;
2036                     case SPREG_GETPROCADDR:
2037                         MessageID = IDS_GETPROCADDR_FAILED;
2038                         break;
2039                     case SPREG_REGSVR:
2040                         MessageID = IDS_REGSVR_FAILED;
2041                         break;
2042                     case SPREG_DLLINSTALL:
2043                         MessageID = IDS_DLLINSTALL_FAILED;
2044                         break;
2045                     case SPREG_TIMEOUT:
2046                         MessageID = IDS_TIMEOUT;
2047                         break;
2048                     default:
2049                         MessageID = IDS_REASON_UNKNOWN;
2050                         break;
2051                 }
2052 
2053                 RegistrationData->pNotify->MessageID = MessageID;
2054                 RegistrationData->pNotify->LastError = StatusInfo->Win32Error;
2055             }
2056             else
2057             {
2058                 RegistrationData->pNotify->MessageID = 0;
2059                 RegistrationData->pNotify->LastError = ERROR_SUCCESS;
2060             }
2061 
2062             if (RegistrationData->Registered < RegistrationData->DllCount)
2063             {
2064                 RegistrationData->Registered++;
2065             }
2066 
2067             RegistrationData->pNotify->Progress = RegistrationData->Registered;
2068             DPRINT("RegisterDll: End step %ld\n", RegistrationData->pNotify->Progress);
2069             SendMessage(RegistrationData->hwndDlg, PM_STEP_END, 0, (LPARAM)RegistrationData->pNotify);
2070         }
2071 
2072         return FILEOP_DOIT;
2073     }
2074     else
2075     {
2076         DPRINT1("Received unexpected notification %u\n", Notification);
2077         return SetupDefaultQueueCallback(RegistrationData->DefaultContext,
2078                                          Notification, Param1, Param2);
2079     }
2080 }
2081 
2082 
2083 static
2084 DWORD
2085 RegisterDlls(
2086     _In_ PITEMSDATA pItemsData,
2087     _In_ PREGISTRATIONNOTIFY pNotify)
2088 {
2089     REGISTRATIONDATA RegistrationData;
2090     WCHAR SectionName[512];
2091     INFCONTEXT Context;
2092     LONG DllCount = 0;
2093     DWORD Error = NO_ERROR;
2094 
2095     ZeroMemory(&RegistrationData, sizeof(REGISTRATIONDATA));
2096     RegistrationData.hwndDlg = pItemsData->hwndDlg;
2097     RegistrationData.Registered = 0;
2098 
2099     if (!SetupFindFirstLineW(hSysSetupInf, L"RegistrationPhase2",
2100                              L"RegisterDlls", &Context))
2101     {
2102         DPRINT1("No RegistrationPhase2 section found\n");
2103         return GetLastError();
2104     }
2105 
2106     if (!SetupGetStringFieldW(&Context, 1, SectionName,
2107                               ARRAYSIZE(SectionName),
2108                               NULL))
2109     {
2110         DPRINT1("Unable to retrieve section name\n");
2111         return GetLastError();
2112     }
2113 
2114     DllCount = SetupGetLineCountW(hSysSetupInf, SectionName);
2115     DPRINT("SectionName %S DllCount %ld\n", SectionName, DllCount);
2116     if (DllCount < 0)
2117     {
2118         return STATUS_NOT_FOUND;
2119     }
2120 
2121     RegistrationData.DllCount = (ULONG)DllCount;
2122     RegistrationData.DefaultContext = SetupInitDefaultQueueCallback(RegistrationData.hwndDlg);
2123     RegistrationData.pNotify = pNotify;
2124 
2125     _SEH2_TRY
2126     {
2127         if (!SetupInstallFromInfSectionW(GetParent(RegistrationData.hwndDlg),
2128                                          hSysSetupInf,
2129                                          L"RegistrationPhase2",
2130                                          SPINST_REGISTRY | SPINST_REGISTERCALLBACKAWARE | SPINST_REGSVR,
2131                                          0,
2132                                          NULL,
2133                                          0,
2134                                          RegistrationNotificationProc,
2135                                          &RegistrationData,
2136                                          NULL,
2137                                          NULL))
2138         {
2139             Error = GetLastError();
2140         }
2141     }
2142     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2143     {
2144         DPRINT("Catching exception\n");
2145         Error = RtlNtStatusToDosError(_SEH2_GetExceptionCode());
2146     }
2147     _SEH2_END;
2148 
2149     SetupTermDefaultQueueCallback(RegistrationData.DefaultContext);
2150 
2151     return Error;
2152 }
2153 
2154 static
2155 VOID
2156 RegisterComponents(
2157     PITEMSDATA pItemsData)
2158 {
2159     WCHAR SectionName[512];
2160     INFCONTEXT Context;
2161     LONG Steps = 0;
2162     DWORD Error = NO_ERROR;
2163     REGISTRATIONNOTIFY Notify;
2164 
2165     ZeroMemory(&Notify, sizeof(Notify));
2166 
2167     /* Count the 'RegisterDlls' steps */
2168     if (!SetupFindFirstLineW(hSysSetupInf, L"RegistrationPhase2",
2169                              L"RegisterDlls", &Context))
2170     {
2171         DPRINT1("No RegistrationPhase2 section found\n");
2172         return;
2173     }
2174 
2175     if (!SetupGetStringFieldW(&Context, 1, SectionName,
2176                               ARRAYSIZE(SectionName),
2177                               NULL))
2178     {
2179         DPRINT1("Unable to retrieve section name\n");
2180         return;
2181     }
2182 
2183     Steps += SetupGetLineCountW(hSysSetupInf, SectionName);
2184 
2185     /* Count the 'TypeLibratries' steps */
2186     Steps += SetupGetLineCountW(hSysSetupInf, L"TypeLibraries");
2187 
2188     /* Start the item */
2189     DPRINT("Register Components: %ld Steps\n", Steps);
2190     SendMessage(pItemsData->hwndDlg, PM_ITEM_START, 0, (LPARAM)Steps);
2191 
2192     Error = RegisterDlls(pItemsData, &Notify);
2193     if (Error == ERROR_SUCCESS)
2194         RegisterTypeLibraries(pItemsData, &Notify, hSysSetupInf, L"TypeLibraries");
2195 
2196     /* End the item */
2197     DPRINT("Register Components: done\n");
2198     SendMessage(pItemsData->hwndDlg, PM_ITEM_END, 0, Error);
2199 }
2200 
2201 
2202 static
2203 DWORD
2204 CALLBACK
2205 ItemCompletionThread(
2206     LPVOID Parameter)
2207 {
2208     PITEMSDATA pItemsData;
2209     HWND hwndDlg;
2210 
2211     pItemsData = (PITEMSDATA)Parameter;
2212     hwndDlg = pItemsData->hwndDlg;
2213 
2214     /* Step 0 - Registering components */
2215     RegisterComponents(pItemsData);
2216 
2217     /* Step 1 - Installing start menu items */
2218     InstallStartMenuItems(pItemsData);
2219 
2220     /* FIXME: Add completion steps here! */
2221 
2222     // FIXME: Move this call to a separate cleanup page!
2223     RtlCreateBootStatusDataFile();
2224 
2225     /* Free the items data */
2226     HeapFree(GetProcessHeap(), 0, pItemsData);
2227 
2228     /* Tell the wizard page that we are done */
2229     PostMessage(hwndDlg, PM_ITEMS_DONE, 0, 0);
2230 
2231     return 0;
2232 }
2233 
2234 
2235 static
2236 BOOL
2237 RunItemCompletionThread(
2238     _In_ HWND hwndDlg)
2239 {
2240     HANDLE hCompletionThread;
2241     PITEMSDATA pItemsData;
2242 
2243     pItemsData = HeapAlloc(GetProcessHeap(), 0, sizeof(ITEMSDATA));
2244     if (pItemsData == NULL)
2245         return FALSE;
2246 
2247     pItemsData->hwndDlg = hwndDlg;
2248 
2249     hCompletionThread = CreateThread(NULL,
2250                                      0,
2251                                      ItemCompletionThread,
2252                                      pItemsData,
2253                                      0,
2254                                      NULL);
2255     if (hCompletionThread == NULL)
2256     {
2257         HeapFree(GetProcessHeap(), 0, pItemsData);
2258     }
2259     else
2260     {
2261         CloseHandle(hCompletionThread);
2262         return TRUE;
2263     }
2264 
2265     return FALSE;
2266 }
2267 
2268 static
2269 VOID
2270 ShowItemError(
2271     HWND hwndDlg,
2272     DWORD LastError)
2273 {
2274     LPWSTR ErrorMessage = NULL;
2275     WCHAR UnknownError[84];
2276     WCHAR Title[64];
2277 
2278     if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
2279                        NULL, LastError, 0, ErrorMessage, 0, NULL) == 0)
2280     {
2281         if (LoadStringW(hDllInstance, IDS_UNKNOWN_ERROR,
2282                         UnknownError,
2283                         ARRAYSIZE(UnknownError) - 20) == 0)
2284         {
2285             wcscpy(UnknownError, L"Unknown error");
2286         }
2287         wcscat(UnknownError, L" ");
2288         _ultow(LastError, UnknownError + wcslen(UnknownError), 10);
2289         ErrorMessage = UnknownError;
2290     }
2291 
2292     if (ErrorMessage != NULL)
2293     {
2294         if (LoadStringW(hDllInstance, IDS_REACTOS_SETUP,
2295                         Title, ARRAYSIZE(Title)) == 0)
2296         {
2297             wcscpy(Title, L"ReactOS Setup");
2298         }
2299 
2300         MessageBoxW(hwndDlg, ErrorMessage, Title, MB_ICONERROR | MB_OK);
2301     }
2302 
2303     if (ErrorMessage != NULL &&
2304         ErrorMessage != UnknownError)
2305     {
2306         LocalFree(ErrorMessage);
2307     }
2308 }
2309 
2310 
2311 static
2312 VOID
2313 ShowStepError(
2314     HWND hwndDlg,
2315     PREGISTRATIONNOTIFY RegistrationNotify)
2316 {
2317     WCHAR ErrorMessage[128];
2318     WCHAR Title[64];
2319 
2320     if (LoadStringW(hDllInstance, RegistrationNotify->MessageID,
2321                     ErrorMessage,
2322                     ARRAYSIZE(ErrorMessage)) == 0)
2323     {
2324         ErrorMessage[0] = L'\0';
2325     }
2326 
2327     if (RegistrationNotify->MessageID != IDS_TIMEOUT)
2328     {
2329         FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
2330                        RegistrationNotify->LastError, 0,
2331                        ErrorMessage + wcslen(ErrorMessage),
2332                        ARRAYSIZE(ErrorMessage) - wcslen(ErrorMessage),
2333                        NULL);
2334     }
2335 
2336     if (ErrorMessage[0] != L'\0')
2337     {
2338         if (LoadStringW(hDllInstance, IDS_REACTOS_SETUP,
2339                         Title, ARRAYSIZE(Title)) == 0)
2340         {
2341             wcscpy(Title, L"ReactOS Setup");
2342         }
2343 
2344         MessageBoxW(hwndDlg, ErrorMessage,
2345                     Title, MB_ICONERROR | MB_OK);
2346     }
2347 }
2348 
2349 
2350 static INT_PTR CALLBACK
2351 ProcessPageDlgProc(HWND hwndDlg,
2352                    UINT uMsg,
2353                    WPARAM wParam,
2354                    LPARAM lParam)
2355 {
2356     PSETUPDATA SetupData;
2357     PREGISTRATIONNOTIFY RegistrationNotify;
2358 
2359     /* Retrieve pointer to the global setup data */
2360     SetupData = (PSETUPDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
2361 
2362     switch (uMsg)
2363     {
2364         case WM_INITDIALOG:
2365             /* Save pointer to the global setup data */
2366             SetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
2367             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (DWORD_PTR)SetupData);
2368             ShowWindow(GetDlgItem(hwndDlg, IDC_TASKTEXT3), SW_HIDE);
2369             ShowWindow(GetDlgItem(hwndDlg, IDC_TASKTEXT4), SW_HIDE);
2370             break;
2371 
2372         case WM_NOTIFY:
2373             switch (((LPNMHDR)lParam)->code)
2374             {
2375                 case PSN_SETACTIVE:
2376                     LogItem(L"BEGIN", L"ProcessPage");
2377 
2378                     /* Disable the Back and Next buttons */
2379                     PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
2380                     RunItemCompletionThread(hwndDlg);
2381                     break;
2382 
2383                 case PSN_WIZNEXT:
2384                     LogItem(L"END", L"ProcessPage");
2385                     break;
2386 
2387                 case PSN_WIZBACK:
2388                     SetupData->UnattendSetup = FALSE;
2389                     break;
2390 
2391                 default:
2392                     break;
2393             }
2394             break;
2395 
2396         case PM_ITEM_START:
2397             DPRINT("PM_ITEM_START %lu\n", (ULONG)lParam);
2398             SendDlgItemMessage(hwndDlg, IDC_PROCESSPROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, (ULONG)lParam));
2399             SendDlgItemMessage(hwndDlg, IDC_PROCESSPROGRESS, PBM_SETPOS, 0, 0);
2400             SendDlgItemMessage(hwndDlg, IDC_TASKTEXT1 + wParam, WM_SETFONT, (WPARAM)SetupData->hBoldFont, (LPARAM)TRUE);
2401             break;
2402 
2403         case PM_ITEM_END:
2404             DPRINT("PM_ITEM_END\n");
2405             if (lParam == ERROR_SUCCESS)
2406             {
2407             }
2408             else
2409             {
2410                 ShowItemError(hwndDlg, (DWORD)lParam);
2411             }
2412             break;
2413 
2414         case PM_STEP_START:
2415             DPRINT("PM_STEP_START\n");
2416             RegistrationNotify = (PREGISTRATIONNOTIFY)lParam;
2417             SendDlgItemMessage(hwndDlg, IDC_ITEM, WM_SETTEXT, 0,
2418                                (LPARAM)((RegistrationNotify->CurrentItem != NULL)? RegistrationNotify->CurrentItem : L""));
2419             break;
2420 
2421         case PM_STEP_END:
2422             DPRINT("PM_STEP_END\n");
2423             RegistrationNotify = (PREGISTRATIONNOTIFY)lParam;
2424             SendDlgItemMessage(hwndDlg, IDC_PROCESSPROGRESS, PBM_SETPOS, RegistrationNotify->Progress, 0);
2425             if (RegistrationNotify->LastError != ERROR_SUCCESS)
2426             {
2427                 ShowStepError(hwndDlg, RegistrationNotify);
2428             }
2429             break;
2430 
2431         case PM_ITEMS_DONE:
2432             DPRINT("PM_ITEMS_DONE\n");
2433             /* Enable the Back and Next buttons */
2434             PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
2435             PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT);
2436             break;
2437 
2438         default:
2439             break;
2440     }
2441 
2442     return FALSE;
2443 }
2444 
2445 
2446 static VOID
2447 SetInstallationCompleted(VOID)
2448 {
2449     HKEY hKey = 0;
2450     DWORD InProgress = 0;
2451     DWORD InstallDate;
2452 
2453     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE,
2454                        L"SYSTEM\\Setup",
2455                        0,
2456                        KEY_WRITE,
2457                        &hKey ) == ERROR_SUCCESS)
2458     {
2459         RegSetValueExW( hKey, L"SystemSetupInProgress", 0, REG_DWORD, (LPBYTE)&InProgress, sizeof(InProgress) );
2460         RegCloseKey( hKey );
2461     }
2462 
2463     if (RegOpenKeyExW( HKEY_LOCAL_MACHINE,
2464                        L"Software\\Microsoft\\Windows NT\\CurrentVersion",
2465                        0,
2466                        KEY_WRITE,
2467                        &hKey ) == ERROR_SUCCESS)
2468     {
2469         InstallDate = (DWORD)time(NULL);
2470         RegSetValueExW( hKey, L"InstallDate", 0, REG_DWORD, (LPBYTE)&InstallDate, sizeof(InstallDate) );
2471         RegCloseKey( hKey );
2472     }
2473 }
2474 
2475 static INT_PTR CALLBACK
2476 FinishDlgProc(HWND hwndDlg,
2477               UINT uMsg,
2478               WPARAM wParam,
2479               LPARAM lParam)
2480 {
2481 
2482     switch (uMsg)
2483     {
2484         case WM_INITDIALOG:
2485         {
2486             /* Get pointer to the global setup data */
2487             PSETUPDATA SetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
2488 
2489             if (!SetupData->UnattendSetup || !SetupData->DisableGeckoInst)
2490             {
2491                 /* Run the Wine Gecko prompt */
2492                 Control_RunDLLW(hwndDlg, 0, L"appwiz.cpl,,install_gecko", SW_SHOW);
2493             }
2494 
2495             /* Set title font */
2496             SendDlgItemMessage(hwndDlg,
2497                                IDC_FINISHTITLE,
2498                                WM_SETFONT,
2499                                (WPARAM)SetupData->hTitleFont,
2500                                (LPARAM)TRUE);
2501             if (SetupData->UnattendSetup)
2502             {
2503                 KillTimer(hwndDlg, 1);
2504                 SetInstallationCompleted();
2505                 PostQuitMessage(0);
2506             }
2507         }
2508         break;
2509 
2510         case WM_DESTROY:
2511         {
2512             SetInstallationCompleted();
2513             PostQuitMessage(0);
2514             return TRUE;
2515         }
2516 
2517         case WM_TIMER:
2518         {
2519             INT Position;
2520             HWND hWndProgress;
2521 
2522             hWndProgress = GetDlgItem(hwndDlg, IDC_RESTART_PROGRESS);
2523             Position = SendMessage(hWndProgress, PBM_GETPOS, 0, 0);
2524             if (Position == 300)
2525             {
2526                 KillTimer(hwndDlg, 1);
2527                 PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH);
2528             }
2529             else
2530             {
2531                 SendMessage(hWndProgress, PBM_SETPOS, Position + 1, 0);
2532             }
2533         }
2534         return TRUE;
2535 
2536         case WM_NOTIFY:
2537         {
2538             LPNMHDR lpnm = (LPNMHDR)lParam;
2539 
2540             switch (lpnm->code)
2541             {
2542                 case PSN_SETACTIVE:
2543                     /* Enable the correct buttons on for the active page */
2544                     PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
2545 
2546                     SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETRANGE, 0,
2547                                        MAKELPARAM(0, 300));
2548                     SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETPOS, 0, 0);
2549                     SetTimer(hwndDlg, 1, 50, NULL);
2550                     break;
2551 
2552                 case PSN_WIZFINISH:
2553                     DestroyWindow(GetParent(hwndDlg));
2554                     break;
2555 
2556                 default:
2557                     break;
2558             }
2559         }
2560         break;
2561 
2562         default:
2563             break;
2564     }
2565 
2566     return FALSE;
2567 }
2568 
2569 
2570 /*
2571  * GetInstallSourceWin32 retrieves the path to the ReactOS installation medium
2572  * in Win32 format, for later use by syssetup and storage in the registry.
2573  */
2574 static BOOL
2575 GetInstallSourceWin32(
2576     OUT PWSTR pwszPath,
2577     IN DWORD cchPathMax,
2578     IN PCWSTR pwszNTPath)
2579 {
2580     WCHAR wszDrives[512];
2581     WCHAR wszNTPath[512]; // MAX_PATH ?
2582     DWORD cchDrives;
2583     PWCHAR pwszDrive;
2584 
2585     *pwszPath = UNICODE_NULL;
2586 
2587     cchDrives = GetLogicalDriveStringsW(_countof(wszDrives) - 1, wszDrives);
2588     if (cchDrives == 0 || cchDrives >= _countof(wszDrives))
2589     {
2590         /* Buffer too small or failure */
2591         LogItem(NULL, L"GetLogicalDriveStringsW failed");
2592         return FALSE;
2593     }
2594 
2595     for (pwszDrive = wszDrives; *pwszDrive; pwszDrive += wcslen(pwszDrive) + 1)
2596     {
2597         WCHAR wszBuf[MAX_PATH];
2598 
2599         /* Retrieve the NT path corresponding to the current Win32 DOS path */
2600         pwszDrive[2] = UNICODE_NULL; // Temporarily remove the backslash
2601         QueryDosDeviceW(pwszDrive, wszNTPath, _countof(wszNTPath));
2602         pwszDrive[2] = L'\\';        // Restore the backslash
2603 
2604         wcscat(wszNTPath, L"\\");    // Concat a backslash
2605 
2606         /* Logging */
2607         wsprintf(wszBuf, L"Testing '%s' --> '%s' %s a CD",
2608                  pwszDrive, wszNTPath,
2609                  (GetDriveTypeW(pwszDrive) == DRIVE_CDROM) ? L"is" : L"is not");
2610         LogItem(NULL, wszBuf);
2611 
2612         /* Check whether the NT path corresponds to the NT installation source path */
2613         if (!_wcsicmp(wszNTPath, pwszNTPath))
2614         {
2615             /* Found it! */
2616             wcscpy(pwszPath, pwszDrive); // cchPathMax
2617 
2618             /* Logging */
2619             wsprintf(wszBuf, L"GetInstallSourceWin32: %s", pwszPath);
2620             LogItem(NULL, wszBuf);
2621             wcscat(wszBuf, L"\n");
2622             OutputDebugStringW(wszBuf);
2623 
2624             return TRUE;
2625         }
2626     }
2627 
2628     return FALSE;
2629 }
2630 
2631 VOID
2632 ProcessUnattendSection(
2633     IN OUT PSETUPDATA pSetupData)
2634 {
2635     INFCONTEXT InfContext;
2636     WCHAR szName[256];
2637     WCHAR szValue[MAX_PATH];
2638     DWORD LineLength;
2639     HKEY hKey;
2640 
2641     if (!SetupFindFirstLineW(pSetupData->hSetupInf,
2642                              L"Unattend",
2643                              L"UnattendSetupEnabled",
2644                              &InfContext))
2645     {
2646         DPRINT1("Error: Cannot find UnattendSetupEnabled Key! %d\n", GetLastError());
2647         return;
2648     }
2649 
2650     if (!SetupGetStringFieldW(&InfContext,
2651                               1,
2652                               szValue,
2653                               ARRAYSIZE(szValue),
2654                               &LineLength))
2655     {
2656         DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2657         return;
2658     }
2659 
2660     if (_wcsicmp(szValue, L"yes") != 0)
2661     {
2662         DPRINT("Unattend setup was disabled by UnattendSetupEnabled key.\n");
2663         return;
2664     }
2665 
2666     pSetupData->UnattendSetup = TRUE;
2667 
2668     if (!SetupFindFirstLineW(pSetupData->hSetupInf,
2669                              L"Unattend",
2670                              NULL,
2671                              &InfContext))
2672     {
2673         DPRINT1("Error: SetupFindFirstLine failed %d\n", GetLastError());
2674         return;
2675     }
2676 
2677     do
2678     {
2679         if (!SetupGetStringFieldW(&InfContext,
2680                                   0,
2681                                   szName,
2682                                   ARRAYSIZE(szName),
2683                                   &LineLength))
2684         {
2685             DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2686             return;
2687         }
2688 
2689         if (!SetupGetStringFieldW(&InfContext,
2690                                   1,
2691                                   szValue,
2692                                   ARRAYSIZE(szValue),
2693                                   &LineLength))
2694         {
2695             DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2696             return;
2697         }
2698         DPRINT1("Name %S Value %S\n", szName, szValue);
2699         if (!_wcsicmp(szName, L"FullName"))
2700         {
2701             if (ARRAYSIZE(pSetupData->OwnerName) > LineLength)
2702             {
2703                 wcscpy(pSetupData->OwnerName, szValue);
2704             }
2705         }
2706         else if (!_wcsicmp(szName, L"OrgName"))
2707         {
2708             if (ARRAYSIZE(pSetupData->OwnerOrganization) > LineLength)
2709             {
2710                 wcscpy(pSetupData->OwnerOrganization, szValue);
2711             }
2712         }
2713         else if (!_wcsicmp(szName, L"ComputerName"))
2714         {
2715             if (ARRAYSIZE(pSetupData->ComputerName) > LineLength)
2716             {
2717                 wcscpy(pSetupData->ComputerName, szValue);
2718             }
2719         }
2720         else if (!_wcsicmp(szName, L"AdminPassword"))
2721         {
2722             if (ARRAYSIZE(pSetupData->AdminPassword) > LineLength)
2723             {
2724                 wcscpy(pSetupData->AdminPassword, szValue);
2725             }
2726         }
2727         else if (!_wcsicmp(szName, L"TimeZoneIndex"))
2728         {
2729             pSetupData->TimeZoneIndex = _wtoi(szValue);
2730         }
2731         else if (!_wcsicmp(szName, L"DisableAutoDaylightTimeSet"))
2732         {
2733             pSetupData->DisableAutoDaylightTimeSet = _wtoi(szValue);
2734         }
2735         else if (!_wcsicmp(szName, L"DisableGeckoInst"))
2736         {
2737             if (!_wcsicmp(szValue, L"yes"))
2738                 pSetupData->DisableGeckoInst = TRUE;
2739             else
2740                 pSetupData->DisableGeckoInst = FALSE;
2741         }
2742         else if (!_wcsicmp(szName, L"ProductOption"))
2743         {
2744             pSetupData->ProductOption = (PRODUCT_OPTION)_wtoi(szValue);
2745         }
2746     } while (SetupFindNextLine(&InfContext, &InfContext));
2747 
2748     if (SetupFindFirstLineW(pSetupData->hSetupInf,
2749                             L"Display",
2750                             NULL,
2751                             &InfContext))
2752     {
2753         DEVMODEW dm = { { 0 } };
2754         dm.dmSize = sizeof(dm);
2755         if (EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &dm))
2756         {
2757             do
2758             {
2759                 int iValue;
2760                 if (!SetupGetStringFieldW(&InfContext,
2761                                           0,
2762                                           szName,
2763                                           ARRAYSIZE(szName),
2764                                           &LineLength))
2765                 {
2766                     DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2767                     return;
2768                 }
2769 
2770                 if (!SetupGetStringFieldW(&InfContext,
2771                                           1,
2772                                           szValue,
2773                                           ARRAYSIZE(szValue),
2774                                           &LineLength))
2775                 {
2776                     DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2777                     return;
2778                 }
2779                 iValue = _wtoi(szValue);
2780                 DPRINT1("Name %S Value %i\n", szName, iValue);
2781 
2782                 if (!iValue)
2783                     continue;
2784 
2785                 if (!_wcsicmp(szName, L"BitsPerPel"))
2786                 {
2787                     dm.dmFields |= DM_BITSPERPEL;
2788                     dm.dmBitsPerPel = iValue;
2789                 }
2790                 else if (!_wcsicmp(szName, L"XResolution"))
2791                 {
2792                     dm.dmFields |= DM_PELSWIDTH;
2793                     dm.dmPelsWidth = iValue;
2794                 }
2795                 else if (!_wcsicmp(szName, L"YResolution"))
2796                 {
2797                     dm.dmFields |= DM_PELSHEIGHT;
2798                     dm.dmPelsHeight = iValue;
2799                 }
2800                 else if (!_wcsicmp(szName, L"VRefresh"))
2801                 {
2802                     dm.dmFields |= DM_DISPLAYFREQUENCY;
2803                     dm.dmDisplayFrequency = iValue;
2804                 }
2805             } while (SetupFindNextLine(&InfContext, &InfContext));
2806 
2807             ChangeDisplaySettingsW(&dm, CDS_UPDATEREGISTRY);
2808         }
2809     }
2810 
2811     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2812                       L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
2813                       0,
2814                       KEY_SET_VALUE,
2815                       &hKey) != ERROR_SUCCESS)
2816     {
2817         DPRINT1("Error: failed to open HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce\n");
2818         return;
2819     }
2820 
2821     if (SetupFindFirstLineW(pSetupData->hSetupInf,
2822                             L"GuiRunOnce",
2823                             NULL,
2824                             &InfContext))
2825     {
2826         int i = 0;
2827         do
2828         {
2829             if (SetupGetStringFieldW(&InfContext,
2830                                      0,
2831                                      szValue,
2832                                      ARRAYSIZE(szValue),
2833                                      NULL))
2834             {
2835                 WCHAR szPath[MAX_PATH];
2836                 swprintf(szName, L"%d", i);
2837                 DPRINT("szName %S szValue %S\n", szName, szValue);
2838 
2839                 if (ExpandEnvironmentStringsW(szValue, szPath, MAX_PATH))
2840                 {
2841                     DPRINT("value %S\n", szPath);
2842                     if (RegSetValueExW(hKey,
2843                                        szName,
2844                                        0,
2845                                        REG_SZ,
2846                                        (const BYTE*)szPath,
2847                                        (wcslen(szPath) + 1) * sizeof(WCHAR)) == ERROR_SUCCESS)
2848                     {
2849                         i++;
2850                     }
2851                 }
2852             }
2853         } while (SetupFindNextLine(&InfContext, &InfContext));
2854     }
2855 
2856     RegCloseKey(hKey);
2857 
2858     if (SetupFindFirstLineW(pSetupData->hSetupInf,
2859         L"Env",
2860         NULL,
2861         &InfContext))
2862     {
2863         if (RegCreateKeyExW(
2864                 HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, NULL,
2865                 REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL, &hKey, NULL) != ERROR_SUCCESS)
2866         {
2867             DPRINT1("Error: failed to open HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\n");
2868             return;
2869         }
2870         do
2871         {
2872             if (!SetupGetStringFieldW(&InfContext,
2873                 0,
2874                 szName,
2875                 ARRAYSIZE(szName),
2876                 &LineLength))
2877             {
2878                 DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2879                 return;
2880             }
2881 
2882             if (!SetupGetStringFieldW(&InfContext,
2883                 1,
2884                 szValue,
2885                 ARRAYSIZE(szValue),
2886                 &LineLength))
2887             {
2888                 DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
2889                 return;
2890             }
2891             DPRINT1("[ENV] %S=%S\n", szName, szValue);
2892 
2893             DWORD dwType = wcschr(szValue, '%') != NULL ? REG_EXPAND_SZ : REG_SZ;
2894 
2895             if (RegSetValueExW(hKey, szName, 0, dwType, (const BYTE*)szValue, (DWORD)(wcslen(szValue) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS)
2896             {
2897                 DPRINT1(" - Error %d\n", GetLastError());
2898             }
2899 
2900         } while (SetupFindNextLine(&InfContext, &InfContext));
2901 
2902         RegCloseKey(hKey);
2903     }
2904 }
2905 
2906 static BOOL
2907 PathIsEqual(
2908     IN LPCWSTR lpPath1,
2909     IN LPCWSTR lpPath2)
2910 {
2911     WCHAR szPath1[MAX_PATH];
2912     WCHAR szPath2[MAX_PATH];
2913 
2914     /* If something goes wrong, better return TRUE,
2915      * so the calling function returns early.
2916      */
2917     if (!PathCanonicalizeW(szPath1, lpPath1))
2918         return TRUE;
2919 
2920     if (!PathAddBackslashW(szPath1))
2921         return TRUE;
2922 
2923     if (!PathCanonicalizeW(szPath2, lpPath2))
2924         return TRUE;
2925 
2926     if (!PathAddBackslashW(szPath2))
2927         return TRUE;
2928 
2929     return (_wcsicmp(szPath1, szPath2) == 0);
2930 }
2931 
2932 static VOID
2933 AddInstallationSource(
2934     IN HKEY hKey,
2935     IN LPWSTR lpPath)
2936 {
2937     LONG res;
2938     DWORD dwRegType;
2939     DWORD dwPathLength = 0;
2940     DWORD dwNewLength = 0;
2941     LPWSTR Buffer = NULL;
2942     LPWSTR Path;
2943 
2944     res = RegQueryValueExW(
2945         hKey,
2946         L"Installation Sources",
2947         NULL,
2948         &dwRegType,
2949         NULL,
2950         &dwPathLength);
2951 
2952     if (res != ERROR_SUCCESS ||
2953         dwRegType != REG_MULTI_SZ ||
2954         dwPathLength == 0 ||
2955         dwPathLength % sizeof(WCHAR) != 0)
2956     {
2957         dwPathLength = 0;
2958         goto set;
2959     }
2960 
2961     /* Reserve space for existing data + new string */
2962     dwNewLength = dwPathLength + (wcslen(lpPath) + 1) * sizeof(WCHAR);
2963     Buffer = HeapAlloc(GetProcessHeap(), 0, dwNewLength);
2964     if (!Buffer)
2965         return;
2966 
2967     ZeroMemory(Buffer, dwNewLength);
2968 
2969     res = RegQueryValueExW(
2970         hKey,
2971         L"Installation Sources",
2972         NULL,
2973         NULL,
2974         (LPBYTE)Buffer,
2975         &dwPathLength);
2976 
2977     if (res != ERROR_SUCCESS)
2978     {
2979         HeapFree(GetProcessHeap(), 0, Buffer);
2980         dwPathLength = 0;
2981         goto set;
2982     }
2983 
2984     /* Sanity check, these should already be zeros */
2985     Buffer[dwPathLength / sizeof(WCHAR) - 2] = UNICODE_NULL;
2986     Buffer[dwPathLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
2987 
2988     for (Path = Buffer; *Path; Path += wcslen(Path) + 1)
2989     {
2990         /* Check if path is already added */
2991         if (PathIsEqual(Path, lpPath))
2992             goto cleanup;
2993     }
2994 
2995     Path = Buffer + dwPathLength / sizeof(WCHAR) - 1;
2996 
2997 set:
2998     if (dwPathLength == 0)
2999     {
3000         dwNewLength = (wcslen(lpPath) + 1 + 1) * sizeof(WCHAR);
3001         Buffer = HeapAlloc(GetProcessHeap(), 0, dwNewLength);
3002         if (!Buffer)
3003             return;
3004 
3005         Path = Buffer;
3006     }
3007 
3008     StringCbCopyW(Path, dwNewLength - (Path - Buffer) * sizeof(WCHAR), lpPath);
3009     Buffer[dwNewLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
3010 
3011     RegSetValueExW(
3012         hKey,
3013         L"Installation Sources",
3014         0,
3015         REG_MULTI_SZ,
3016         (LPBYTE)Buffer,
3017         dwNewLength);
3018 
3019 cleanup:
3020     HeapFree(GetProcessHeap(), 0, Buffer);
3021 }
3022 
3023 VOID
3024 ProcessSetupInf(
3025     IN OUT PSETUPDATA pSetupData)
3026 {
3027     WCHAR szPath[MAX_PATH];
3028     WCHAR szValue[MAX_PATH];
3029     INFCONTEXT InfContext;
3030     DWORD LineLength;
3031     HKEY hKey;
3032     LONG res;
3033 
3034     pSetupData->hSetupInf = INVALID_HANDLE_VALUE;
3035 
3036     /* Retrieve the path of the setup INF */
3037     GetSystemDirectoryW(szPath, _countof(szPath));
3038     wcscat(szPath, L"\\$winnt$.inf");
3039 
3040     /* Open the setup INF */
3041     pSetupData->hSetupInf = SetupOpenInfFileW(szPath,
3042                                               NULL,
3043                                               INF_STYLE_OLDNT,
3044                                               NULL);
3045     if (pSetupData->hSetupInf == INVALID_HANDLE_VALUE)
3046     {
3047         DPRINT1("Error: Cannot open the setup information file %S with error %d\n", szPath, GetLastError());
3048         return;
3049     }
3050 
3051 
3052     /* Retrieve the NT source path from which the 1st-stage installer was run */
3053     if (!SetupFindFirstLineW(pSetupData->hSetupInf,
3054                              L"data",
3055                              L"sourcepath",
3056                              &InfContext))
3057     {
3058         DPRINT1("Error: Cannot find sourcepath Key! %d\n", GetLastError());
3059         return;
3060     }
3061 
3062     if (!SetupGetStringFieldW(&InfContext,
3063                               1,
3064                               szValue,
3065                               ARRAYSIZE(szValue),
3066                               &LineLength))
3067     {
3068         DPRINT1("Error: SetupGetStringField failed with %d\n", GetLastError());
3069         return;
3070     }
3071 
3072     *pSetupData->SourcePath = UNICODE_NULL;
3073 
3074     /* Close the setup INF as we are going to modify it manually */
3075     if (pSetupData->hSetupInf != INVALID_HANDLE_VALUE)
3076         SetupCloseInfFile(pSetupData->hSetupInf);
3077 
3078 
3079     /* Find the installation source path in Win32 format */
3080     if (!GetInstallSourceWin32(pSetupData->SourcePath,
3081                                _countof(pSetupData->SourcePath),
3082                                szValue))
3083     {
3084         *pSetupData->SourcePath = UNICODE_NULL;
3085     }
3086 
3087     /* Save the path in Win32 format in the setup INF */
3088     swprintf(szValue, L"\"%s\"", pSetupData->SourcePath);
3089     WritePrivateProfileStringW(L"data", L"dospath", szValue, szPath);
3090 
3091     /*
3092      * Save it also in the registry, in the following keys:
3093      * - HKLM\Software\Microsoft\Windows\CurrentVersion\Setup ,
3094      *   values "SourcePath" and "ServicePackSourcePath" (REG_SZ);
3095      * - HKLM\Software\Microsoft\Windows NT\CurrentVersion ,
3096      *   value "SourcePath" (REG_SZ); set to the full path (e.g. D:\I386).
3097      */
3098 #if 0
3099     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
3100                         L"Software\\Microsoft\\Windows NT\\CurrentVersion",
3101                         0,
3102                         KEY_ALL_ACCESS,
3103                         &hKey);
3104 
3105     if (res != ERROR_SUCCESS)
3106     {
3107         return FALSE;
3108     }
3109 #endif
3110 
3111     res = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
3112                           L"Software\\Microsoft\\Windows\\CurrentVersion\\Setup",
3113                           0, NULL,
3114                           REG_OPTION_NON_VOLATILE,
3115                           KEY_ALL_ACCESS, // KEY_WRITE
3116                           NULL,
3117                           &hKey,
3118                           NULL);
3119     if (res == ERROR_SUCCESS)
3120     {
3121         AddInstallationSource(hKey, pSetupData->SourcePath);
3122 
3123         res = RegSetValueExW(hKey,
3124                              L"SourcePath",
3125                              0,
3126                              REG_SZ,
3127                              (LPBYTE)pSetupData->SourcePath,
3128                              (wcslen(pSetupData->SourcePath) + 1) * sizeof(WCHAR));
3129 
3130         res = RegSetValueExW(hKey,
3131                              L"ServicePackSourcePath",
3132                              0,
3133                              REG_SZ,
3134                              (LPBYTE)pSetupData->SourcePath,
3135                              (wcslen(pSetupData->SourcePath) + 1) * sizeof(WCHAR));
3136 
3137         RegCloseKey(hKey);
3138     }
3139 
3140 
3141     /* Now, re-open the setup INF (this must succeed) */
3142     pSetupData->hSetupInf = SetupOpenInfFileW(szPath,
3143                                               NULL,
3144                                               INF_STYLE_OLDNT,
3145                                               NULL);
3146     if (pSetupData->hSetupInf == INVALID_HANDLE_VALUE)
3147     {
3148         DPRINT1("Error: Cannot open the setup information file %S with error %d\n", szPath, GetLastError());
3149         return;
3150     }
3151 
3152     /* Process the unattended section of the setup file */
3153     ProcessUnattendSection(pSetupData);
3154 }
3155 
3156 typedef DWORD(WINAPI *PFNREQUESTWIZARDPAGES)(PDWORD, HPROPSHEETPAGE *, PSETUPDATA);
3157 
3158 VOID
3159 InstallWizard(VOID)
3160 {
3161     PROPSHEETHEADER psh = {0};
3162     HPROPSHEETPAGE *phpage = NULL;
3163     PROPSHEETPAGE psp = {0};
3164     UINT nPages = 0;
3165     HWND hWnd;
3166     MSG msg;
3167     PSETUPDATA pSetupData = NULL;
3168     HMODULE hNetShell = NULL;
3169     PFNREQUESTWIZARDPAGES pfn = NULL;
3170     DWORD dwPageCount = 10, dwNetworkPageCount = 0;
3171 
3172     LogItem(L"BEGIN_SECTION", L"InstallWizard");
3173 
3174     /* Allocate setup data */
3175     pSetupData = HeapAlloc(GetProcessHeap(),
3176                            HEAP_ZERO_MEMORY,
3177                            sizeof(SETUPDATA));
3178     if (pSetupData == NULL)
3179     {
3180         LogItem(NULL, L"SetupData allocation failed!");
3181         MessageBoxW(NULL,
3182                     L"Setup failed to allocate global data!",
3183                     L"ReactOS Setup",
3184                     MB_ICONERROR | MB_OK);
3185         goto done;
3186     }
3187     pSetupData->ProductOption = PRODUCT_OPTION_DEFAULT;
3188 
3189     hNetShell = LoadLibraryW(L"netshell.dll");
3190     if (hNetShell != NULL)
3191     {
3192         DPRINT("Netshell.dll loaded!\n");
3193 
3194         pfn = (PFNREQUESTWIZARDPAGES)GetProcAddress(hNetShell,
3195                                                     "NetSetupRequestWizardPages");
3196         if (pfn != NULL)
3197         {
3198             pfn(&dwNetworkPageCount, NULL, NULL);
3199             dwPageCount += dwNetworkPageCount;
3200         }
3201     }
3202 
3203     DPRINT("PageCount: %lu\n", dwPageCount);
3204 
3205     phpage = HeapAlloc(GetProcessHeap(),
3206                        HEAP_ZERO_MEMORY,
3207                        dwPageCount * sizeof(HPROPSHEETPAGE));
3208     if (phpage == NULL)
3209     {
3210         LogItem(NULL, L"Page array allocation failed!");
3211         MessageBoxW(NULL,
3212                     L"Setup failed to allocate page array!",
3213                     L"ReactOS Setup",
3214                     MB_ICONERROR | MB_OK);
3215         goto done;
3216     }
3217 
3218     /* Process the $winnt$.inf setup file */
3219     ProcessSetupInf(pSetupData);
3220 
3221     /* Create the Welcome page */
3222     psp.dwSize = sizeof(PROPSHEETPAGE);
3223     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
3224     psp.hInstance = hDllInstance;
3225     psp.lParam = (LPARAM)pSetupData;
3226     psp.pfnDlgProc = WelcomeDlgProc;
3227     psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE);
3228     phpage[nPages++] = CreatePropertySheetPage(&psp);
3229 
3230     /* Create the Acknowledgements page */
3231     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3232     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_ACKTITLE);
3233     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_ACKSUBTITLE);
3234     psp.pszTemplate = MAKEINTRESOURCE(IDD_ACKPAGE);
3235     psp.pfnDlgProc = AckPageDlgProc;
3236     phpage[nPages++] = CreatePropertySheetPage(&psp);
3237 
3238     /* Create the Product page */
3239     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3240     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_PRODUCTTITLE);
3241     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_PRODUCTSUBTITLE);
3242     psp.pszTemplate = MAKEINTRESOURCE(IDD_PRODUCT);
3243     psp.pfnDlgProc = ProductPageDlgProc;
3244     phpage[nPages++] = CreatePropertySheetPage(&psp);
3245 
3246     /* Create the Locale page */
3247     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3248     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_LOCALETITLE);
3249     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_LOCALESUBTITLE);
3250     psp.pfnDlgProc = LocalePageDlgProc;
3251     psp.pszTemplate = MAKEINTRESOURCE(IDD_LOCALEPAGE);
3252     phpage[nPages++] = CreatePropertySheetPage(&psp);
3253 
3254     /* Create the Owner page */
3255     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3256     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_OWNERTITLE);
3257     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_OWNERSUBTITLE);
3258     psp.pszTemplate = MAKEINTRESOURCE(IDD_OWNERPAGE);
3259     psp.pfnDlgProc = OwnerPageDlgProc;
3260     phpage[nPages++] = CreatePropertySheetPage(&psp);
3261 
3262     /* Create the Computer page */
3263     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3264     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_COMPUTERTITLE);
3265     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_COMPUTERSUBTITLE);
3266     psp.pfnDlgProc = ComputerPageDlgProc;
3267     psp.pszTemplate = MAKEINTRESOURCE(IDD_COMPUTERPAGE);
3268     phpage[nPages++] = CreatePropertySheetPage(&psp);
3269 
3270     /* Create the DateTime page */
3271     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3272     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_DATETIMETITLE);
3273     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_DATETIMESUBTITLE);
3274     psp.pfnDlgProc = DateTimePageDlgProc;
3275     psp.pszTemplate = MAKEINTRESOURCE(IDD_DATETIMEPAGE);
3276     phpage[nPages++] = CreatePropertySheetPage(&psp);
3277 
3278     /* Create the theme selection page */
3279     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3280     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_THEMESELECTIONTITLE);
3281     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_THEMESELECTIONSUBTITLE);
3282     psp.pfnDlgProc = ThemePageDlgProc;
3283     psp.pszTemplate = MAKEINTRESOURCE(IDD_THEMEPAGE);
3284     phpage[nPages++] = CreatePropertySheetPage(&psp);
3285 
3286     pSetupData->uFirstNetworkWizardPage = IDD_PROCESSPAGE;
3287     pSetupData->uPostNetworkWizardPage = IDD_PROCESSPAGE;
3288 
3289     if (pfn)
3290     {
3291         pfn(&dwNetworkPageCount, &phpage[nPages], pSetupData);
3292         nPages += dwNetworkPageCount;
3293     }
3294 
3295     /* Create the Process page */
3296     psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
3297     psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_PROCESSTITLE);
3298     psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_PROCESSSUBTITLE);
3299     psp.pfnDlgProc = ProcessPageDlgProc;
3300     psp.pszTemplate = MAKEINTRESOURCE(IDD_PROCESSPAGE);
3301     phpage[nPages++] = CreatePropertySheetPage(&psp);
3302 
3303     /* Create the Finish page */
3304     psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
3305     psp.pfnDlgProc = FinishDlgProc;
3306     psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE);
3307     phpage[nPages++] = CreatePropertySheetPage(&psp);
3308 
3309     ASSERT(nPages == dwPageCount);
3310 
3311     /* Create the property sheet */
3312     psh.dwSize = sizeof(PROPSHEETHEADER);
3313     psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER | PSH_MODELESS;
3314     psh.hInstance = hDllInstance;
3315     psh.hwndParent = NULL;
3316     psh.nPages = nPages;
3317     psh.nStartPage = 0;
3318     psh.phpage = phpage;
3319     psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
3320     psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
3321 
3322     /* Create title font */
3323     pSetupData->hTitleFont = CreateTitleFont();
3324     pSetupData->hBoldFont  = CreateBoldFont();
3325 
3326     /* Display the wizard */
3327     hWnd = (HWND)PropertySheet(&psh);
3328     ShowWindow(hWnd, SW_SHOW);
3329 
3330     while (GetMessage(&msg, NULL, 0, 0))
3331     {
3332         if (!IsDialogMessage(hWnd, &msg))
3333         {
3334             TranslateMessage(&msg);
3335             DispatchMessage(&msg);
3336         }
3337     }
3338 
3339     DeleteObject(pSetupData->hBoldFont);
3340     DeleteObject(pSetupData->hTitleFont);
3341 
3342     if (pSetupData->hSetupInf != INVALID_HANDLE_VALUE)
3343         SetupCloseInfFile(pSetupData->hSetupInf);
3344 
3345 done:
3346     if (phpage != NULL)
3347         HeapFree(GetProcessHeap(), 0, phpage);
3348 
3349     if (hNetShell != NULL)
3350         FreeLibrary(hNetShell);
3351 
3352     if (pSetupData != NULL)
3353         HeapFree(GetProcessHeap(), 0, pSetupData);
3354 
3355     LogItem(L"END_SECTION", L"InstallWizard");
3356 }
3357 
3358 /* EOF */
3359