Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1379 lines
37 KiB

/*++
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"
#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.
UAE_HIDDEN <- When it comes time to show the page to the user, what kind
of access should the user have to the string.
UAE_HIDDEN <- Unless the page has errors, the user can never
see it. And if does have errors, but in another item, then
item with this string CANNOT be editted, although it is shown
to the user
UAE_FIXED <- Don't display it to the user the first time, however
if there is an error in ANY page, then this page could be
actived, although not edited.
UAE_DEFAULT <- Don't display it to the user the first time,
however if there is an error in ANY page, then this page could
be actived, although it can be edited.
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_WIZNEXT);
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
//
BOOL
CheckServer(
struct _UNATTENDANSWER *rec
);
BOOL
CheckComputerName(
struct _UNATTENDANSWER *rec
);
BOOL
CheckMode(
struct _UNATTENDANSWER *rec
);
//
// SetActiveCallbacks
//
BOOL
SetPid(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetSetupMode(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetServerType(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetPentium(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
BOOL
SetRepairDisk(
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
);
BOOL
SetOptionsYesNo(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
);
//
// Do not change the order of these unless you know what you are doing
//
UNATTENDANSWER UnattendAnswerTable[] = {
{ UAE_PROGRAM, FALSE, FALSE, FALSE, 0,
pwGuiUnattended, pwProgram, pwNull,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_ARGUMENT, FALSE, FALSE, FALSE, 0,
pwGuiUnattended, pwArgument, pwNull,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_SERVER, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwServer, pwNull,
UAM_HIDDEN, UAT_STRING, CheckServer },
{ UAE_TIMEZONE, FALSE, TRUE, FALSE, 0,
pwGuiUnattended, pwTimeZone, pwTime,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_FULLNAME, FALSE, TRUE, FALSE, 0,
pwUserData, pwFullName, NULL,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_ORGNAME, FALSE, FALSE, FALSE, 0,
pwUserData, pwOrgName, pwNull,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_COMPNAME, FALSE, TRUE, FALSE, 0,
pwUserData, pwCompName, NULL,
UAM_HIDDEN, UAT_STRING, CheckComputerName },
{ UAE_PRODID, FALSE, TRUE, FALSE, 0,
pwUserData, pwProdId, NULL,
UAM_HIDDEN, UAT_STRING, NULL },
{ UAE_MODE, FALSE, TRUE, FALSE, 0,
pwUnattended, pwMode, pwExpress,
UAM_HIDDEN,UAT_STRING, CheckMode },
};
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, 1, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID2, 2, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] }
};
UNATTENDITEM ItemPidOem[] = {
{ IDT_EDIT_PID1, 1, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID2, 2, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
{ IDT_EDIT_PID3, 3, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] }
};
UNATTENDITEM ItemCompName[] = {
{ IDT_EDIT1, 0, 0, NULL, &UnattendAnswerTable[UAE_COMPNAME] }
};
UNATTENDITEM ItemServerType[] = {
{ 0, IDB_RADIO_1, IDB_RADIO_3, SetServerType, &UnattendAnswerTable[UAE_SERVER] }
};
#ifdef _X86_
UNATTENDITEM ItemPentium[] = {
{ 0, IDC_RADIO_1, IDC_RADIO_2, SetPentium, NULL }
};
#endif
UNATTENDITEM ItemRepairDisk[] = {
{ 0, IDB_RADIO_1, IDB_RADIO_2, SetRepairDisk, NULL }
};
UNATTENDITEM ItemOptionsYesNo[] = {
{ 0, 0, 0, SetOptionsYesNo, NULL }
};
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 },
{ IDD_WELCOMEBUTTONS, FALSE, FALSE, FALSE, 1, ItemSetup },
{ IDD_NAMEORG, FALSE, FALSE, FALSE, 2, ItemNameOrg },
{ IDD_PID_CD, FALSE, FALSE, FALSE, 2, ItemPidCd },
{ IDD_PID_OEM, FALSE, FALSE, FALSE, 3, ItemPidOem },
{ IDD_COMPUTERNAME, FALSE, FALSE, FALSE, 1, ItemCompName },
{ IDD_SERVERTYPE, FALSE, FALSE, FALSE, 1, ItemServerType },
{ IDD_ADMINPASSWORD, FALSE, FALSE, FALSE, 0, NULL },
{ IDD_CAIROUSERACCOUNT, FALSE, FALSE, FALSE, 0, NULL },
{ IDD_CAIRODOMAINNAME, FALSE, FALSE, FALSE, 0, NULL },
#ifdef DOLOCALUSER
{ IDD_USERACCOUNT, FALSE, FALSE, FALSE, 0, NULL },
#endif
#ifdef _X86_
{ IDD_PENTIUM, FALSE, FALSE, FALSE, 1, ItemPentium },
#endif
{ IDD_REPAIRDISK, FALSE, FALSE, FALSE, 1, ItemRepairDisk },
{ IDD_SPECIAL_OPTIONS, FALSE, FALSE, FALSE, 0, NULL },
{ IDD_OPTIONS_YESNO, FALSE, FALSE, FALSE, 1, ItemOptionsYesNo },
{ 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];
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];
WCHAR Def[MAX_BUF];
PWSTR Ptr;
if(!AnswerFile[0]) {
//
// We haven't calculated the path to $winnt$.sif yet, do so now
//
GetSystemDirectory(AnswerFile,MAX_PATH);
ConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
}
if(ans->DefaultAnswer) {
lstrcpyn(Def,ans->DefaultAnswer,MAX_BUF);
} else {
//
// There is no default answer that we can use, so we have to check to see
// if the key name exists first before we go out and fetch it
//
// BUGBUG: This code only work if the list of key names is smaller then
// the maximum buffer size.
//
if(!GetPrivateProfileString(ans->Section,NULL,L"",Buf,MAX_BUF,AnswerFile)) {
//
// We read 0 characters into the buffer, so nothing in the desired
// section is present, return the correct answer
//
ans->Present = FALSE;
return(!ans->Required);
}
//
// Search the buffer for the desired string
//
Ptr = Buf;
while(*Ptr) {
if(!lstrcmpi(Ptr,ans->Key)) {
//
// We have found a matching key
//
break;
}
Ptr += lstrlen(Ptr) + 1;
}
//
// Check to see if we found something or if we looking at a null char
//
if(*Ptr == 0) {
//
// Set Present to False since we don't have an answer
// The key does not exist in the file, return correct answer
//
ans->Present = FALSE;
return(!ans->Required);
}
//
// Set the default buffer value
//
Def[0] = 0;
}
if(!GetPrivateProfileString(ans->Section,ans->Key,Def,Buf,MAX_BUF,AnswerFile)) {
//
// We didn't read anything into the buffer, so something is wrong
//
ans->Present = FALSE;
return(!ans->Required);
}
//
// Fill in the answer structure
//
ans->Mode = UAM_HIDDEN;
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 = DuplicateString(Buf);
if(!ans->Answer.String) {
OutOfMemory(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 = ((Ptr[0] == L'y') || (Ptr[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.
--*/
{
BOOL Success = TRUE;
UINT i;
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;
}
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;
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
// 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 = FALSE;
stop = FALSE;
for(j=0;j<pPage->ItemCount;j++) {
pItem = &(pPage->Item[j]);
//
// If the item has a call back function then
// execute that function, otherwise try to load
// the answer into the appropriate message box
//
if(pItem->pfnSetActive) {
success = pItem->pfnSetActive(hwnd,0,pItem);
pPage->ShowPage |= !success;
stop |= !success;
} else if (!pItem->Item->Present) {
pPage->ShowPage |= pItem->Item->Required;
stop |= 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;
}
//
// Switch to set the visibility of the item on the screen
//
switch(pItem->Item->Mode) {
case UAM_HIDDEN:
pPage->ShowPage |= FALSE;
EnableWindow(GetDlgItem(hwnd,pItem->ControlId),FALSE);
break;
case UAM_FIXED:
pPage->ShowPage |= TRUE;
EnableWindow(GetDlgItem(hwnd,pItem->ControlId),FALSE);
break;
case UAM_DEFAULT:
pPage->ShowPage |= TRUE;
stop = TRUE;
EnableWindow(GetDlgItem(hwnd,pItem->ControlId),TRUE);
break;
default:
break;
}
}
}
//
// Allow the page to become activated
//
SetWindowLong(hwnd,DWL_MSGRESULT,0);
if(!stop) {
//
// Simulate the pressing of the next button
//
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
//
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) {
SetWindowLong(hwnd,DWL_MSGRESULT,-1);
} else {
SetWindowLong(hwnd,DWL_MSGRESULT,0);
}
return(pPage->ShowPage);
}
}
}
//
// We didn't find a matching id, stop at the page that called us.
//
SetWindowLong(hwnd,DWL_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(DuplicateString(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_LANSECNT)
&& 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
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 = DuplicateString(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 TempRpc[ MAX_PID20_RPC + 1 ];
WCHAR TempSite[ MAX_PID20_SITE + 1 ];
WCHAR TempSerialChk[ MAX_PID20_SERIAL_CHK + 1 ];
WCHAR TempRandom[ MAX_PID20_RANDOM + 1 ];
PWSTR szOemSite = L"-OEM-";
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);
}
if(item->Reserved2) {
//
// Case for an OEM PID dialog box
// Check that the string specified on the unattended script file
// represents a valid COA (OEM's PID).
// For the OEM case, the PID must have the same format of the COA
// specified in the Certificate of Authenticity, as follows:
//
// 12345-OEM-1234567-12345
//
// As a first validation test, we verify that the length is correct,
// that the 6th-10th character form the string "-OEM-", and that the
// 18th character is a '-'.
//
//
if( ( lstrlen( item->Item->Answer.String ) != MAX_PID20_RPC +
lstrlen( szOemSite ) +
MAX_PID20_SERIAL_CHK + 1 +
MAX_PID20_RANDOM ) ||
( _wcsnicmp( &item->Item->Answer.String[MAX_PID20_RPC], szOemSite, lstrlen( szOemSite ) ) ) ||
( item->Item->Answer.String[MAX_PID20_RPC +
lstrlen( szOemSite ) +
MAX_PID20_SERIAL_CHK] != (WCHAR)'-' )
) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
switch(item->Reserved1) {
case 1:
//
// Process the 'Rpc' field
//
ptr = &(item->Item->Answer.String[0]);
length = MAX_PID20_RPC;
lstrcpyn(Buf,ptr,length + 1);
Buf[MAX_PID20_RPC] = (WCHAR)'\0';
if( !ValidateOemRpc( Buf ) ) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
break;
case 2:
//
// Process the 'SerialChk' field
//
ptr = &(item->Item->Answer.String[MAX_PID20_RPC +
lstrlen(szOemSite) ]);
length = MAX_PID20_SERIAL_CHK;
lstrcpyn(Buf,ptr,length + 1);
Buf[MAX_PID20_SERIAL_CHK] = (WCHAR)'\0';
if( !ValidateOemSerialChk( Buf ) ) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
break;
case 3:
//
// Process the 'Random' field
//
ptr = &(item->Item->Answer.String[MAX_PID20_RPC +
lstrlen(szOemSite) +
MAX_PID20_SERIAL_CHK + 1]);
length = MAX_PID20_RANDOM;
lstrcpyn(Buf,ptr,length + 1);
Buf[MAX_PID20_RANDOM] = (WCHAR)'\0';
if( !ValidateOemRandom( Buf ) ) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
break;
default:
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
} else {
//
// Case for a CD PID dialog box
// Check that the string specified on the unattended script file
// represents a valid PID.
// For the CD Retail case, the PID must have the same format of
// the PID specified in the CD label, as follows:
//
// 123-1234567
//
// As a first validation test, we verify that the length is correct,
// and that the 4th character is '-'.
//
if( ( lstrlen( item->Item->Answer.String ) != MAX_PID20_SITE +
MAX_PID20_SERIAL_CHK + 1)
|| ( item->Item->Answer.String[MAX_PID20_SITE] != (WCHAR)'-' )
) {
//
// The Pid in the unattended script file is invalid.
//
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
switch(item->Reserved1) {
case 1:
//
// Process the 'Site' field.
//
ptr = &(item->Item->Answer.String[0]);
length = MAX_PID20_SITE;
lstrcpyn(Buf,ptr,length + 1);
Buf[MAX_PID20_SITE] = (WCHAR)'\0';
if( !ValidateCDRetailSite( Buf ) ) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
break;
case 2:
//
// Process the 'SerialChk' field.
//
ptr = &(item->Item->Answer.String[MAX_PID20_SITE+1]);
length = MAX_PID20_SERIAL_CHK;
lstrcpyn(Buf,ptr,length + 1);
Buf[MAX_PID20_SERIAL_CHK] = (WCHAR)'\0';
if( !ValidateSerialChk( Buf ) ) {
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
break;
default:
MyFree(item->Item->Answer.String);
item->Item->Present = FALSE;
return(FALSE);
}
}
//
// Copy the string to a buffer, set the dialog text and return success.
//
// lstrcpyn(Buf,ptr,length+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);
}
BOOL
SetServerType(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
MYASSERT(item);
MYASSERT(item->Item);
//
// Check to see if we got a parse error
//
if(item->Item->ParseErrors) {
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_1);
PostMessage(hwnd,WM_IAMVISIBLE,0,0);
return(FALSE);
}
//
// Check that we a string and that the string is present
//
if(!item->Item->Present || (item->Item->Type != UAT_STRING)) {
item->Item->ParseErrors = TRUE;
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_1);
if( !Preinstall ) {
PostMessage(hwnd,WM_IAMVISIBLE,0,0);
}
return(FALSE);
}
if(!lstrcmpi(item->Item->Answer.String,WINNT_A_LANMANNT) && ISDC(ProductType)) {
//
// Primary Domain Controller
//
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_1);
} else if(!lstrcmpi(item->Item->Answer.String,WINNT_A_LANSECNT) && ISDC(ProductType)) {
//
// Backup Domain Controller
//
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_2);
} else if(!lstrcmpi(item->Item->Answer.String,WINNT_A_SERVERNT)) {
//
// StandAlone Server
//
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_3);
} else {
//
// Check something even though an error occurred so the UI looks right.
//
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_3,IDB_RADIO_1);
item->Item->ParseErrors = TRUE;
PostMessage(hwnd,WM_IAMVISIBLE,0,0);
return(FALSE);
}
return(TRUE);
}
#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
SetRepairDisk(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
//
// Always refuse to create a repair disk
//
CheckRadioButton(hwnd,IDB_RADIO_1,IDB_RADIO_2,IDB_RADIO_2);
return(TRUE);
}
BOOL
SetOptionsYesNo(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
//
// Always refuse to show the optional components and always succeed.
//
ShowOptionalComponents = FALSE;
CheckRadioButton(hwnd,IDC_TYPICAL,IDC_CUSTOM,IDC_TYPICAL);
return(TRUE);
}
BOOL
SetStepsPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
//
// Only return true if no error occured in any page
//
if(UnattendWizard.Successful) {
return(TRUE);
}
//
// Reset the success flag;
// an error occured, so stop on this page
//
UnattendWizard.Successful = TRUE;
return(FALSE);
}
BOOL
SetLastPage(
HWND hwnd,
DWORD contextinfo,
struct _UNATTENDITEM *item
)
{
//
// Only return true if no error occured in any page
//
if(UnattendWizard.Successful) {
return(TRUE);
}
//
// An error occured, so stop on this page
//
return(FALSE);
}