[USETUP] Diverse fixes for path handling.

- Convert almost all swprintf() into StringCchPrintfW() and wcscpy() into StringCchCopyW();
- Explicitly add a trailing path separator to the "\Device\HarddiskX\PartitionY(\)" paths
  when they refer to FS directories (and not to partition objects);
- Remove useless (and half-buggy) "Remove trailing backslash" code.

With that, it is possible to install ReactOS in e.g. C:\ReactOS (as usual), C:\ReactOS\dir1\dir2 (as many dirs as you wish), and also in C:\ (yes yes!).
But in that latter case, a strange bug related to the registry arises...

Additionally:
- Adjust some comments;
- Add some debugging DPRINTs;
- The SetInstallPathValue() is part of the big hack I've mentioned in f51faa4a (r74709).

svn path=/branches/setup_improvements/; revision=74717
This commit is contained in:
Hermès Bélusca-Maïto 2017-05-31 02:19:08 +00:00
parent 3cb1392eff
commit 57402ee91e
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 92 additions and 94 deletions

View file

@ -38,13 +38,16 @@
#define NDEBUG #define NDEBUG
#include <debug.h> #include <debug.h>
// HACK!
#include <strsafe.h>
/* GLOBALS ******************************************************************/ /* GLOBALS ******************************************************************/
HANDLE ProcessHeap; HANDLE ProcessHeap;
UNICODE_STRING SourceRootPath; static UNICODE_STRING SourceRootPath;
UNICODE_STRING SourceRootDir; static UNICODE_STRING SourceRootDir;
UNICODE_STRING SourcePath; /* static */ UNICODE_STRING SourcePath;
BOOLEAN IsUnattendedSetup = FALSE; BOOLEAN IsUnattendedSetup = FALSE;
LONG UnattendDestinationDiskNumber; LONG UnattendDestinationDiskNumber;
LONG UnattendDestinationPartitionNumber; LONG UnattendDestinationPartitionNumber;
@ -427,7 +430,7 @@ CheckUnattendedSetup(VOID)
INT IntValue; INT IntValue;
PWCHAR Value; PWCHAR Value;
CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, SourcePath.Buffer, L"\\unattend.inf"); CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, SourcePath.Buffer, L"unattend.inf");
if (DoesFileExist(NULL, UnattendInfPath) == FALSE) if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
{ {
@ -580,7 +583,7 @@ CheckUnattendedSetup(VOID)
if (INF_GetData(&Context, NULL, &Value)) if (INF_GetData(&Context, NULL, &Value))
{ {
LONG Id = wcstol(Value, NULL, 16); LONG Id = wcstol(Value, NULL, 16);
swprintf(LocaleID,L"%08lx", Id); swprintf(LocaleID, L"%08lx", Id);
} }
} }
@ -806,24 +809,18 @@ SetupStartPage(PINPUT_RECORD Ir)
Status = GetSourcePaths(&SourcePath, Status = GetSourcePaths(&SourcePath,
&SourceRootPath, &SourceRootPath,
&SourceRootDir); &SourceRootDir);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
CONSOLE_PrintTextXY(6, 15, "GetSourcePaths() failed (Status 0x%08lx)", Status); CONSOLE_PrintTextXY(6, 15, "GetSourcePaths() failed (Status 0x%08lx)", Status);
MUIDisplayError(ERROR_NO_SOURCE_DRIVE, Ir, POPUP_WAIT_ENTER); MUIDisplayError(ERROR_NO_SOURCE_DRIVE, Ir, POPUP_WAIT_ENTER);
return QUIT_PAGE; return QUIT_PAGE;
} }
#if 0 DPRINT1("SourcePath: '%wZ'", &SourcePath);
else DPRINT1("SourceRootPath: '%wZ'", &SourceRootPath);
{ DPRINT1("SourceRootDir: '%wZ'", &SourceRootDir);
CONSOLE_PrintTextXY(6, 15, "SourcePath: '%wZ'", &SourcePath);
CONSOLE_PrintTextXY(6, 16, "SourceRootPath: '%wZ'", &SourceRootPath);
CONSOLE_PrintTextXY(6, 17, "SourceRootDir: '%wZ'", &SourceRootDir);
}
#endif
/* Load txtsetup.sif from install media. */ /* Load txtsetup.sif from install media. */
CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2, SourcePath.Buffer, L"\\txtsetup.sif"); CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2, SourcePath.Buffer, L"txtsetup.sif");
SetupInf = SetupOpenInfFileW(FileNameBuffer, SetupInf = SetupOpenInfFileW(FileNameBuffer,
NULL, NULL,
INF_STYLE_WIN4, INF_STYLE_WIN4,
@ -2639,6 +2636,13 @@ SelectFileSystemPage(PINPUT_RECORD Ir)
if (PartitionList->SystemPartition == NULL) if (PartitionList->SystemPartition == NULL)
{ {
/* FIXME: show an error dialog */ /* FIXME: show an error dialog */
//
// Error dialog should say that we cannot find a suitable
// system partition and create one on the system. At this point,
// it may be nice to ask the user whether he wants to continue,
// or use an external drive as the system drive/partition
// (e.g. floppy, USB drive, etc...)
//
return QUIT_PAGE; return QUIT_PAGE;
} }
@ -3026,12 +3030,11 @@ FormatPartitionPage(PINPUT_RECORD Ir)
} }
/* Set PartitionRootPath */ /* Set PartitionRootPath */
swprintf(PathBuffer, StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\\Device\\Harddisk%lu\\Partition%lu", L"\\Device\\Harddisk%lu\\Partition%lu",
DiskEntry->DiskNumber, DiskEntry->DiskNumber,
PartEntry->PartitionNumber); PartEntry->PartitionNumber);
RtlInitUnicodeString(&PartitionRootPath, RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
PathBuffer);
DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath); DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
/* Format the partition */ /* Format the partition */
@ -3100,10 +3103,10 @@ CheckFileSystemPage(PINPUT_RECORD Ir)
} }
/* Set PartitionRootPath */ /* Set PartitionRootPath */
swprintf(PathBuffer, StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\\Device\\Harddisk%lu\\Partition%lu", L"\\Device\\Harddisk%lu\\Partition%lu",
DiskEntry->DiskNumber, DiskEntry->DiskNumber,
PartEntry->PartitionNumber); PartEntry->PartitionNumber);
RtlInitUnicodeString(&PartitionRootPath, PathBuffer); RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath); DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
@ -3191,10 +3194,10 @@ BuildInstallPaths(PWCHAR InstallDir,
/* Create 'DestinationRootPath' string */ /* Create 'DestinationRootPath' string */
RtlFreeUnicodeString(&DestinationRootPath); RtlFreeUnicodeString(&DestinationRootPath);
swprintf(PathBuffer, StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\\Device\\Harddisk%lu\\Partition%lu", L"\\Device\\Harddisk%lu\\Partition%lu\\",
DiskEntry->DiskNumber, DiskEntry->DiskNumber,
PartEntry->PartitionNumber); PartEntry->PartitionNumber);
RtlCreateUnicodeString(&DestinationRootPath, PathBuffer); RtlCreateUnicodeString(&DestinationRootPath, PathBuffer);
DPRINT("DestinationRootPath: %wZ\n", &DestinationRootPath); DPRINT("DestinationRootPath: %wZ\n", &DestinationRootPath);
@ -3206,10 +3209,10 @@ BuildInstallPaths(PWCHAR InstallDir,
/* Create 'DestinationArcPath' */ /* Create 'DestinationArcPath' */
RtlFreeUnicodeString(&DestinationArcPath); RtlFreeUnicodeString(&DestinationArcPath);
swprintf(PathBuffer, StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"multi(0)disk(0)rdisk(%lu)partition(%lu)", L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
DiskEntry->BiosDiskNumber, DiskEntry->BiosDiskNumber,
PartEntry->PartitionNumber); PartEntry->PartitionNumber);
ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallDir); ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallDir);
RtlCreateUnicodeString(&DestinationArcPath, PathBuffer); RtlCreateUnicodeString(&DestinationArcPath, PathBuffer);
} }
@ -3413,18 +3416,23 @@ AddSectionToCopyQueueCab(HINF InfFile,
PWCHAR DirKeyValue; PWCHAR DirKeyValue;
PWCHAR TargetFileName; PWCHAR TargetFileName;
/*
* This code enumerates the list of files in reactos.dff / reactos.inf
* that need to be extracted from reactos.cab and be installed in their
* respective directories.
*/
/* Search for the SectionName section */ /* Search for the SectionName section */
if (!SetupFindFirstLineW(InfFile, SectionName, NULL, &FilesContext)) if (!SetupFindFirstLineW(InfFile, SectionName, NULL, &FilesContext))
{ {
char Buffer[128]; CHAR Buffer[128];
sprintf(Buffer, MUIGetString(STRING_TXTSETUPFAILED), SectionName); sprintf(Buffer, MUIGetString(STRING_TXTSETUPFAILED), SectionName);
PopupError(Buffer, MUIGetString(STRING_REBOOTCOMPUTER), Ir, POPUP_WAIT_ENTER); PopupError(Buffer, MUIGetString(STRING_REBOOTCOMPUTER), Ir, POPUP_WAIT_ENTER);
return FALSE; return FALSE;
} }
/* /*
* Enumerate the files in the section * Enumerate the files in the section and add them to the file queue.
* and add them to the file queue.
*/ */
do do
{ {
@ -3487,24 +3495,27 @@ AddSectionToCopyQueue(HINF InfFile,
PWCHAR FileKeyValue; PWCHAR FileKeyValue;
PWCHAR DirKeyValue; PWCHAR DirKeyValue;
PWCHAR TargetFileName; PWCHAR TargetFileName;
ULONG Length; WCHAR CompleteOrigDirName[512]; // FIXME: MAX_PATH is not enough?
WCHAR CompleteOrigDirName[512];
if (SourceCabinet) if (SourceCabinet)
return AddSectionToCopyQueueCab(InfFile, L"SourceFiles", SourceCabinet, DestinationPath, Ir); return AddSectionToCopyQueueCab(InfFile, L"SourceFiles", SourceCabinet, DestinationPath, Ir);
/*
* This code enumerates the list of files in txtsetup.sif
* that need to be installed in their respective directories.
*/
/* Search for the SectionName section */ /* Search for the SectionName section */
if (!SetupFindFirstLineW(InfFile, SectionName, NULL, &FilesContext)) if (!SetupFindFirstLineW(InfFile, SectionName, NULL, &FilesContext))
{ {
char Buffer[128]; CHAR Buffer[128];
sprintf(Buffer, MUIGetString(STRING_TXTSETUPFAILED), SectionName); sprintf(Buffer, MUIGetString(STRING_TXTSETUPFAILED), SectionName);
PopupError(Buffer, MUIGetString(STRING_REBOOTCOMPUTER), Ir, POPUP_WAIT_ENTER); PopupError(Buffer, MUIGetString(STRING_REBOOTCOMPUTER), Ir, POPUP_WAIT_ENTER);
return FALSE; return FALSE;
} }
/* /*
* Enumerate the files in the section * Enumerate the files in the section and add them to the file queue.
* and add them to the file queue.
*/ */
do do
{ {
@ -3550,25 +3561,32 @@ AddSectionToCopyQueue(HINF InfFile,
if ((DirKeyValue[0] == UNICODE_NULL) || (DirKeyValue[0] == L'\\' && DirKeyValue[1] == UNICODE_NULL)) if ((DirKeyValue[0] == UNICODE_NULL) || (DirKeyValue[0] == L'\\' && DirKeyValue[1] == UNICODE_NULL))
{ {
/* Installation path */ /* Installation path */
wcscpy(CompleteOrigDirName, SourceRootDir.Buffer); DPRINT("InstallationPath: '%S'\n", DirKeyValue);
StringCchCopyW(CompleteOrigDirName, ARRAYSIZE(CompleteOrigDirName),
SourceRootDir.Buffer);
DPRINT("InstallationPath(2): '%S'\n", CompleteOrigDirName);
} }
else if (DirKeyValue[0] == L'\\') else if (DirKeyValue[0] == L'\\')
{ {
/* Absolute path */ /* Absolute path */
wcscpy(CompleteOrigDirName, DirKeyValue); DPRINT("AbsolutePath: '%S'\n", DirKeyValue);
StringCchCopyW(CompleteOrigDirName, ARRAYSIZE(CompleteOrigDirName),
DirKeyValue);
DPRINT("AbsolutePath(2): '%S'\n", CompleteOrigDirName);
} }
else // if (DirKeyValue[0] != L'\\') else // if (DirKeyValue[0] != L'\\')
{ {
/* Path relative to the installation path */ /* Path relative to the installation path */
DPRINT("RelativePath: '%S'\n", DirKeyValue);
CombinePaths(CompleteOrigDirName, ARRAYSIZE(CompleteOrigDirName), 2, CombinePaths(CompleteOrigDirName, ARRAYSIZE(CompleteOrigDirName), 2,
SourceRootDir.Buffer, DirKeyValue); SourceRootDir.Buffer, DirKeyValue);
}
/* Remove trailing backslash */ DPRINT("RelativePath(2): '%S'\n", CompleteOrigDirName);
Length = wcslen(CompleteOrigDirName);
if ((Length > 0) && (CompleteOrigDirName[Length - 1] == L'\\'))
{
CompleteOrigDirName[Length - 1] = UNICODE_NULL;
} }
if (!SetupQueueCopy(SetupFileQueue, if (!SetupQueueCopy(SetupFileQueue,
@ -3593,12 +3611,11 @@ PrepareCopyPageInfFile(HINF InfFile,
PWCHAR SourceCabinet, PWCHAR SourceCabinet,
PINPUT_RECORD Ir) PINPUT_RECORD Ir)
{ {
WCHAR PathBuffer[MAX_PATH]; NTSTATUS Status;
INFCONTEXT DirContext; INFCONTEXT DirContext;
PWCHAR AdditionalSectionName = NULL; PWCHAR AdditionalSectionName = NULL;
PWCHAR DirKeyValue; PWCHAR DirKeyValue;
ULONG Length; WCHAR PathBuffer[MAX_PATH];
NTSTATUS Status;
/* Add common files */ /* Add common files */
if (!AddSectionToCopyQueue(InfFile, L"SourceDisksFiles", SourceCabinet, &DestinationPath, Ir)) if (!AddSectionToCopyQueue(InfFile, L"SourceDisksFiles", SourceCabinet, &DestinationPath, Ir))
@ -3621,22 +3638,16 @@ PrepareCopyPageInfFile(HINF InfFile,
/* /*
* FIXME: * FIXME:
* - Install directories like '\reactos\test' are not handled yet. * Copying files to DestinationRootPath should be done from within
* - Copying files to DestinationRootPath should be done from within * the SystemPartitionFiles section.
* the SystemPartitionFiles section. * At the moment we check whether we specify paths like '\foo' or '\\' for that.
* At the moment we check whether we specify paths like '\foo' or '\\' for that. * For installing to DestinationPath specify just '\' .
* For installing to DestinationPath specify just '\' .
*/ */
/* Get destination path */ /* Get destination path */
wcscpy(PathBuffer, DestinationPath.Buffer); StringCchCopyW(PathBuffer, ARRAYSIZE(PathBuffer), DestinationPath.Buffer);
/* Remove trailing backslash */ DPRINT("FullPath(1): '%S'\n", PathBuffer);
Length = wcslen(PathBuffer);
if ((Length > 0) && (PathBuffer[Length - 1] == L'\\'))
{
PathBuffer[Length - 1] = UNICODE_NULL;
}
/* Create the install directory */ /* Create the install directory */
Status = SetupCreateDirectory(PathBuffer); Status = SetupCreateDirectory(PathBuffer);
@ -3676,26 +3687,20 @@ PrepareCopyPageInfFile(HINF InfFile,
/* Installation path */ /* Installation path */
DPRINT("InstallationPath: '%S'\n", DirKeyValue); DPRINT("InstallationPath: '%S'\n", DirKeyValue);
wcscpy(PathBuffer, DestinationPath.Buffer); StringCchCopyW(PathBuffer, ARRAYSIZE(PathBuffer),
DestinationPath.Buffer);
DPRINT("FullPath: '%S'\n", PathBuffer); DPRINT("InstallationPath(2): '%S'\n", PathBuffer);
} }
else if (DirKeyValue[0] == L'\\') else if (DirKeyValue[0] == L'\\')
{ {
/* Absolute path */ /* Absolute path */
DPRINT("Absolute Path: '%S'\n", DirKeyValue); DPRINT("AbsolutePath: '%S'\n", DirKeyValue);
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2, CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
DestinationRootPath.Buffer, DirKeyValue); DestinationRootPath.Buffer, DirKeyValue);
/* Remove trailing backslash */ DPRINT("AbsolutePath(2): '%S'\n", PathBuffer);
Length = wcslen(PathBuffer);
if ((Length > 0) && (PathBuffer[Length - 1] == L'\\'))
{
PathBuffer[Length - 1] = UNICODE_NULL;
}
DPRINT("FullPath: '%S'\n", PathBuffer);
Status = SetupCreateDirectory(PathBuffer); Status = SetupCreateDirectory(PathBuffer);
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
@ -3713,14 +3718,7 @@ PrepareCopyPageInfFile(HINF InfFile,
CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2, CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
DestinationPath.Buffer, DirKeyValue); DestinationPath.Buffer, DirKeyValue);
/* Remove trailing backslash */ DPRINT("RelativePath(2): '%S'\n", PathBuffer);
Length = wcslen(PathBuffer);
if ((Length > 0) && (PathBuffer[Length - 1] == L'\\'))
{
PathBuffer[Length - 1] = UNICODE_NULL;
}
DPRINT("FullPath: '%S'\n", PathBuffer);
Status = SetupCreateDirectory(PathBuffer); Status = SetupCreateDirectory(PathBuffer);
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
@ -4038,12 +4036,14 @@ RegistryPage(PINPUT_RECORD Ir)
return SUCCESS_PAGE; return SUCCESS_PAGE;
} }
/************************ HACK!!!!!!!!!!! *********************************/
if (!SetInstallPathValue(&DestinationPath)) if (!SetInstallPathValue(&DestinationPath))
{ {
DPRINT1("SetInstallPathValue() failed\n"); DPRINT1("SetInstallPathValue() failed\n");
MUIDisplayError(ERROR_INITIALIZE_REGISTRY, Ir, POPUP_WAIT_ENTER); MUIDisplayError(ERROR_INITIALIZE_REGISTRY, Ir, POPUP_WAIT_ENTER);
return QUIT_PAGE; return QUIT_PAGE;
} }
/************************ HACK!!!!!!!!!!! *********************************/
/* Create the default hives */ /* Create the default hives */
Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP); Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP);
@ -4193,10 +4193,10 @@ BootLoaderPage(PINPUT_RECORD Ir)
CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT)); CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
RtlFreeUnicodeString(&SystemRootPath); RtlFreeUnicodeString(&SystemRootPath);
swprintf(PathBuffer, StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
L"\\Device\\Harddisk%lu\\Partition%lu", L"\\Device\\Harddisk%lu\\Partition%lu\\",
PartitionList->SystemPartition->DiskEntry->DiskNumber, PartitionList->SystemPartition->DiskEntry->DiskNumber,
PartitionList->SystemPartition->PartitionNumber); PartitionList->SystemPartition->PartitionNumber);
RtlCreateUnicodeString(&SystemRootPath, PathBuffer); RtlCreateUnicodeString(&SystemRootPath, PathBuffer);
DPRINT1("SystemRootPath: %wZ\n", &SystemRootPath); DPRINT1("SystemRootPath: %wZ\n", &SystemRootPath);
@ -4438,7 +4438,7 @@ BootLoaderHarddiskVbrPage(PINPUT_RECORD Ir)
* *
* SIDEEFFECTS * SIDEEFFECTS
* Calls InstallVBRToPartition() * Calls InstallVBRToPartition()
* CallsInstallMbrBootCodeToDisk() * Calls InstallMbrBootCodeToDisk()
* *
* RETURNS * RETURNS
* Number of the next page. * Number of the next page.
@ -4466,16 +4466,16 @@ BootLoaderHarddiskMbrPage(PINPUT_RECORD Ir)
} }
/* Step 2: Write the MBR */ /* Step 2: Write the MBR */
swprintf(DestinationDevicePathBuffer, StringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
L"\\Device\\Harddisk%d\\Partition0", L"\\Device\\Harddisk%d\\Partition0",
PartitionList->SystemPartition->DiskEntry->DiskNumber); PartitionList->SystemPartition->DiskEntry->DiskNumber);
CombinePaths(SourceMbrPathBuffer, ARRAYSIZE(SourceMbrPathBuffer), 2, SourceRootPath.Buffer, L"\\loader\\dosmbr.bin"); CombinePaths(SourceMbrPathBuffer, ARRAYSIZE(SourceMbrPathBuffer), 2, SourceRootPath.Buffer, L"\\loader\\dosmbr.bin");
if (IsThereAValidBootSector(DestinationDevicePathBuffer)) if (IsThereAValidBootSector(DestinationDevicePathBuffer))
{ {
/* Save current MBR */ /* Save current MBR */
CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath.Buffer, L"\\mbr.old"); CombinePaths(DstPath, ARRAYSIZE(DstPath), 2, SystemRootPath.Buffer, L"mbr.old");
DPRINT1("Save MBR: %S ==> %S\n", DestinationDevicePathBuffer, DstPath); DPRINT1("Save MBR: %S ==> %S\n", DestinationDevicePathBuffer, DstPath);
Status = SaveBootSector(DestinationDevicePathBuffer, DstPath, sizeof(PARTITION_SECTOR)); Status = SaveBootSector(DestinationDevicePathBuffer, DstPath, sizeof(PARTITION_SECTOR));

View file

@ -72,8 +72,6 @@
#include "mui.h" #include "mui.h"
extern HANDLE ProcessHeap; extern HANDLE ProcessHeap;
extern UNICODE_STRING SourceRootPath;
extern UNICODE_STRING SourceRootDir;
extern UNICODE_STRING SourcePath; extern UNICODE_STRING SourcePath;
extern BOOLEAN IsUnattendedSetup; extern BOOLEAN IsUnattendedSetup;
extern PWCHAR SelectedLanguageId; extern PWCHAR SelectedLanguageId;