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