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