windows-nt/Source/XPSP1/NT/base/ntsetup/syssetup/preinst.c

794 lines
19 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "setupp.h"
#pragma hdrstop
//
// List of oem preinstall/unattend values we can fetch from profile files.
// Do NOT change the order of this without changing the order of the
// PreinstUnattendData array.
//
typedef enum {
OemDatumBackgroundBitmap,
OemDatumBannerText,
OemDatumLogoBitmap,
OemDatumMax
} OemPreinstallDatum;
//
// Define structure that represents a single bit of data read
// from a profile file relating to preinstallation.
//
typedef struct _PREINSTALL_UNATTEND_DATUM {
//
// Filename. If NULL, use the system answer file $winnt$.inf.
// Otherwise this is relative to the root of the source drive.
//
PCWSTR Filename;
//
// Section name.
//
PCWSTR Section;
//
// Key name.
//
PCWSTR Key;
//
// Default value. Can be NULL but that will be translated to
// "" when the profile API is called to retrieve the data.
//
PCWSTR Default;
//
// Where to put the actual value. The actual value may be
// a string, or NULL.
//
PWSTR *Value;
//
// Value for sanity checking.
//
OemPreinstallDatum WhichValue;
} PREINSTALL_UNATTEND_DATUM, *PPREINSTALL_UNATTEND_DATUM;
//
// Name of oem background bitmap file and logo bitmap file.
// Replacement banner text.
// Read from unattend.txt
//
PWSTR OemBackgroundBitmapFile;
PWSTR OemLogoBitmapFile;
PWSTR OemBannerText;
//
// If this is non-NULL, it is a replacement bitmap
// to use for the background.
//
HBITMAP OemBackgroundBitmap;
PREINSTALL_UNATTEND_DATUM PreinstUnattendData[OemDatumMax] = {
//
// Background bitmap
//
{ NULL,
WINNT_OEM_ADS,
WINNT_OEM_ADS_BACKGROUND,
NULL,
&OemBackgroundBitmapFile,
OemDatumBackgroundBitmap
},
//
// Banner text
//
{
NULL,
WINNT_OEM_ADS,
WINNT_OEM_ADS_BANNER,
NULL,
&OemBannerText,
OemDatumBannerText
},
//
// Logo bitmap
//
{
NULL,
WINNT_OEM_ADS,
WINNT_OEM_ADS_LOGO,
NULL,
&OemLogoBitmapFile,
OemDatumLogoBitmap
}
};
//
// Path to the registry key that contains the list of preinstalled
// drivers (SCSI, keyboard and mouse)
//
PCWSTR szPreinstallKeyName = L"SYSTEM\\Setup\\Preinstall";
//
// Forward references
//
VOID
ProcessOemBitmap(
IN PWSTR FilenameAndResId,
IN SetupBm WhichOne
);
BOOL
CleanupPreinstalledComponents(
);
BOOL
InitializePreinstall(
VOID
)
{
WCHAR Buffer[2*MAX_PATH];
DWORD d;
int i;
//
// Must be run after main initialization. We rely on stuff that is
// set up there.
//
MYASSERT(AnswerFile[0]);
//
// Special skip-eula value. Note that we want this value even if we're not
// doing a Preinstall.
//
GetPrivateProfileString(
WINNT_UNATTENDED,
L"OemSkipEula",
pwNo,
Buffer,
sizeof(Buffer)/sizeof(Buffer[0]),
AnswerFile
);
OemSkipEula = (lstrcmpi(Buffer,pwYes) == 0);
//
// For the mini-setup case, always be preinstall.
//
if( MiniSetup ) {
Preinstall = TRUE;
} else {
MYASSERT(SourcePath[0]);
//
// First, figure out whether this is an OEM preinstallation.
//
GetPrivateProfileString(
WINNT_UNATTENDED,
WINNT_OEMPREINSTALL,
pwNo,
Buffer,
sizeof(Buffer)/sizeof(Buffer[0]),
AnswerFile
);
Preinstall = (lstrcmpi(Buffer,pwYes) == 0);
//
// If not preinstallation, nothing more to do.
//
if(!Preinstall) {
return(TRUE);
}
}
//
// OK, it's preinstsall. Fill in our data table.
//
for(i=0; i<OemDatumMax; i++) {
//
// Sanity check
//
MYASSERT(PreinstUnattendData[i].WhichValue == i);
//
// Retrieve data and duplicate. If the value comes back as ""
// assume it means there is no value in there.
//
GetPrivateProfileString(
PreinstUnattendData[i].Section,
PreinstUnattendData[i].Key,
PreinstUnattendData[i].Default ? PreinstUnattendData[i].Default : NULL,
Buffer,
sizeof(Buffer)/sizeof(Buffer[0]),
PreinstUnattendData[i].Filename ? PreinstUnattendData[i].Filename : AnswerFile
);
*PreinstUnattendData[i].Value = Buffer[0] ? pSetupDuplicateString(Buffer) : NULL;
if(Buffer[0] && (*PreinstUnattendData[i].Value == NULL)) {
//
// Out of memory.
//
pSetupOutOfMemory(MainWindowHandle);
return(FALSE);
}
}
//
// Change the banner text, if the OEM supplied new text.
// Make sure our product name is in there.
//
if(OemBannerText) {
if(wcsstr(OemBannerText,L"Windows NT") ||
wcsstr(OemBannerText,L"BackOffice")) {
//
// Substitute * with \n
//
for(i=0; OemBannerText[i]; i++) {
if(OemBannerText[i] == L'*') {
OemBannerText[i] = L'\n';
}
}
#if 0
//
// Disable the banner for now.
//
SendMessage(MainWindowHandle,WM_NEWBITMAP,SetupBmBanner,(LPARAM)OemBannerText);
#endif
} else {
MyFree(OemBannerText);
OemBannerText = NULL;
}
}
//
// Load the OEM background bitmap, if any.
// Load the OEM logo bitmap, if any.
//
ProcessOemBitmap(OemBackgroundBitmapFile,SetupBmBackground);
ProcessOemBitmap(OemLogoBitmapFile,SetupBmLogo);
//
// Repaint the main window. Specify that the background should be erased
// because the main window relies on this behavior.
//
InvalidateRect(MainWindowHandle,NULL,TRUE);
UpdateWindow(MainWindowHandle);
CleanupPreinstalledComponents();
return(TRUE);
}
VOID
ProcessOemBitmap(
IN PWSTR FilenameAndResId,
IN SetupBm WhichOne
)
/*++
Routine Description:
This routine processes a single oem bitmap specification.
The OEM bitmap may either be in a resource file or in a standalone
bitmap file. Once the bitmap has been loaded the main window
is told about it.
Arguments:
FileNameAndResId - specifies a profile string with either one
or 2 fields. If the string contains a comma, it is assumed to be
the name of a DLL followed by a resource ID. The dll is loaded
from the $OEM$\OEMFILES directory and then we call LoadBitmap
with the given resource id, which is a base-10 string of ascii digits.
The string is split at the comma during this routine.
If this parameter does not contain a comma then it is assumed to be
the name of a .bmp in $OEM$\OEMFILES and we load it via LoadImage().
WhichOne - supplies a value indicating which bitmap this is.
Return Value:
None.
--*/
{
HINSTANCE ModuleHandle;
PWCHAR p,q;
HBITMAP Bitmap;
WCHAR Buffer[MAX_PATH];
DWORD Result;
if(FilenameAndResId) {
Bitmap = NULL;
if( !MiniSetup ) {
lstrcpy(Buffer,SourcePath);
pSetupConcatenatePaths(Buffer,WINNT_OEM_DIR,MAX_PATH,NULL);
} else {
//
// If we're doing a mini-install, look for the bmp in
// the \sysprep directory on the drive where NT is
// installed, not $OEM$
//
Result = GetWindowsDirectory( Buffer, MAX_PATH );
if( Result == 0) {
MYASSERT(FALSE);
return;
}
Buffer[3] = 0;
pSetupConcatenatePaths( Buffer, TEXT("sysprep"), MAX_PATH, NULL );
}
if(p = wcschr(FilenameAndResId,L',')) {
q = p;
//
// Skip backwards over spaces and quotes. The text setup ini file writer
// will create a line like
//
// a = "b","c"
//
// whose RHS comes back as
//
// b","c
//
// since the profile APIs strip off the outermost quotes.
//
//
while((q > FilenameAndResId) && ((*(q-1) == L'\"') || iswspace(*(q-1)))) {
q--;
}
*q = 0;
q = p+1;
while(*q && ((*q == L'\"') || iswspace(*q))) {
q++;
}
pSetupConcatenatePaths(Buffer,FilenameAndResId,MAX_PATH,NULL);
if(ModuleHandle = LoadLibraryEx(Buffer,NULL,LOAD_LIBRARY_AS_DATAFILE)) {
Bitmap = LoadBitmap(ModuleHandle,MAKEINTRESOURCE(wcstoul(q,NULL,10)));
FreeLibrary(ModuleHandle);
}
} else {
pSetupConcatenatePaths(Buffer,FilenameAndResId,MAX_PATH,NULL);
Bitmap = (HBITMAP)LoadImage(NULL,Buffer,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
}
if(Bitmap) {
//
// Got a valid bitmap. Tell main window about it.
//
SendMessage(MainWindowHandle,WM_NEWBITMAP,WhichOne,(LPARAM)Bitmap);
}
}
}
LONG
ExaminePreinstalledComponent(
IN HKEY hPreinstall,
IN SC_HANDLE hSC,
IN PCWSTR ServiceName
)
/*++
Routine Description:
Query a preinstalled component, and disable it if necessary.
If the component is an OEM component, and is running, then disable
any associated service, if necessary.
Arguments:
hPreinstall - Handle to the key SYSTEM\Setup\Preinstall.
hSC - Handle to the Service Control Manager.
ServiceName - Name of the service to be examined.
Return Value:
Returns a Win32 error code indicating the outcome of the operation.
--*/
{
BOOL OemComponent;
HKEY Key;
LONG Error;
DWORD cbData;
WCHAR Data[ MAX_PATH + 1];
DWORD Type;
SC_HANDLE hSCService;
SERVICE_STATUS ServiceStatus;
//
// Open the key that contains the info about the preinstalled component.
//
Error = RegOpenKeyEx( hPreinstall,
ServiceName,
0,
KEY_READ,
&Key );
if( Error != ERROR_SUCCESS ) {
return( Error );
}
//
// Find out if the component is an OEM or RETAIL
//
cbData = sizeof(Data);
Error = RegQueryValueEx( Key,
L"OemComponent",
0,
&Type,
( LPBYTE )Data,
&cbData );
if( Error != ERROR_SUCCESS ) {
RegCloseKey( Key );
return( Error );
}
OemComponent = (*((PULONG)Data) == 1);
if( OemComponent ) {
//
// Get the name of the retail service to disable
//
cbData = sizeof(Data);
Error = RegQueryValueEx( Key,
L"RetailClassToDisable",
0,
&Type,
( LPBYTE )Data,
&cbData );
if( Error != ERROR_SUCCESS ) {
*(( PWCHAR )Data) = '\0';
}
}
RegCloseKey( Key );
//
// Query the service
//
hSCService = OpenService( hSC,
ServiceName,
SERVICE_QUERY_STATUS );
if( hSCService == NULL ) {
Error = GetLastError();
return( Error );
}
if( !QueryServiceStatus( hSCService,
&ServiceStatus ) ) {
Error = GetLastError();
return( Error );
}
CloseServiceHandle( hSCService );
if( ServiceStatus.dwCurrentState == SERVICE_STOPPED ) {
//
// Due to the nature of the services that were pre-installed,
// we can assume that the service failed to initialize, and that
// it can be disabled.
//
MyChangeServiceStart( ServiceName,
SERVICE_DISABLED );
} else {
if( OemComponent &&
( lstrlen( (PCWSTR)Data ) != 0 ) ) {
MyChangeServiceStart( (PCWSTR)Data,
SERVICE_DISABLED );
}
}
return( ERROR_SUCCESS );
}
BOOL
CleanupPreinstalledComponents(
)
/*++
Routine Description:
Query the preinstalled components, and disable the ones that
failed to start.
This is done by enumerating the subkeys of SYSTEM\Setup\Preinstall.
Each subkey represents a SCSI, Keyboard or Mouse installed.
The video drivers are not listed here. The "Display" applet will
determine and disable the video drivers that were preinstalled, but
failed to start.
Arguments:
None.
Return Value:
Returns TRUE if the operation succeeded, or FALSE otherwise.
--*/
{
LONG Error;
LONG SavedError;
HKEY Key;
HKEY SubKeyHandle;
ULONG i;
ULONG SubKeys;
WCHAR SubKeyName[ MAX_PATH + 1 ];
ULONG NameSize;
FILETIME LastWriteTime;
SC_HANDLE hSC;
EnableEventlogPopup();
//
// Find out the number of subkeys of SYSTEM\Setup\Preinstall
//
Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
szPreinstallKeyName,
0,
KEY_READ,
&Key );
if( Error != ERROR_SUCCESS ) {
//
// If the key doesn't exist, then assume no driver to preinstall,
// and return TRUE.
//
return( Error == ERROR_FILE_NOT_FOUND );
}
Error = RegQueryInfoKey( Key,
NULL,
NULL,
NULL,
&SubKeys,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL );
if( Error != ERROR_SUCCESS ) {
return( FALSE );
}
//
// If there are no subkeys, then assume no driver to preinstall
//
if( SubKeys == 0 ) {
return( TRUE );
}
//
// Get a handle to the service control manager
//
hSC = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(hSC == NULL) {
Error = GetLastError();
return( FALSE );
}
//
// Query each SCSI, keyboard and mouse driver that was preinstalled
// and disable it if necessary.
//
SavedError = ERROR_SUCCESS;
for( i = 0; i < SubKeys; i++ ) {
NameSize = sizeof( SubKeyName ) / sizeof( WCHAR );
Error = RegEnumKeyEx( Key,
i,
SubKeyName,
&NameSize,
NULL,
NULL,
NULL,
&LastWriteTime );
if( Error != ERROR_SUCCESS ) {
if( SavedError == ERROR_SUCCESS ) {
SavedError = Error;
}
continue;
}
Error = ExaminePreinstalledComponent( Key,
hSC,
SubKeyName );
if( Error != ERROR_SUCCESS ) {
if( SavedError == ERROR_SUCCESS ) {
SavedError = Error;
}
// continue;
}
}
RegCloseKey( Key );
//
// At this point we can remove the Setup\Preinstall key
//
// DeletePreinstallKey();
Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"SYSTEM\\Setup",
0,
MAXIMUM_ALLOWED,
&Key );
if( Error == ERROR_SUCCESS ) {
pSetupRegistryDelnode( Key, L"Preinstall" );
RegCloseKey( Key );
}
return( TRUE );
}
BOOL
EnableEventlogPopup(
VOID
)
/*++
Routine Description:
Delete from the registry the value entry that disables the error
popups displayed by the eventlog, if one or more pre-installed
driver failed to load.
This value entry is created in the registry during textmode setup.
Arguments:
None.
Return Value:
Returns TRUE if the operation succeeded, or FALSE otherwise.
--*/
{
HKEY hKey = 0;
ULONG Error;
//
// Delete the 'NoPopupsOnBoot' value
//
Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\Windows",
0,
KEY_SET_VALUE,
&hKey );
if(Error == NO_ERROR) {
Error = RegDeleteValue(hKey,
L"NoPopupsOnBoot");
}
if (hKey) {
RegCloseKey(hKey);
}
return( Error == ERROR_SUCCESS );
}
BOOL
ExecutePreinstallCommands(
VOID
)
/*++
Routine Description:
Executes all commands specified in the file $OEM$\OEMFILES\cmdlines.txt.
Arguments:
None.
Return Value:
Returns TRUE if the operation succeeded, or FALSE otherwise.
--*/
{
WCHAR OldCurrentDir[MAX_PATH];
WCHAR FileName[MAX_PATH];
HINF CmdlinesTxtInf;
LONG LineCount,LineNo;
INFCONTEXT InfContext;
PCWSTR CommandLine;
DWORD DontCare;
BOOL AnyError;
PCWSTR SectionName;
//
// Set current directory to $OEM$.
// Preserve current directory to minimize side-effects.
//
if(!GetCurrentDirectory(MAX_PATH,OldCurrentDir)) {
OldCurrentDir[0] = 0;
}
lstrcpy(FileName,SourcePath);
pSetupConcatenatePaths(FileName,WINNT_OEM_DIR,MAX_PATH,NULL);
SetCurrentDirectory(FileName);
//
// Form name of cmdlines.txt and see if it exists.
//
pSetupConcatenatePaths(FileName,WINNT_OEM_CMDLINE_LIST,MAX_PATH,NULL);
AnyError = FALSE;
if(FileExists(FileName,NULL)) {
CmdlinesTxtInf = SetupOpenInfFile(FileName,NULL,INF_STYLE_OLDNT,NULL);
if(CmdlinesTxtInf == INVALID_HANDLE_VALUE) {
//
// The file exists but is invalid.
//
AnyError = TRUE;
} else {
//
// Get the number of lines in the section that contains the commands to
// be executed. The section may be empty or non-existant; this is not
// an error condition. In that case LineCount may be -1 or 0.
//
SectionName = L"Commands";
LineCount = SetupGetLineCount(CmdlinesTxtInf,SectionName);
for(LineNo=0; LineNo<LineCount; LineNo++) {
if(SetupGetLineByIndex(CmdlinesTxtInf,SectionName,(DWORD)LineNo,&InfContext)
&& (CommandLine = pSetupGetField(&InfContext,1))) {
if(!InvokeExternalApplication(NULL,CommandLine,&DontCare)) {
AnyError = TRUE;
}
} else {
//
// Strange case, inf is messed up
//
AnyError = TRUE;
}
}
}
}
//
// Reset current directory and return.
//
if(OldCurrentDir[0]) {
SetCurrentDirectory(OldCurrentDir);
}
if(AnyError) {
SetuplogError(
LogSevError,
SETUPLOG_USE_MESSAGEID,
MSG_LOG_OEMPRE_FAIL,
NULL,NULL);
}
return(!AnyError);
}