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

1374 lines
34 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
Unattend.c
Description:
This performs all of the automated installation GUI mode setup.
See below for usage and modification information
Author:
Stephane Plante (t-stepl) 4-Sep-1995
Revision History:
15-Sep-1995 (t-stepl) rewritten in table format
26-Feb-1996 (tedm) massive cleanup
--*/
#include "setupp.h"
#include <pencrypt.h>
#pragma hdrstop
/*
Table-driven unattended engine
------------------------------
There are two interrelated tables.
The first table is concerned with fetching data from the parameters file
($winnt$.inf) and processing it into a format that can be accessed by the
second table. The second table is associated with the pages in the setup wizard,
and provides the unattend engine with the rules for filling in the contents
of the associated pages from the data contained in the first table.
Adding a new piece of data to the parameters file
-------------------------------------------------
In the header file there is an enumerated type called UNATTENDENTRIES.
Add an entry for your data to the end of this enum. Now add an entry to the
UNATTENDANSWER table.
Here's an explanation of an entry in the UNATTENDEDANSWER table:
{ UAE_PROGRAM, <-This is the identifier for the data item that I want
to fetch. It is used to index into the table array
FALSE, <-This is a runtime variable. Just keep it as false
FALSE, <-If this is true, then it is considered an error in the
unattend script if this value is unspecified. If it is
false, then it does not matter if the value is not
present.
FALSE, <-Another runtime flag. Just keep it as false
0, <-This is the answer we have initially. Since it gets overwritten
quickly, there is no reason why not to set it to 0
pwGuiUnattended <- This is the string which identifies the section we want
pwProgram <- This is the string which identifies the key we want
pwNull <- This identifies the default. Note: NULL means that there is
no default and so it is a serious error if the key does not
exist in the file. pwNull, on the other hand, means the
empty string.
UAT_STRING <- What format we want the answer in. Can be as a string, boolean
or ULONG
NULL <- No callback function exists, however if one did, then must
in the form of: BOOL fnc( struct _UNATTENDANSWER *rec)
Where the fnc returns TRUE if the answer contained in the
record is correct, or FALSE if the answer contained in the
record is incorrect. This callback is meant to allow the
programmer the ability to check to see if his answer is correct.
Note: there is no bound as to when this callback can be issued.
As such, no code which depends on a certain state of the
installation should be used. For the record, the first time
that an answer is required is the time when all records are
filled in in the theory that it is cheaper to do all of the
disk access at once rather then doing it on a as required basis.
Adding/changing wizard pages
----------------------------
Each page contains a series of items which must be filled in by the user.
Since the user wants hands off operation, he is counting on us
to do that filling in. As such, we require information about what elements are
contained on each page. To do this, we define an array whose elements each
describe a single element on the page. Here is the example from the NameOrg
page:
UNATTENDITEM ItemNameOrg[] = {
{ IDT_NAME, <-This is the label that identifies the item to which we
will try to send messages to, using SetDlgItemText().
0, <-One of the reserved words which can be used for
information passing during a callback
0, <-The second such word
NULL, <-Callback function. When we are trying to do something
complicated for the item (like comparing two strings)
it is easier to hardcode it in C. The format for it is:
BOOL fnc(HWND hwnd,DWORD contextinfo,
struct _UNATTENDITEM *item), where contextinfo is
a pointer to the page that the item resides on. The
function returns TRUE if is succeeded and doesn't think
that the user should see the page. FALSE otherwise.
&UnattendAnswerTable[UAE_FULLNAME]
^- This is a pointer to the data table so that we know
how to fill the item. If a callback is specified, this
could be set to null. Note that reference is made using
the enum that had defined previously. This is why
keeping the answer data table in order is so critical.
},
{ IDT_ORGANIZATION, 0, 0, FALSE, NULL, &UnattendAnswerTable[UAE_ORGNAME] }
};
After this table has been created (if required), then you are ready to add
an entry to the UnattendPageTable[]. In this case, order doesn't matter,
but it is general practice to keep the entries in the same order
as the pages. Here is the entry in the table for the NAMEORG page:
{
IDD_NAMEORG, <- This is the page id. We search based on this key.
Simply use whatever resourcename you used for the
dialogs.dlg file
FALSE, <- Runtime flag. Set it as false
FALSE, <- Runtime flag. Set it as false
FALSE, <- If this flag is true, then if there is an error
that occured in the unattended process, then this
page will always be displayed for the user. Good
for the start and finish pages
2, <- The number of items in the array
ItemNameOrg <- The array of items
},
Once this is done, then you can add:
if (Unattended) {
UnattendSetActiveDlg( hwnd, <pageid> );
}
break;
As the last thing in the code for the page's setactive.
This function does is that it sets the DWL_MSGRESULT based on wether or
not the user should see the page and returns that value also (TRUE -- user
should see the page, FALSE, he should not). Then you should add:
case WM_SIMULATENEXT:
PropSheet_PressButton(GetParent(hwnd),PSBTN_NEXT);
to the DlgProc for the page. This means that the code in PSN_WIZNEXT
case will be executed.
You can also use UnattendErrorDlg( hwnd, <pageid> ); in the PSN_WIZNEXT
case if you detect any errors. That will allow unattended operation to try
to clean itself up a bit before control returns to the user for the page.
Note however that as soon as the user hits the next or back button that
control returns to the unattended engine.
*/
//
// Initialization Callbacks
//
// These are used to verify that the entries in the answer file are valid.
//
BOOL
CheckServer(
struct _UNATTENDANSWER *rec
);
BOOL
CheckComputerName(
struct _UNATTENDANSWER *rec
);
BOOL
CheckAdminPassword(
struct _UNATTENDANSWER *rec
);
BOOL
CheckMode(
struct _UNATTENDANSWER *rec
);
//
// SetActive Callbacks
//
// When a wizard page receives a PSN_SETACTIVE notification, a callback is used
// to set the controls on that wizard page, based on the values in the answer
// file.
//
BOOL
SetPid(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetSetupMode(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetPentium(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetLastPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetStepsPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
//
// Do not change the order of these unless you know what you are doing.
// These entries must me in the same order as the UNATTENDENTRIES enum.
//
UNATTENDANSWER UnattendAnswerTable[] = {
{ UAE_PROGRAM, FALSE, FALSE, FALSE, 0,
pwGuiUnattended, pwProgram, pwNull,
UAT_STRING, NULL },
{ UAE_ARGUMENT, FALSE, FALSE, FALSE, 0,
pwGuiUnattended, pwArgument, pwNull,
UAT_STRING, NULL },
{ UAE_TIMEZONE, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwTimeZone, pwTime,
UAT_STRING, NULL },
{ UAE_FULLNAME, FALSE, TRUE, FALSE, 0,
pwUserData, pwFullName, NULL,
UAT_STRING, NULL },
{ UAE_ORGNAME, FALSE, FALSE, FALSE, 0,
pwUserData, pwOrgName, pwNull,
UAT_STRING, NULL },
{ UAE_COMPNAME, FALSE, TRUE, FALSE, 0,
pwUserData, pwCompName, NULL,
UAT_STRING, CheckComputerName },
{ UAE_ADMINPASS, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwAdminPassword, NULL,
UAT_STRING, CheckAdminPassword },
{ UAE_PRODID, FALSE, TRUE, FALSE, 0,
pwUserData, pwProductKey, NULL,
UAT_STRING, NULL },
{ UAE_MODE, FALSE, TRUE, FALSE, 0,
pwUnattended, pwMode, pwExpress,
UAT_STRING, CheckMode },
{ UAE_AUTOLOGON, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwAutoLogon, pwNull,
UAT_STRING, NULL },
{ UAE_PROFILESDIR, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwProfilesDir, pwNull,
UAT_STRING, NULL },
{ UAE_PROGRAMFILES, FALSE, FALSE, FALSE, 0,
pwUnattended, pwProgramFilesDir, pwNull,
UAT_STRING, NULL },
{ UAE_COMMONPROGRAMFILES, FALSE, FALSE, FALSE, 0,
pwUnattended, pwCommonProgramFilesDir, pwNull,
UAT_STRING, NULL },
{ UAE_PROGRAMFILES_X86, FALSE, FALSE, FALSE, 0,
pwUnattended, pwProgramFilesX86Dir, pwNull,
UAT_STRING, NULL },
{ UAE_COMMONPROGRAMFILES_X86, FALSE, FALSE, FALSE, 0,
pwUnattended, pwCommonProgramFilesX86Dir, pwNull,
UAT_STRING, NULL },
};
UNATTENDITEM ItemSetup[] = {
{ 0, IDC_TYPICAL, IDC_CUSTOM, SetSetupMode, &UnattendAnswerTable[UAE_MODE] }
};
UNATTENDITEM ItemNameOrg[] = {
{ IDT_NAME, 0, 0, NULL, &UnattendAnswerTable[UAE_FULLNAME] },
{ IDT_ORGANIZATION, 0, 0, NULL, &UnattendAnswerTable[UAE_ORGNAME] }
};
UNATTENDITEM ItemPidCd[] = {
{ IDT_EDIT_PID1, 0, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID2, 1, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID3, 2, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID4, 3, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID5, 4, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] }
};
UNATTENDITEM ItemPidOem[] = {
{ IDT_EDIT_PID1, 0, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID2, 1, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID3, 2, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID4, 3, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID5, 4, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] }
};
UNATTENDITEM ItemCompName[] = {
{ IDT_EDIT1, 0, 0, NULL, &UnattendAnswerTable[UAE_COMPNAME] },
{ IDT_EDIT2, 0, 0, NULL, &UnattendAnswerTable[UAE_ADMINPASS] },
{ IDT_EDIT3, 0, 0, NULL, &UnattendAnswerTable[UAE_ADMINPASS] }
};
#ifdef _X86_
UNATTENDITEM ItemPentium[] = {
{ 0, IDC_RADIO_1, IDC_RADIO_2, SetPentium, NULL }
};
#endif
UNATTENDITEM ItemStepsPage[] = {
{ 0, 0, 0, SetStepsPage, NULL }
};
UNATTENDITEM ItemLastPage[] = {
{ 0, 0, 0, SetLastPage, NULL }
};
UNATTENDPAGE UnattendPageTable[] = {
{ IDD_WELCOME, FALSE, FALSE, TRUE, 0, NULL },
{ IDD_PREPARING, FALSE, FALSE, FALSE, 0, NULL },
#ifdef PNP_DEBUG_UI
{ IDD_HARDWARE, FALSE, FALSE, TRUE, 0, NULL },
#endif // PNP_DEBUG_UI
{ IDD_WELCOMEBUTTONS, FALSE, FALSE, FALSE, 1, ItemSetup },
{ IDD_REGIONAL_SETTINGS, FALSE, FALSE, FALSE, 0, NULL },
{ IDD_NAMEORG, FALSE, FALSE, FALSE, 2, ItemNameOrg },
{ IDD_PID_CD, FALSE, FALSE, FALSE, 5, ItemPidCd },
{ IDD_PID_OEM, FALSE, FALSE, FALSE, 5, ItemPidOem },
{ IDD_COMPUTERNAME, FALSE, FALSE, FALSE, 3, ItemCompName },
#ifdef DOLOCALUSER
{ IDD_USERACCOUNT, FALSE, FALSE, FALSE, 0, NULL },
#endif
#ifdef _X86_
{ IDD_PENTIUM, FALSE, FALSE, FALSE, 1, ItemPentium },
#endif
{ IDD_OPTIONS, FALSE, FALSE, FALSE, 0, NULL },
{ IDD_STEPS1, FALSE, FALSE, TRUE, 1, ItemStepsPage },
{ IDD_LAST_WIZARD_PAGE, FALSE, FALSE, TRUE, 1, ItemLastPage }
};
UNATTENDWIZARD UnattendWizard = {
FALSE, FALSE, TRUE,
sizeof(UnattendPageTable)/sizeof(UnattendPageTable[0]),
UnattendPageTable,
sizeof(UnattendAnswerTable)/sizeof(UnattendAnswerTable[0]),
UnattendAnswerTable
};
//
// Global Pointer to the Answer file
//
WCHAR AnswerFile[MAX_PATH] = TEXT("");
BOOL
GetAnswerFileSetting (
IN PCWSTR Section,
IN PCWSTR Key,
OUT PWSTR Buffer,
IN UINT BufferSize
)
/*++
Routine Description:
GetAnswerFileSetting uses the private profile APIs to obtain an answer file
string from %systemroot%\system32\$winnt$.inf. It also performs %% removal,
since $winnt$.inf is an INF, not an INI.
Arguments:
Section - Specifies the section to retreive the value from (such as
GuiUnattended)
Key - Specifies the key within the section (such as TimeZone)
Buffer - Receives the value
BufferSize - Specifies the size, in WCHARs, of Buffer.
Return Value:
TRUE if the setting was retrived, FALSE otherwise.
--*/
{
PCWSTR src;
PWSTR dest;
WCHAR testBuf[3];
MYASSERT (BufferSize > 2);
if (!AnswerFile[0]) {
GetSystemDirectory (AnswerFile, MAX_PATH);
pSetupConcatenatePaths (AnswerFile, WINNT_GUI_FILE, MAX_PATH, NULL);
SetEnvironmentVariable (L"UnattendFile", AnswerFile);
}
if (!GetPrivateProfileString (
Section,
Key,
L"",
Buffer,
BufferSize,
AnswerFile
)) {
//
// String not present or is empty -- try again with a different
// default. If the string is empty, we'll get back 0. If the key does
// not exist, we'll get back 1.
//
MYASSERT (BufferSize == 0 || *Buffer == 0);
return 0 == GetPrivateProfileString (
Section,
Key,
L"X",
testBuf,
3,
AnswerFile
);
}
//
// We obtained the string. Now remove pairs of %.
//
if (BufferSize) {
src = Buffer;
dest = Buffer;
while (*src) {
if (src[0] == L'%' && src[1] == L'%') {
src++;
}
*dest++ = *src++;
}
*dest = 0;
}
return TRUE;
}
BOOL
UnattendFindAnswer(
IN OUT PUNATTENDANSWER ans
)
/*++
Routine Description:
Fills in the response from the unattend file to the key 'id' into
the structure pointed to by 'ans'. If a non-null 'def' is specified
and no answer exists in the file, 'def' is parsed as the answer.
Arguments:
ans - pointer to the structure information for the answer
Return Value:
TRUE - 'ans' structure has been filled in with an answer
FALSE - otherwise
--*/
{
WCHAR Buf[MAX_BUF];
MYASSERT(AnswerFile[0]);
if (!GetAnswerFileSetting (ans->Section, ans->Key, Buf, MAX_BUF)) {
//
// Setting does not exist. If there is a default, use it.
//
if (ans->DefaultAnswer) {
lstrcpyn (Buf, ans->DefaultAnswer, MAX_BUF);
} else {
ans->Present = FALSE;
return (!ans->Required);
}
}
//
// Assume empty string means the string does not exist. This is how the
// original implementation worked.
//
if (*Buf == 0) {
ans->Present = FALSE;
return !ans->Required;
}
//
// Found a value, or using the default
//
ans->Present = TRUE;
//
// Copy the data into the answer structure. This requires
// switching on the type of data expected and converting it to
// the required format. In the case of strings, it also means
// allocating a pool of memory for the result
//
switch(ans->Type) {
case UAT_STRING:
//
// We allocate some memory, so we must free it later
//
ans->Answer.String = pSetupDuplicateString(Buf);
if(!ans->Answer.String) {
pSetupOutOfMemory(GetActiveWindow());
return(FALSE);
}
break;
case UAT_LONGINT:
//
// Simply convert the number from string to long
//
ans->Answer.Num = _wtol(Buf);
break;
case UAT_BOOLEAN:
//
// check to see if the answer is yes
//
ans->Answer.Bool = ((Buf[0] == L'y') || (Buf[0] == L'Y'));
break;
default:
break;
}
//
// Execute any callbacks if present
//
if(ans->pfnCheckValid) {
if(!ans->pfnCheckValid(ans)) {
ans->Present = FALSE;
ans->ParseErrors = TRUE;
return(!ans->Required);
}
}
//
// Success.
//
return(TRUE);
}
VOID
UnattendInitialize(
VOID
)
/*++
Routine Description:
Initialize unattended mode support by loading all answers
from the unattend file.
Arguments:
None.
Return Value:
None.
--*/
{
WCHAR p[MAX_BUF];
DWORD Result;
BOOL Success = TRUE;
UINT i;
//
// If we haven't calculated the path to $winnt$.sif yet, do so now
//
if(!AnswerFile[0]) {
GetSystemDirectory(AnswerFile,MAX_PATH);
pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
SetEnvironmentVariable( L"UnattendFile", AnswerFile );
}
if( MiniSetup ) {
WCHAR MyAnswerFile[MAX_PATH];
//
// First, see if there's a sysprep.inf on the a:\ drive. If so, use it.
//
lstrcpy( MyAnswerFile, TEXT("a:\\sysprep.inf") );
if( !FileExists( MyAnswerFile, NULL ) ) {
//
// Nope. Check for a \sysprep\sysprep.inf.
//
Result = GetWindowsDirectory( MyAnswerFile, MAX_PATH );
if( Result == 0) {
MYASSERT(FALSE);
return;
}
MyAnswerFile[3] = 0;
pSetupConcatenatePaths( MyAnswerFile, TEXT("sysprep\\sysprep.inf"), MAX_PATH, NULL );
}
//
// We've assumed that we're running unattended, but
// network setup hates it when we pretend to be unattended, but
// don't provide an answer file. So if there is no answer file,
// quit the facade.
//
Unattended = FileExists(MyAnswerFile, NULL);
Preinstall = Unattended;
//
// Now either replace or delete the original unattend file.
// We do this so that we don't erroneously pickup unattend
// entries out of the old answer file. However, if OOBE is
// running, we still need the old answerfile.
//
if( Unattended ) {
CopyFile( MyAnswerFile, AnswerFile, FALSE );
} else if ( !OobeSetup ) {
DeleteFile( AnswerFile );
}
}
//
// We need to make the product id an alias for the product key.
//
if ( GetPrivateProfileString(
pwUserData, pwProdId, pwNull, p, MAX_BUF, AnswerFile)
) {
if ( !WritePrivateProfileString(
pwUserData, pwProductKey, p, AnswerFile ) ) {
SetupDebugPrint( L"SETUP: WritePrivateProfileString failed to write the product key in UnattendInitialize()." );
}
}
//
// Now get all the answers.
//
MYASSERT(!UnattendWizard.Initialized);
UnattendWizard.Initialized = TRUE;
for(i=0; i<UnattendWizard.AnswerCount; i++) {
//
// Check to make sure that the table order hasn't changed
// and load the appropriate answer
//
MYASSERT((UINT)UnattendWizard.Answer[i].AnswerId == i);
Success &= UnattendFindAnswer(&UnattendWizard.Answer[i]);
}
UnattendWizard.ShowWizard = !Success;
}
LRESULT
SendDlgMessage (
HWND hdlg,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT OldResult;
LRESULT Result;
OldResult = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
SendMessage (hdlg, Message, wParam, lParam);
Result = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
SetWindowLongPtr (hdlg, DWLP_MSGRESULT, OldResult);
return Result;
}
BOOL
ReturnDlgResult (
HWND hdlg,
LRESULT Result
)
{
SetWindowLongPtr (hdlg, DWLP_MSGRESULT, Result);
return TRUE;
}
VOID
UnattendAdvanceIfValid (
IN HWND hwnd
)
{
LRESULT ValidationState;
//
// Validate wizard page data with UI
//
ValidationState = SendDlgMessage (hwnd, WMX_VALIDATE, 0, TRUE);
if (ValidationState == VALIDATE_DATA_INVALID) {
SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_DISALLOWED);
} else {
SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_OK);
}
}
BOOL
UnattendSetActiveDlg(
IN HWND hwnd,
IN DWORD controlid
)
/*++
Routine Description:
Initialize unattended mode support by loading all answers
from the unattend file.
Arguments:
None.
Return Value:
TRUE - Page will become active
FALSE - Page will not become active
--*/
{
PUNATTENDPAGE pPage;
PUNATTENDITEM pItem;
BOOL success;
UINT i,j;
MYASSERT(UnattendWizard.Initialized);
for(i=0; i<UnattendWizard.PageCount; i++) {
if(controlid == UnattendWizard.Page[i].PageId) {
//
// Found Matching Page entry
// Check to see if we have already loaded the page
//
pPage = & (UnattendWizard.Page[i]);
if(!pPage->LoadPage) {
//
// Set the flags that load and display the page and
// the flag that controls wether or not to stop on this page
//
pPage->LoadPage = TRUE;
pPage->ShowPage = (UnattendMode == UAM_PROVIDEDEFAULT);
for(j=0;j<pPage->ItemCount;j++) {
pItem = &(pPage->Item[j]);
if(pItem->pfnSetActive) {
//
// If the item has a call back function then
// execute that function, otherwise try to load
// the answer into the appropriate message box
//
success = pItem->pfnSetActive(hwnd,0,pItem);
pPage->ShowPage |= !success;
} else if (!pItem->Item->Present) {
//
// The answer for this item is missing.
//
pPage->ShowPage |= pItem->Item->Required;
} else {
//
// Switch to set the text of the item on the screen
//
switch(pItem->Item->Type) {
case UAT_STRING:
SetDlgItemText(hwnd,pItem->ControlId,pItem->Item->Answer.String);
break;
case UAT_LONGINT:
case UAT_BOOLEAN:
case UAT_NONE:
default:
break;
}
if( UnattendMode == UAM_PROVIDEDEFAULT ||
UnattendMode == UAM_DEFAULTHIDE) {
EnableWindow(GetDlgItem(hwnd,pItem->ControlId), TRUE);
} else {
EnableWindow(GetDlgItem(hwnd,pItem->ControlId),FALSE);
}
} // if (pItem
} // for(j
//
// Allow the page to become activated
//
SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
if(!pPage->ShowPage) {
//
// Perform validation, skip activation if validation succeeds.
//
if (SendDlgMessage (hwnd, WMX_VALIDATE, 0, 0) == 1) {
SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
return FALSE;
}
//
// Simulate the pressing of the next button, which causes the
// wizard page proc to evaluate the data in its controls, and
// throw up popups to the user.
//
PostMessage(hwnd,WM_SIMULATENEXT,0,0);
} else if (!pPage->NeverSkip) {
//
// Pages which are marked as NeverSkip should not
// cause the unattended status to be considered
// unsuccessful.
//
// We can't skip this page so mark the init as
// unsuccessful. If this is the first error in a fully
// unattended setup, notify the user.
//
if(UnattendMode == UAM_FULLUNATTENDED) {
SetuplogError(
LogSevError,
SETUPLOG_USE_MESSAGEID,
MSG_LOG_BAD_UNATTEND_PARAM,
pItem->Item->Key,
pItem->Item->Section,
NULL,NULL);
if(UnattendWizard.Successful) {
MessageBoxFromMessage(
MainWindowHandle,
MSG_FULLUNATTENDED_ERROR,
NULL,
IDS_ERROR,
MB_ICONERROR | MB_OK | MB_SYSTEMMODAL
);
}
}
UnattendWizard.Successful = FALSE;
}
return(TRUE);
} else {
//
// The Page has already been loaded, so we don't do that again
// If we are ShowPage is FALSE, then we don't show the page to
// the user, otherwise we do.
//
if(!pPage->ShowPage && !pPage->NeverSkip) {
SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
} else {
SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
}
return(pPage->ShowPage);
}
}
}
//
// We didn't find a matching id, stop at the page that called us.
//
SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
return(TRUE);
}
BOOL
UnattendErrorDlg(
IN HWND hwnd,
IN DWORD controlid
)
/*++
Routine Description:
Called when an error occurs in a DLG. Enables all windows
in the dialog and turns off the successful flag for the
unattend wizard
Arguments:
Return Value:
Boolean value indicating outcome.
--*/
{
PUNATTENDPAGE pPage;
PUNATTENDITEM pItem;
BOOL success;
BOOL stop;
UINT i,j;
MYASSERT(UnattendWizard.Initialized);
for(i=0; i<UnattendWizard.PageCount; i++) {
if(controlid == UnattendWizard.Page[i].PageId) {
//
// Found Matching Page entry
//
pPage = &UnattendWizard.Page[i];
if(!pPage->LoadPage) {
//
// The Page hasn't been loaded, so it isn't correct
//
continue;
}
//
// Always display the page from now on
//
pPage->ShowPage = TRUE;
//
// Enable all the items
//
for (j=0;j<pPage->ItemCount;j++) {
pItem = &(pPage->Item[j]);
if(pItem->pfnSetActive) {
//
// if this is present then we assume that the
// callback handled itself properly already
//
continue;
}
EnableWindow( GetDlgItem(hwnd,pItem->ControlId), TRUE);
}
}
}
UnattendWizard.Successful = FALSE;
return(TRUE);
}
PWSTR
UnattendFetchString(
IN UNATTENDENTRIES entry
)
/*++
Routine Description:
Finds the string which corresponds to 'entry' in the answer
table and returns a pointer to a copy of that string
Arguments:
entry - which answer do you want?
Return Value:
NULL - if any errors occur
string - if a normal string
Note: if the answer is an int or a bool or some other type,
the behavior of this function is undefined (for now it will
return NULL -- in the future it might make sense to turn these
into strings...)
--*/
{
MYASSERT(UnattendWizard.Initialized);
//
// Sanity check to make sure that the order of the answers is
// what we expect.
//
MYASSERT(UnattendWizard.Answer[entry].AnswerId == entry);
if(!UnattendWizard.Answer[entry].Present
|| (UnattendWizard.Answer[entry].Type != UAT_STRING)) {
//
// There is no string to return
//
return NULL;
}
return(pSetupDuplicateString(UnattendWizard.Answer[entry].Answer.String));
}
BOOL
CheckServer(
struct _UNATTENDANSWER *rec
)
/*++
Routine Description:
Callback to check that the string used for the server type is valid
Arguments:
Return Value:
TRUE - Answer is valid
FALSE - Answer is invalid
--*/
{
MYASSERT(rec);
//
// Check to make sure that we have a string
//
if(rec->Type != UAT_STRING) {
return(FALSE);
}
//
// Check to see if we have one of the valid strings
//
if(lstrcmpi(rec->Answer.String,WINNT_A_LANMANNT)
&& lstrcmpi(rec->Answer.String,WINNT_A_SERVERNT)) {
//
// We don't have a valid string, so we can clean up the answer
//
MyFree(rec->Answer.String);
rec->Present = FALSE;
rec->ParseErrors = TRUE;
return(FALSE);
}
return(TRUE);
}
BOOL
CheckComputerName(
struct _UNATTENDANSWER *rec
)
/*+
Routine Description:
Uppercase the computer name that comes out of the unattended file.
Arguments:
Returns:
Always TRUE.
--*/
{
if((rec->Type == UAT_STRING) && rec->Answer.String) {
CharUpper(rec->Answer.String);
}
return(TRUE);
}
BOOL
CheckAdminPassword(
struct _UNATTENDANSWER *rec
)
/*+
Routine Description:
Check for the "NoChange" keyword.
Arguments:
Returns:
Always TRUE.
--*/
{
//Ignore the check for 'No Change' in the encrypted password case.
if( !IsEncryptedAdminPasswordPresent() ){
if((rec->Type == UAT_STRING) && rec->Answer.String &&
!lstrcmpi(rec->Answer.String, L"NoChange")) {
DontChangeAdminPassword = TRUE;
rec->Answer.String[0] = (WCHAR)'\0';
}
}
return(TRUE);
}
BOOL
CheckMode(
struct _UNATTENDANSWER *rec
)
/*+
Routine Description:
Callback to check that the string used for the setup type is valid
Arguments:
Returns:
TRUE - Answer is valid
FALSE - Answer is invalid
--*/
{
MYASSERT(rec);
//
// Check to make sure that we have a string
//
if(rec->Type != UAT_STRING) {
return(FALSE);
}
//
// Check to see if the string is the custom or express one
//
if(lstrcmpi(rec->Answer.String,WINNT_A_CUSTOM)
&& lstrcmpi(rec->Answer.String,WINNT_A_EXPRESS)) {
//
// Free the old string and allocate a new one
//
MyFree(rec->Answer.String);
rec->Answer.String = pSetupDuplicateString(WINNT_A_EXPRESS);
rec->ParseErrors = TRUE;
}
return(TRUE);
}
BOOL
SetPid(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
/*++
Routine Description:
Callback for both the OEM and CD dialog boxes that split the
product string into the proper location boxes.
Arguments:
Returns:
TRUE - success
FALSE - failure
--*/
{
WCHAR *ptr;
UINT length;
WCHAR Buf[MAX_BUF];
WCHAR szPid[MAX_BUF];
MYASSERT(item);
MYASSERT(item->Item);
//
// Check to see if we found the pid and make sure that we have a string
//
if(!item->Item->Present || (item->Item->Type != UAT_STRING)) {
return(FALSE);
}
//
// oem and cd installs are both the same case for pid3.0
//
lstrcpyn(szPid, item->Item->Answer.String, MAX_BUF);
szPid[MAX_BUF - 1] = L'\0';
if ( ( lstrlen( szPid ) != (4 + MAX_PID30_EDIT*5) ) ||
( szPid[5] != (WCHAR)L'-' ) ||
( szPid[11] != (WCHAR)L'-' ) ||
( szPid[17] != (WCHAR)L'-' ) ||
( szPid[23] != (WCHAR)L'-' )
) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
if (item->Reserved1 > 5) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
ptr = &szPid[item->Reserved1*(MAX_PID30_EDIT+1)];
lstrcpyn(Pid30Text[item->Reserved1], ptr, MAX_PID30_EDIT+1 );
Pid30Text[item->Reserved1][MAX_PID30_EDIT] = (WCHAR)L'\0';
//
// Copy the string to a buffer, set the dialog text and return success.
//
lstrcpyn(Buf,ptr,MAX_PID30_EDIT+1);
SetDlgItemText(hwnd,item->ControlId,Buf);
return(TRUE);
}
BOOL
SetSetupMode(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
MYASSERT(item);
MYASSERT(item->Item);
//
// Make sure that we have a string
//
if(item->Item->Type != UAT_STRING) {
return(FALSE);
}
//
// Did we get a parse error? if so display something that the user can
// see so that the problem gets corrected in the future
//
if(item->Item->ParseErrors) {
PostMessage(hwnd,WM_IAMVISIBLE,0,0);
}
SetupMode = lstrcmpi(item->Item->Answer.String,WINNT_A_CUSTOM)
? SETUPMODE_TYPICAL
: SETUPMODE_CUSTOM;
return(!item->Item->ParseErrors);
}
#ifdef _X86_
BOOL
SetPentium(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
//
// Do nothing. The dialog procedure takes care of all the logic.
// See i386\fpu.c.
//
UNREFERENCED_PARAMETER(hwnd);
UNREFERENCED_PARAMETER(contextinfo);
UNREFERENCED_PARAMETER(item);
return(TRUE);
}
#endif
BOOL
SetStepsPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
return(TRUE);
}
BOOL
SetLastPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
return(TRUE);
}