Source code of Windows XP (NT5)
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.

1373 lines
34 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. Unattend.c
  5. Description:
  6. This performs all of the automated installation GUI mode setup.
  7. See below for usage and modification information
  8. Author:
  9. Stephane Plante (t-stepl) 4-Sep-1995
  10. Revision History:
  11. 15-Sep-1995 (t-stepl) rewritten in table format
  12. 26-Feb-1996 (tedm) massive cleanup
  13. --*/
  14. #include "setupp.h"
  15. #include <pencrypt.h>
  16. #pragma hdrstop
  17. /*
  18. Table-driven unattended engine
  19. ------------------------------
  20. There are two interrelated tables.
  21. The first table is concerned with fetching data from the parameters file
  22. ($winnt$.inf) and processing it into a format that can be accessed by the
  23. second table. The second table is associated with the pages in the setup wizard,
  24. and provides the unattend engine with the rules for filling in the contents
  25. of the associated pages from the data contained in the first table.
  26. Adding a new piece of data to the parameters file
  27. -------------------------------------------------
  28. In the header file there is an enumerated type called UNATTENDENTRIES.
  29. Add an entry for your data to the end of this enum. Now add an entry to the
  30. UNATTENDANSWER table.
  31. Here's an explanation of an entry in the UNATTENDEDANSWER table:
  32. { UAE_PROGRAM, <-This is the identifier for the data item that I want
  33. to fetch. It is used to index into the table array
  34. FALSE, <-This is a runtime variable. Just keep it as false
  35. FALSE, <-If this is true, then it is considered an error in the
  36. unattend script if this value is unspecified. If it is
  37. false, then it does not matter if the value is not
  38. present.
  39. FALSE, <-Another runtime flag. Just keep it as false
  40. 0, <-This is the answer we have initially. Since it gets overwritten
  41. quickly, there is no reason why not to set it to 0
  42. pwGuiUnattended <- This is the string which identifies the section we want
  43. pwProgram <- This is the string which identifies the key we want
  44. pwNull <- This identifies the default. Note: NULL means that there is
  45. no default and so it is a serious error if the key does not
  46. exist in the file. pwNull, on the other hand, means the
  47. empty string.
  48. UAT_STRING <- What format we want the answer in. Can be as a string, boolean
  49. or ULONG
  50. NULL <- No callback function exists, however if one did, then must
  51. in the form of: BOOL fnc( struct _UNATTENDANSWER *rec)
  52. Where the fnc returns TRUE if the answer contained in the
  53. record is correct, or FALSE if the answer contained in the
  54. record is incorrect. This callback is meant to allow the
  55. programmer the ability to check to see if his answer is correct.
  56. Note: there is no bound as to when this callback can be issued.
  57. As such, no code which depends on a certain state of the
  58. installation should be used. For the record, the first time
  59. that an answer is required is the time when all records are
  60. filled in in the theory that it is cheaper to do all of the
  61. disk access at once rather then doing it on a as required basis.
  62. Adding/changing wizard pages
  63. ----------------------------
  64. Each page contains a series of items which must be filled in by the user.
  65. Since the user wants hands off operation, he is counting on us
  66. to do that filling in. As such, we require information about what elements are
  67. contained on each page. To do this, we define an array whose elements each
  68. describe a single element on the page. Here is the example from the NameOrg
  69. page:
  70. UNATTENDITEM ItemNameOrg[] = {
  71. { IDT_NAME, <-This is the label that identifies the item to which we
  72. will try to send messages to, using SetDlgItemText().
  73. 0, <-One of the reserved words which can be used for
  74. information passing during a callback
  75. 0, <-The second such word
  76. NULL, <-Callback function. When we are trying to do something
  77. complicated for the item (like comparing two strings)
  78. it is easier to hardcode it in C. The format for it is:
  79. BOOL fnc(HWND hwnd,DWORD contextinfo,
  80. struct _UNATTENDITEM *item), where contextinfo is
  81. a pointer to the page that the item resides on. The
  82. function returns TRUE if is succeeded and doesn't think
  83. that the user should see the page. FALSE otherwise.
  84. &UnattendAnswerTable[UAE_FULLNAME]
  85. ^- This is a pointer to the data table so that we know
  86. how to fill the item. If a callback is specified, this
  87. could be set to null. Note that reference is made using
  88. the enum that had defined previously. This is why
  89. keeping the answer data table in order is so critical.
  90. },
  91. { IDT_ORGANIZATION, 0, 0, FALSE, NULL, &UnattendAnswerTable[UAE_ORGNAME] }
  92. };
  93. After this table has been created (if required), then you are ready to add
  94. an entry to the UnattendPageTable[]. In this case, order doesn't matter,
  95. but it is general practice to keep the entries in the same order
  96. as the pages. Here is the entry in the table for the NAMEORG page:
  97. {
  98. IDD_NAMEORG, <- This is the page id. We search based on this key.
  99. Simply use whatever resourcename you used for the
  100. dialogs.dlg file
  101. FALSE, <- Runtime flag. Set it as false
  102. FALSE, <- Runtime flag. Set it as false
  103. FALSE, <- If this flag is true, then if there is an error
  104. that occured in the unattended process, then this
  105. page will always be displayed for the user. Good
  106. for the start and finish pages
  107. 2, <- The number of items in the array
  108. ItemNameOrg <- The array of items
  109. },
  110. Once this is done, then you can add:
  111. if (Unattended) {
  112. UnattendSetActiveDlg( hwnd, <pageid> );
  113. }
  114. break;
  115. As the last thing in the code for the page's setactive.
  116. This function does is that it sets the DWL_MSGRESULT based on wether or
  117. not the user should see the page and returns that value also (TRUE -- user
  118. should see the page, FALSE, he should not). Then you should add:
  119. case WM_SIMULATENEXT:
  120. PropSheet_PressButton(GetParent(hwnd),PSBTN_NEXT);
  121. to the DlgProc for the page. This means that the code in PSN_WIZNEXT
  122. case will be executed.
  123. You can also use UnattendErrorDlg( hwnd, <pageid> ); in the PSN_WIZNEXT
  124. case if you detect any errors. That will allow unattended operation to try
  125. to clean itself up a bit before control returns to the user for the page.
  126. Note however that as soon as the user hits the next or back button that
  127. control returns to the unattended engine.
  128. */
  129. //
  130. // Initialization Callbacks
  131. //
  132. // These are used to verify that the entries in the answer file are valid.
  133. //
  134. BOOL
  135. CheckServer(
  136. struct _UNATTENDANSWER *rec
  137. );
  138. BOOL
  139. CheckComputerName(
  140. struct _UNATTENDANSWER *rec
  141. );
  142. BOOL
  143. CheckAdminPassword(
  144. struct _UNATTENDANSWER *rec
  145. );
  146. BOOL
  147. CheckMode(
  148. struct _UNATTENDANSWER *rec
  149. );
  150. //
  151. // SetActive Callbacks
  152. //
  153. // When a wizard page receives a PSN_SETACTIVE notification, a callback is used
  154. // to set the controls on that wizard page, based on the values in the answer
  155. // file.
  156. //
  157. BOOL
  158. SetPid(
  159. HWND hwnd,
  160. DWORD contextinfo,
  161. struct _UNATTENDITEM *item
  162. );
  163. BOOL
  164. SetSetupMode(
  165. HWND hwnd,
  166. DWORD contextinfo,
  167. struct _UNATTENDITEM *item
  168. );
  169. BOOL
  170. SetPentium(
  171. HWND hwnd,
  172. DWORD contextinfo,
  173. struct _UNATTENDITEM *item
  174. );
  175. BOOL
  176. SetLastPage(
  177. HWND hwnd,
  178. DWORD contextinfo,
  179. struct _UNATTENDITEM *item
  180. );
  181. BOOL
  182. SetStepsPage(
  183. HWND hwnd,
  184. DWORD contextinfo,
  185. struct _UNATTENDITEM *item
  186. );
  187. //
  188. // Do not change the order of these unless you know what you are doing.
  189. // These entries must me in the same order as the UNATTENDENTRIES enum.
  190. //
  191. UNATTENDANSWER UnattendAnswerTable[] = {
  192. { UAE_PROGRAM, FALSE, FALSE, FALSE, 0,
  193. pwGuiUnattended, pwProgram, pwNull,
  194. UAT_STRING, NULL },
  195. { UAE_ARGUMENT, FALSE, FALSE, FALSE, 0,
  196. pwGuiUnattended, pwArgument, pwNull,
  197. UAT_STRING, NULL },
  198. { UAE_TIMEZONE, FALSE, TRUE, FALSE, 0,
  199. pwGuiUnattended, pwTimeZone, pwTime,
  200. UAT_STRING, NULL },
  201. { UAE_FULLNAME, FALSE, TRUE, FALSE, 0,
  202. pwUserData, pwFullName, NULL,
  203. UAT_STRING, NULL },
  204. { UAE_ORGNAME, FALSE, FALSE, FALSE, 0,
  205. pwUserData, pwOrgName, pwNull,
  206. UAT_STRING, NULL },
  207. { UAE_COMPNAME, FALSE, TRUE, FALSE, 0,
  208. pwUserData, pwCompName, NULL,
  209. UAT_STRING, CheckComputerName },
  210. { UAE_ADMINPASS, FALSE, TRUE, FALSE, 0,
  211. pwGuiUnattended, pwAdminPassword, NULL,
  212. UAT_STRING, CheckAdminPassword },
  213. { UAE_PRODID, FALSE, TRUE, FALSE, 0,
  214. pwUserData, pwProductKey, NULL,
  215. UAT_STRING, NULL },
  216. { UAE_MODE, FALSE, TRUE, FALSE, 0,
  217. pwUnattended, pwMode, pwExpress,
  218. UAT_STRING, CheckMode },
  219. { UAE_AUTOLOGON, FALSE, TRUE, FALSE, 0,
  220. pwGuiUnattended, pwAutoLogon, pwNull,
  221. UAT_STRING, NULL },
  222. { UAE_PROFILESDIR, FALSE, TRUE, FALSE, 0,
  223. pwGuiUnattended, pwProfilesDir, pwNull,
  224. UAT_STRING, NULL },
  225. { UAE_PROGRAMFILES, FALSE, FALSE, FALSE, 0,
  226. pwUnattended, pwProgramFilesDir, pwNull,
  227. UAT_STRING, NULL },
  228. { UAE_COMMONPROGRAMFILES, FALSE, FALSE, FALSE, 0,
  229. pwUnattended, pwCommonProgramFilesDir, pwNull,
  230. UAT_STRING, NULL },
  231. { UAE_PROGRAMFILES_X86, FALSE, FALSE, FALSE, 0,
  232. pwUnattended, pwProgramFilesX86Dir, pwNull,
  233. UAT_STRING, NULL },
  234. { UAE_COMMONPROGRAMFILES_X86, FALSE, FALSE, FALSE, 0,
  235. pwUnattended, pwCommonProgramFilesX86Dir, pwNull,
  236. UAT_STRING, NULL },
  237. };
  238. UNATTENDITEM ItemSetup[] = {
  239. { 0, IDC_TYPICAL, IDC_CUSTOM, SetSetupMode, &UnattendAnswerTable[UAE_MODE] }
  240. };
  241. UNATTENDITEM ItemNameOrg[] = {
  242. { IDT_NAME, 0, 0, NULL, &UnattendAnswerTable[UAE_FULLNAME] },
  243. { IDT_ORGANIZATION, 0, 0, NULL, &UnattendAnswerTable[UAE_ORGNAME] }
  244. };
  245. UNATTENDITEM ItemPidCd[] = {
  246. { IDT_EDIT_PID1, 0, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  247. { IDT_EDIT_PID2, 1, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  248. { IDT_EDIT_PID3, 2, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  249. { IDT_EDIT_PID4, 3, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  250. { IDT_EDIT_PID5, 4, 0, SetPid, &UnattendAnswerTable[UAE_PRODID] }
  251. };
  252. UNATTENDITEM ItemPidOem[] = {
  253. { IDT_EDIT_PID1, 0, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  254. { IDT_EDIT_PID2, 1, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  255. { IDT_EDIT_PID3, 2, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  256. { IDT_EDIT_PID4, 3, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] },
  257. { IDT_EDIT_PID5, 4, 1, SetPid, &UnattendAnswerTable[UAE_PRODID] }
  258. };
  259. UNATTENDITEM ItemCompName[] = {
  260. { IDT_EDIT1, 0, 0, NULL, &UnattendAnswerTable[UAE_COMPNAME] },
  261. { IDT_EDIT2, 0, 0, NULL, &UnattendAnswerTable[UAE_ADMINPASS] },
  262. { IDT_EDIT3, 0, 0, NULL, &UnattendAnswerTable[UAE_ADMINPASS] }
  263. };
  264. #ifdef _X86_
  265. UNATTENDITEM ItemPentium[] = {
  266. { 0, IDC_RADIO_1, IDC_RADIO_2, SetPentium, NULL }
  267. };
  268. #endif
  269. UNATTENDITEM ItemStepsPage[] = {
  270. { 0, 0, 0, SetStepsPage, NULL }
  271. };
  272. UNATTENDITEM ItemLastPage[] = {
  273. { 0, 0, 0, SetLastPage, NULL }
  274. };
  275. UNATTENDPAGE UnattendPageTable[] = {
  276. { IDD_WELCOME, FALSE, FALSE, TRUE, 0, NULL },
  277. { IDD_PREPARING, FALSE, FALSE, FALSE, 0, NULL },
  278. #ifdef PNP_DEBUG_UI
  279. { IDD_HARDWARE, FALSE, FALSE, TRUE, 0, NULL },
  280. #endif // PNP_DEBUG_UI
  281. { IDD_WELCOMEBUTTONS, FALSE, FALSE, FALSE, 1, ItemSetup },
  282. { IDD_REGIONAL_SETTINGS, FALSE, FALSE, FALSE, 0, NULL },
  283. { IDD_NAMEORG, FALSE, FALSE, FALSE, 2, ItemNameOrg },
  284. { IDD_PID_CD, FALSE, FALSE, FALSE, 5, ItemPidCd },
  285. { IDD_PID_OEM, FALSE, FALSE, FALSE, 5, ItemPidOem },
  286. { IDD_COMPUTERNAME, FALSE, FALSE, FALSE, 3, ItemCompName },
  287. #ifdef DOLOCALUSER
  288. { IDD_USERACCOUNT, FALSE, FALSE, FALSE, 0, NULL },
  289. #endif
  290. #ifdef _X86_
  291. { IDD_PENTIUM, FALSE, FALSE, FALSE, 1, ItemPentium },
  292. #endif
  293. { IDD_OPTIONS, FALSE, FALSE, FALSE, 0, NULL },
  294. { IDD_STEPS1, FALSE, FALSE, TRUE, 1, ItemStepsPage },
  295. { IDD_LAST_WIZARD_PAGE, FALSE, FALSE, TRUE, 1, ItemLastPage }
  296. };
  297. UNATTENDWIZARD UnattendWizard = {
  298. FALSE, FALSE, TRUE,
  299. sizeof(UnattendPageTable)/sizeof(UnattendPageTable[0]),
  300. UnattendPageTable,
  301. sizeof(UnattendAnswerTable)/sizeof(UnattendAnswerTable[0]),
  302. UnattendAnswerTable
  303. };
  304. //
  305. // Global Pointer to the Answer file
  306. //
  307. WCHAR AnswerFile[MAX_PATH] = TEXT("");
  308. BOOL
  309. GetAnswerFileSetting (
  310. IN PCWSTR Section,
  311. IN PCWSTR Key,
  312. OUT PWSTR Buffer,
  313. IN UINT BufferSize
  314. )
  315. /*++
  316. Routine Description:
  317. GetAnswerFileSetting uses the private profile APIs to obtain an answer file
  318. string from %systemroot%\system32\$winnt$.inf. It also performs %% removal,
  319. since $winnt$.inf is an INF, not an INI.
  320. Arguments:
  321. Section - Specifies the section to retreive the value from (such as
  322. GuiUnattended)
  323. Key - Specifies the key within the section (such as TimeZone)
  324. Buffer - Receives the value
  325. BufferSize - Specifies the size, in WCHARs, of Buffer.
  326. Return Value:
  327. TRUE if the setting was retrived, FALSE otherwise.
  328. --*/
  329. {
  330. PCWSTR src;
  331. PWSTR dest;
  332. WCHAR testBuf[3];
  333. MYASSERT (BufferSize > 2);
  334. if (!AnswerFile[0]) {
  335. GetSystemDirectory (AnswerFile, MAX_PATH);
  336. pSetupConcatenatePaths (AnswerFile, WINNT_GUI_FILE, MAX_PATH, NULL);
  337. SetEnvironmentVariable (L"UnattendFile", AnswerFile);
  338. }
  339. if (!GetPrivateProfileString (
  340. Section,
  341. Key,
  342. L"",
  343. Buffer,
  344. BufferSize,
  345. AnswerFile
  346. )) {
  347. //
  348. // String not present or is empty -- try again with a different
  349. // default. If the string is empty, we'll get back 0. If the key does
  350. // not exist, we'll get back 1.
  351. //
  352. MYASSERT (BufferSize == 0 || *Buffer == 0);
  353. return 0 == GetPrivateProfileString (
  354. Section,
  355. Key,
  356. L"X",
  357. testBuf,
  358. 3,
  359. AnswerFile
  360. );
  361. }
  362. //
  363. // We obtained the string. Now remove pairs of %.
  364. //
  365. if (BufferSize) {
  366. src = Buffer;
  367. dest = Buffer;
  368. while (*src) {
  369. if (src[0] == L'%' && src[1] == L'%') {
  370. src++;
  371. }
  372. *dest++ = *src++;
  373. }
  374. *dest = 0;
  375. }
  376. return TRUE;
  377. }
  378. BOOL
  379. UnattendFindAnswer(
  380. IN OUT PUNATTENDANSWER ans
  381. )
  382. /*++
  383. Routine Description:
  384. Fills in the response from the unattend file to the key 'id' into
  385. the structure pointed to by 'ans'. If a non-null 'def' is specified
  386. and no answer exists in the file, 'def' is parsed as the answer.
  387. Arguments:
  388. ans - pointer to the structure information for the answer
  389. Return Value:
  390. TRUE - 'ans' structure has been filled in with an answer
  391. FALSE - otherwise
  392. --*/
  393. {
  394. WCHAR Buf[MAX_BUF];
  395. MYASSERT(AnswerFile[0]);
  396. if (!GetAnswerFileSetting (ans->Section, ans->Key, Buf, MAX_BUF)) {
  397. //
  398. // Setting does not exist. If there is a default, use it.
  399. //
  400. if (ans->DefaultAnswer) {
  401. lstrcpyn (Buf, ans->DefaultAnswer, MAX_BUF);
  402. } else {
  403. ans->Present = FALSE;
  404. return (!ans->Required);
  405. }
  406. }
  407. //
  408. // Assume empty string means the string does not exist. This is how the
  409. // original implementation worked.
  410. //
  411. if (*Buf == 0) {
  412. ans->Present = FALSE;
  413. return !ans->Required;
  414. }
  415. //
  416. // Found a value, or using the default
  417. //
  418. ans->Present = TRUE;
  419. //
  420. // Copy the data into the answer structure. This requires
  421. // switching on the type of data expected and converting it to
  422. // the required format. In the case of strings, it also means
  423. // allocating a pool of memory for the result
  424. //
  425. switch(ans->Type) {
  426. case UAT_STRING:
  427. //
  428. // We allocate some memory, so we must free it later
  429. //
  430. ans->Answer.String = pSetupDuplicateString(Buf);
  431. if(!ans->Answer.String) {
  432. pSetupOutOfMemory(GetActiveWindow());
  433. return(FALSE);
  434. }
  435. break;
  436. case UAT_LONGINT:
  437. //
  438. // Simply convert the number from string to long
  439. //
  440. ans->Answer.Num = _wtol(Buf);
  441. break;
  442. case UAT_BOOLEAN:
  443. //
  444. // check to see if the answer is yes
  445. //
  446. ans->Answer.Bool = ((Buf[0] == L'y') || (Buf[0] == L'Y'));
  447. break;
  448. default:
  449. break;
  450. }
  451. //
  452. // Execute any callbacks if present
  453. //
  454. if(ans->pfnCheckValid) {
  455. if(!ans->pfnCheckValid(ans)) {
  456. ans->Present = FALSE;
  457. ans->ParseErrors = TRUE;
  458. return(!ans->Required);
  459. }
  460. }
  461. //
  462. // Success.
  463. //
  464. return(TRUE);
  465. }
  466. VOID
  467. UnattendInitialize(
  468. VOID
  469. )
  470. /*++
  471. Routine Description:
  472. Initialize unattended mode support by loading all answers
  473. from the unattend file.
  474. Arguments:
  475. None.
  476. Return Value:
  477. None.
  478. --*/
  479. {
  480. WCHAR p[MAX_BUF];
  481. DWORD Result;
  482. BOOL Success = TRUE;
  483. UINT i;
  484. //
  485. // If we haven't calculated the path to $winnt$.sif yet, do so now
  486. //
  487. if(!AnswerFile[0]) {
  488. GetSystemDirectory(AnswerFile,MAX_PATH);
  489. pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
  490. SetEnvironmentVariable( L"UnattendFile", AnswerFile );
  491. }
  492. if( MiniSetup ) {
  493. WCHAR MyAnswerFile[MAX_PATH];
  494. //
  495. // First, see if there's a sysprep.inf on the a:\ drive. If so, use it.
  496. //
  497. lstrcpy( MyAnswerFile, TEXT("a:\\sysprep.inf") );
  498. if( !FileExists( MyAnswerFile, NULL ) ) {
  499. //
  500. // Nope. Check for a \sysprep\sysprep.inf.
  501. //
  502. Result = GetWindowsDirectory( MyAnswerFile, MAX_PATH );
  503. if( Result == 0) {
  504. MYASSERT(FALSE);
  505. return;
  506. }
  507. MyAnswerFile[3] = 0;
  508. pSetupConcatenatePaths( MyAnswerFile, TEXT("sysprep\\sysprep.inf"), MAX_PATH, NULL );
  509. }
  510. //
  511. // We've assumed that we're running unattended, but
  512. // network setup hates it when we pretend to be unattended, but
  513. // don't provide an answer file. So if there is no answer file,
  514. // quit the facade.
  515. //
  516. Unattended = FileExists(MyAnswerFile, NULL);
  517. Preinstall = Unattended;
  518. //
  519. // Now either replace or delete the original unattend file.
  520. // We do this so that we don't erroneously pickup unattend
  521. // entries out of the old answer file. However, if OOBE is
  522. // running, we still need the old answerfile.
  523. //
  524. if( Unattended ) {
  525. CopyFile( MyAnswerFile, AnswerFile, FALSE );
  526. } else if ( !OobeSetup ) {
  527. DeleteFile( AnswerFile );
  528. }
  529. }
  530. //
  531. // We need to make the product id an alias for the product key.
  532. //
  533. if ( GetPrivateProfileString(
  534. pwUserData, pwProdId, pwNull, p, MAX_BUF, AnswerFile)
  535. ) {
  536. if ( !WritePrivateProfileString(
  537. pwUserData, pwProductKey, p, AnswerFile ) ) {
  538. SetupDebugPrint( L"SETUP: WritePrivateProfileString failed to write the product key in UnattendInitialize()." );
  539. }
  540. }
  541. //
  542. // Now get all the answers.
  543. //
  544. MYASSERT(!UnattendWizard.Initialized);
  545. UnattendWizard.Initialized = TRUE;
  546. for(i=0; i<UnattendWizard.AnswerCount; i++) {
  547. //
  548. // Check to make sure that the table order hasn't changed
  549. // and load the appropriate answer
  550. //
  551. MYASSERT((UINT)UnattendWizard.Answer[i].AnswerId == i);
  552. Success &= UnattendFindAnswer(&UnattendWizard.Answer[i]);
  553. }
  554. UnattendWizard.ShowWizard = !Success;
  555. }
  556. LRESULT
  557. SendDlgMessage (
  558. HWND hdlg,
  559. UINT Message,
  560. WPARAM wParam,
  561. LPARAM lParam
  562. )
  563. {
  564. LRESULT OldResult;
  565. LRESULT Result;
  566. OldResult = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
  567. SendMessage (hdlg, Message, wParam, lParam);
  568. Result = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
  569. SetWindowLongPtr (hdlg, DWLP_MSGRESULT, OldResult);
  570. return Result;
  571. }
  572. BOOL
  573. ReturnDlgResult (
  574. HWND hdlg,
  575. LRESULT Result
  576. )
  577. {
  578. SetWindowLongPtr (hdlg, DWLP_MSGRESULT, Result);
  579. return TRUE;
  580. }
  581. VOID
  582. UnattendAdvanceIfValid (
  583. IN HWND hwnd
  584. )
  585. {
  586. LRESULT ValidationState;
  587. //
  588. // Validate wizard page data with UI
  589. //
  590. ValidationState = SendDlgMessage (hwnd, WMX_VALIDATE, 0, TRUE);
  591. if (ValidationState == VALIDATE_DATA_INVALID) {
  592. SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_DISALLOWED);
  593. } else {
  594. SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_OK);
  595. }
  596. }
  597. BOOL
  598. UnattendSetActiveDlg(
  599. IN HWND hwnd,
  600. IN DWORD controlid
  601. )
  602. /*++
  603. Routine Description:
  604. Initialize unattended mode support by loading all answers
  605. from the unattend file.
  606. Arguments:
  607. None.
  608. Return Value:
  609. TRUE - Page will become active
  610. FALSE - Page will not become active
  611. --*/
  612. {
  613. PUNATTENDPAGE pPage;
  614. PUNATTENDITEM pItem;
  615. BOOL success;
  616. UINT i,j;
  617. MYASSERT(UnattendWizard.Initialized);
  618. for(i=0; i<UnattendWizard.PageCount; i++) {
  619. if(controlid == UnattendWizard.Page[i].PageId) {
  620. //
  621. // Found Matching Page entry
  622. // Check to see if we have already loaded the page
  623. //
  624. pPage = & (UnattendWizard.Page[i]);
  625. if(!pPage->LoadPage) {
  626. //
  627. // Set the flags that load and display the page and
  628. // the flag that controls wether or not to stop on this page
  629. //
  630. pPage->LoadPage = TRUE;
  631. pPage->ShowPage = (UnattendMode == UAM_PROVIDEDEFAULT);
  632. for(j=0;j<pPage->ItemCount;j++) {
  633. pItem = &(pPage->Item[j]);
  634. if(pItem->pfnSetActive) {
  635. //
  636. // If the item has a call back function then
  637. // execute that function, otherwise try to load
  638. // the answer into the appropriate message box
  639. //
  640. success = pItem->pfnSetActive(hwnd,0,pItem);
  641. pPage->ShowPage |= !success;
  642. } else if (!pItem->Item->Present) {
  643. //
  644. // The answer for this item is missing.
  645. //
  646. pPage->ShowPage |= pItem->Item->Required;
  647. } else {
  648. //
  649. // Switch to set the text of the item on the screen
  650. //
  651. switch(pItem->Item->Type) {
  652. case UAT_STRING:
  653. SetDlgItemText(hwnd,pItem->ControlId,pItem->Item->Answer.String);
  654. break;
  655. case UAT_LONGINT:
  656. case UAT_BOOLEAN:
  657. case UAT_NONE:
  658. default:
  659. break;
  660. }
  661. if( UnattendMode == UAM_PROVIDEDEFAULT ||
  662. UnattendMode == UAM_DEFAULTHIDE) {
  663. EnableWindow(GetDlgItem(hwnd,pItem->ControlId), TRUE);
  664. } else {
  665. EnableWindow(GetDlgItem(hwnd,pItem->ControlId),FALSE);
  666. }
  667. } // if (pItem
  668. } // for(j
  669. //
  670. // Allow the page to become activated
  671. //
  672. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  673. if(!pPage->ShowPage) {
  674. //
  675. // Perform validation, skip activation if validation succeeds.
  676. //
  677. if (SendDlgMessage (hwnd, WMX_VALIDATE, 0, 0) == 1) {
  678. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
  679. return FALSE;
  680. }
  681. //
  682. // Simulate the pressing of the next button, which causes the
  683. // wizard page proc to evaluate the data in its controls, and
  684. // throw up popups to the user.
  685. //
  686. PostMessage(hwnd,WM_SIMULATENEXT,0,0);
  687. } else if (!pPage->NeverSkip) {
  688. //
  689. // Pages which are marked as NeverSkip should not
  690. // cause the unattended status to be considered
  691. // unsuccessful.
  692. //
  693. // We can't skip this page so mark the init as
  694. // unsuccessful. If this is the first error in a fully
  695. // unattended setup, notify the user.
  696. //
  697. if(UnattendMode == UAM_FULLUNATTENDED) {
  698. SetuplogError(
  699. LogSevError,
  700. SETUPLOG_USE_MESSAGEID,
  701. MSG_LOG_BAD_UNATTEND_PARAM,
  702. pItem->Item->Key,
  703. pItem->Item->Section,
  704. NULL,NULL);
  705. if(UnattendWizard.Successful) {
  706. MessageBoxFromMessage(
  707. MainWindowHandle,
  708. MSG_FULLUNATTENDED_ERROR,
  709. NULL,
  710. IDS_ERROR,
  711. MB_ICONERROR | MB_OK | MB_SYSTEMMODAL
  712. );
  713. }
  714. }
  715. UnattendWizard.Successful = FALSE;
  716. }
  717. return(TRUE);
  718. } else {
  719. //
  720. // The Page has already been loaded, so we don't do that again
  721. // If we are ShowPage is FALSE, then we don't show the page to
  722. // the user, otherwise we do.
  723. //
  724. if(!pPage->ShowPage && !pPage->NeverSkip) {
  725. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
  726. } else {
  727. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  728. }
  729. return(pPage->ShowPage);
  730. }
  731. }
  732. }
  733. //
  734. // We didn't find a matching id, stop at the page that called us.
  735. //
  736. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  737. return(TRUE);
  738. }
  739. BOOL
  740. UnattendErrorDlg(
  741. IN HWND hwnd,
  742. IN DWORD controlid
  743. )
  744. /*++
  745. Routine Description:
  746. Called when an error occurs in a DLG. Enables all windows
  747. in the dialog and turns off the successful flag for the
  748. unattend wizard
  749. Arguments:
  750. Return Value:
  751. Boolean value indicating outcome.
  752. --*/
  753. {
  754. PUNATTENDPAGE pPage;
  755. PUNATTENDITEM pItem;
  756. BOOL success;
  757. BOOL stop;
  758. UINT i,j;
  759. MYASSERT(UnattendWizard.Initialized);
  760. for(i=0; i<UnattendWizard.PageCount; i++) {
  761. if(controlid == UnattendWizard.Page[i].PageId) {
  762. //
  763. // Found Matching Page entry
  764. //
  765. pPage = &UnattendWizard.Page[i];
  766. if(!pPage->LoadPage) {
  767. //
  768. // The Page hasn't been loaded, so it isn't correct
  769. //
  770. continue;
  771. }
  772. //
  773. // Always display the page from now on
  774. //
  775. pPage->ShowPage = TRUE;
  776. //
  777. // Enable all the items
  778. //
  779. for (j=0;j<pPage->ItemCount;j++) {
  780. pItem = &(pPage->Item[j]);
  781. if(pItem->pfnSetActive) {
  782. //
  783. // if this is present then we assume that the
  784. // callback handled itself properly already
  785. //
  786. continue;
  787. }
  788. EnableWindow( GetDlgItem(hwnd,pItem->ControlId), TRUE);
  789. }
  790. }
  791. }
  792. UnattendWizard.Successful = FALSE;
  793. return(TRUE);
  794. }
  795. PWSTR
  796. UnattendFetchString(
  797. IN UNATTENDENTRIES entry
  798. )
  799. /*++
  800. Routine Description:
  801. Finds the string which corresponds to 'entry' in the answer
  802. table and returns a pointer to a copy of that string
  803. Arguments:
  804. entry - which answer do you want?
  805. Return Value:
  806. NULL - if any errors occur
  807. string - if a normal string
  808. Note: if the answer is an int or a bool or some other type,
  809. the behavior of this function is undefined (for now it will
  810. return NULL -- in the future it might make sense to turn these
  811. into strings...)
  812. --*/
  813. {
  814. MYASSERT(UnattendWizard.Initialized);
  815. //
  816. // Sanity check to make sure that the order of the answers is
  817. // what we expect.
  818. //
  819. MYASSERT(UnattendWizard.Answer[entry].AnswerId == entry);
  820. if(!UnattendWizard.Answer[entry].Present
  821. || (UnattendWizard.Answer[entry].Type != UAT_STRING)) {
  822. //
  823. // There is no string to return
  824. //
  825. return NULL;
  826. }
  827. return(pSetupDuplicateString(UnattendWizard.Answer[entry].Answer.String));
  828. }
  829. BOOL
  830. CheckServer(
  831. struct _UNATTENDANSWER *rec
  832. )
  833. /*++
  834. Routine Description:
  835. Callback to check that the string used for the server type is valid
  836. Arguments:
  837. Return Value:
  838. TRUE - Answer is valid
  839. FALSE - Answer is invalid
  840. --*/
  841. {
  842. MYASSERT(rec);
  843. //
  844. // Check to make sure that we have a string
  845. //
  846. if(rec->Type != UAT_STRING) {
  847. return(FALSE);
  848. }
  849. //
  850. // Check to see if we have one of the valid strings
  851. //
  852. if(lstrcmpi(rec->Answer.String,WINNT_A_LANMANNT)
  853. && lstrcmpi(rec->Answer.String,WINNT_A_SERVERNT)) {
  854. //
  855. // We don't have a valid string, so we can clean up the answer
  856. //
  857. MyFree(rec->Answer.String);
  858. rec->Present = FALSE;
  859. rec->ParseErrors = TRUE;
  860. return(FALSE);
  861. }
  862. return(TRUE);
  863. }
  864. BOOL
  865. CheckComputerName(
  866. struct _UNATTENDANSWER *rec
  867. )
  868. /*+
  869. Routine Description:
  870. Uppercase the computer name that comes out of the unattended file.
  871. Arguments:
  872. Returns:
  873. Always TRUE.
  874. --*/
  875. {
  876. if((rec->Type == UAT_STRING) && rec->Answer.String) {
  877. CharUpper(rec->Answer.String);
  878. }
  879. return(TRUE);
  880. }
  881. BOOL
  882. CheckAdminPassword(
  883. struct _UNATTENDANSWER *rec
  884. )
  885. /*+
  886. Routine Description:
  887. Check for the "NoChange" keyword.
  888. Arguments:
  889. Returns:
  890. Always TRUE.
  891. --*/
  892. {
  893. //Ignore the check for 'No Change' in the encrypted password case.
  894. if( !IsEncryptedAdminPasswordPresent() ){
  895. if((rec->Type == UAT_STRING) && rec->Answer.String &&
  896. !lstrcmpi(rec->Answer.String, L"NoChange")) {
  897. DontChangeAdminPassword = TRUE;
  898. rec->Answer.String[0] = (WCHAR)'\0';
  899. }
  900. }
  901. return(TRUE);
  902. }
  903. BOOL
  904. CheckMode(
  905. struct _UNATTENDANSWER *rec
  906. )
  907. /*+
  908. Routine Description:
  909. Callback to check that the string used for the setup type is valid
  910. Arguments:
  911. Returns:
  912. TRUE - Answer is valid
  913. FALSE - Answer is invalid
  914. --*/
  915. {
  916. MYASSERT(rec);
  917. //
  918. // Check to make sure that we have a string
  919. //
  920. if(rec->Type != UAT_STRING) {
  921. return(FALSE);
  922. }
  923. //
  924. // Check to see if the string is the custom or express one
  925. //
  926. if(lstrcmpi(rec->Answer.String,WINNT_A_CUSTOM)
  927. && lstrcmpi(rec->Answer.String,WINNT_A_EXPRESS)) {
  928. //
  929. // Free the old string and allocate a new one
  930. //
  931. MyFree(rec->Answer.String);
  932. rec->Answer.String = pSetupDuplicateString(WINNT_A_EXPRESS);
  933. rec->ParseErrors = TRUE;
  934. }
  935. return(TRUE);
  936. }
  937. BOOL
  938. SetPid(
  939. HWND hwnd,
  940. DWORD contextinfo,
  941. struct _UNATTENDITEM *item
  942. )
  943. /*++
  944. Routine Description:
  945. Callback for both the OEM and CD dialog boxes that split the
  946. product string into the proper location boxes.
  947. Arguments:
  948. Returns:
  949. TRUE - success
  950. FALSE - failure
  951. --*/
  952. {
  953. WCHAR *ptr;
  954. UINT length;
  955. WCHAR Buf[MAX_BUF];
  956. WCHAR szPid[MAX_BUF];
  957. MYASSERT(item);
  958. MYASSERT(item->Item);
  959. //
  960. // Check to see if we found the pid and make sure that we have a string
  961. //
  962. if(!item->Item->Present || (item->Item->Type != UAT_STRING)) {
  963. return(FALSE);
  964. }
  965. //
  966. // oem and cd installs are both the same case for pid3.0
  967. //
  968. lstrcpyn(szPid, item->Item->Answer.String, MAX_BUF);
  969. szPid[MAX_BUF - 1] = L'\0';
  970. if ( ( lstrlen( szPid ) != (4 + MAX_PID30_EDIT*5) ) ||
  971. ( szPid[5] != (WCHAR)L'-' ) ||
  972. ( szPid[11] != (WCHAR)L'-' ) ||
  973. ( szPid[17] != (WCHAR)L'-' ) ||
  974. ( szPid[23] != (WCHAR)L'-' )
  975. ) {
  976. MyFree(item->Item->Answer.String);
  977. item->Item->Present = FALSE;
  978. return(FALSE);
  979. }
  980. if (item->Reserved1 > 5) {
  981. MyFree(item->Item->Answer.String);
  982. item->Item->Present = FALSE;
  983. return(FALSE);
  984. }
  985. ptr = &szPid[item->Reserved1*(MAX_PID30_EDIT+1)];
  986. lstrcpyn(Pid30Text[item->Reserved1], ptr, MAX_PID30_EDIT+1 );
  987. Pid30Text[item->Reserved1][MAX_PID30_EDIT] = (WCHAR)L'\0';
  988. //
  989. // Copy the string to a buffer, set the dialog text and return success.
  990. //
  991. lstrcpyn(Buf,ptr,MAX_PID30_EDIT+1);
  992. SetDlgItemText(hwnd,item->ControlId,Buf);
  993. return(TRUE);
  994. }
  995. BOOL
  996. SetSetupMode(
  997. HWND hwnd,
  998. DWORD contextinfo,
  999. struct _UNATTENDITEM *item
  1000. )
  1001. {
  1002. MYASSERT(item);
  1003. MYASSERT(item->Item);
  1004. //
  1005. // Make sure that we have a string
  1006. //
  1007. if(item->Item->Type != UAT_STRING) {
  1008. return(FALSE);
  1009. }
  1010. //
  1011. // Did we get a parse error? if so display something that the user can
  1012. // see so that the problem gets corrected in the future
  1013. //
  1014. if(item->Item->ParseErrors) {
  1015. PostMessage(hwnd,WM_IAMVISIBLE,0,0);
  1016. }
  1017. SetupMode = lstrcmpi(item->Item->Answer.String,WINNT_A_CUSTOM)
  1018. ? SETUPMODE_TYPICAL
  1019. : SETUPMODE_CUSTOM;
  1020. return(!item->Item->ParseErrors);
  1021. }
  1022. #ifdef _X86_
  1023. BOOL
  1024. SetPentium(
  1025. HWND hwnd,
  1026. DWORD contextinfo,
  1027. struct _UNATTENDITEM *item
  1028. )
  1029. {
  1030. //
  1031. // Do nothing. The dialog procedure takes care of all the logic.
  1032. // See i386\fpu.c.
  1033. //
  1034. UNREFERENCED_PARAMETER(hwnd);
  1035. UNREFERENCED_PARAMETER(contextinfo);
  1036. UNREFERENCED_PARAMETER(item);
  1037. return(TRUE);
  1038. }
  1039. #endif
  1040. BOOL
  1041. SetStepsPage(
  1042. HWND hwnd,
  1043. DWORD contextinfo,
  1044. struct _UNATTENDITEM *item
  1045. )
  1046. {
  1047. return(TRUE);
  1048. }
  1049. BOOL
  1050. SetLastPage(
  1051. HWND hwnd,
  1052. DWORD contextinfo,
  1053. struct _UNATTENDITEM *item
  1054. )
  1055. {
  1056. return(TRUE);
  1057. }