Leaked source code of windows server 2003
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.

1370 lines
36 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. { IDD_WELCOMEBUTTONS, FALSE, FALSE, FALSE, 1, ItemSetup },
  279. { IDD_REGIONAL_SETTINGS, FALSE, FALSE, FALSE, 0, NULL },
  280. { IDD_NAMEORG, FALSE, FALSE, FALSE, 2, ItemNameOrg },
  281. { IDD_PID_CD, FALSE, FALSE, FALSE, 5, ItemPidCd },
  282. { IDD_PID_OEM, FALSE, FALSE, FALSE, 5, ItemPidOem },
  283. { IDD_COMPUTERNAME, FALSE, FALSE, FALSE, 3, ItemCompName },
  284. #ifdef DOLOCALUSER
  285. { IDD_USERACCOUNT, FALSE, FALSE, FALSE, 0, NULL },
  286. #endif
  287. #ifdef _X86_
  288. { IDD_PENTIUM, FALSE, FALSE, FALSE, 1, ItemPentium },
  289. #endif
  290. { IDD_OPTIONS, FALSE, FALSE, FALSE, 0, NULL },
  291. { IDD_STEPS1, FALSE, FALSE, TRUE, 1, ItemStepsPage },
  292. { IDD_LAST_WIZARD_PAGE, FALSE, FALSE, TRUE, 1, ItemLastPage }
  293. };
  294. UNATTENDWIZARD UnattendWizard = {
  295. FALSE, FALSE, TRUE,
  296. sizeof(UnattendPageTable)/sizeof(UnattendPageTable[0]),
  297. UnattendPageTable,
  298. sizeof(UnattendAnswerTable)/sizeof(UnattendAnswerTable[0]),
  299. UnattendAnswerTable
  300. };
  301. //
  302. // Global Pointer to the Answer file
  303. //
  304. WCHAR AnswerFile[MAX_PATH] = TEXT("");
  305. BOOL
  306. GetAnswerFileSetting (
  307. IN PCWSTR Section,
  308. IN PCWSTR Key,
  309. OUT PWSTR Buffer,
  310. IN UINT BufferSize
  311. )
  312. /*++
  313. Routine Description:
  314. GetAnswerFileSetting uses the private profile APIs to obtain an answer file
  315. string from %systemroot%\system32\$winnt$.inf. It also performs %% removal,
  316. since $winnt$.inf is an INF, not an INI.
  317. Arguments:
  318. Section - Specifies the section to retreive the value from (such as
  319. GuiUnattended)
  320. Key - Specifies the key within the section (such as TimeZone)
  321. Buffer - Receives the value
  322. BufferSize - Specifies the size, in WCHARs, of Buffer.
  323. Return Value:
  324. TRUE if the setting was retrived, FALSE otherwise.
  325. --*/
  326. {
  327. PCWSTR src;
  328. PWSTR dest;
  329. WCHAR testBuf[3];
  330. MYASSERT (BufferSize > 2);
  331. if (!AnswerFile[0]) {
  332. GetSystemDirectory (AnswerFile, MAX_PATH);
  333. pSetupConcatenatePaths (AnswerFile, WINNT_GUI_FILE, MAX_PATH, NULL);
  334. SetEnvironmentVariable (L"UnattendFile", AnswerFile);
  335. }
  336. if (!GetPrivateProfileString (
  337. Section,
  338. Key,
  339. L"",
  340. Buffer,
  341. BufferSize,
  342. AnswerFile
  343. )) {
  344. //
  345. // String not present or is empty -- try again with a different
  346. // default. If the string is empty, we'll get back 0. If the key does
  347. // not exist, we'll get back 1.
  348. //
  349. MYASSERT (BufferSize == 0 || *Buffer == 0);
  350. return 0 == GetPrivateProfileString (
  351. Section,
  352. Key,
  353. L"X",
  354. testBuf,
  355. 3,
  356. AnswerFile
  357. );
  358. }
  359. //
  360. // We obtained the string. Now remove pairs of %.
  361. //
  362. if (BufferSize) {
  363. src = Buffer;
  364. dest = Buffer;
  365. while (*src) {
  366. if (src[0] == L'%' && src[1] == L'%') {
  367. src++;
  368. }
  369. *dest++ = *src++;
  370. }
  371. *dest = 0;
  372. }
  373. return TRUE;
  374. }
  375. BOOL
  376. UnattendFindAnswer(
  377. IN OUT PUNATTENDANSWER ans
  378. )
  379. /*++
  380. Routine Description:
  381. Fills in the response from the unattend file to the key 'id' into
  382. the structure pointed to by 'ans'. If a non-null 'def' is specified
  383. and no answer exists in the file, 'def' is parsed as the answer.
  384. Arguments:
  385. ans - pointer to the structure information for the answer
  386. Return Value:
  387. TRUE - 'ans' structure has been filled in with an answer
  388. FALSE - otherwise
  389. --*/
  390. {
  391. WCHAR Buf[MAX_BUF];
  392. MYASSERT(AnswerFile[0]);
  393. if (!GetAnswerFileSetting (ans->Section, ans->Key, Buf, MAX_BUF)) {
  394. //
  395. // Setting does not exist. If there is a default, use it.
  396. //
  397. if (ans->DefaultAnswer) {
  398. lstrcpyn (Buf, ans->DefaultAnswer, MAX_BUF);
  399. } else {
  400. ans->Present = FALSE;
  401. return (!ans->Required);
  402. }
  403. }
  404. //
  405. // Assume empty string means the string does not exist. This is how the
  406. // original implementation worked.
  407. //
  408. if (*Buf == 0) {
  409. ans->Present = FALSE;
  410. return !ans->Required;
  411. }
  412. //
  413. // Found a value, or using the default
  414. //
  415. ans->Present = TRUE;
  416. //
  417. // Copy the data into the answer structure. This requires
  418. // switching on the type of data expected and converting it to
  419. // the required format. In the case of strings, it also means
  420. // allocating a pool of memory for the result
  421. //
  422. switch(ans->Type) {
  423. case UAT_STRING:
  424. //
  425. // We allocate some memory, so we must free it later
  426. //
  427. ans->Answer.String = pSetupDuplicateString(Buf);
  428. if(!ans->Answer.String) {
  429. pSetupOutOfMemory(GetActiveWindow());
  430. return(FALSE);
  431. }
  432. break;
  433. case UAT_LONGINT:
  434. //
  435. // Simply convert the number from string to long
  436. //
  437. ans->Answer.Num = _wtol(Buf);
  438. break;
  439. case UAT_BOOLEAN:
  440. //
  441. // check to see if the answer is yes
  442. //
  443. ans->Answer.Bool = ((Buf[0] == L'y') || (Buf[0] == L'Y'));
  444. break;
  445. default:
  446. break;
  447. }
  448. //
  449. // Execute any callbacks if present
  450. //
  451. if(ans->pfnCheckValid) {
  452. if(!ans->pfnCheckValid(ans)) {
  453. ans->Present = FALSE;
  454. ans->ParseErrors = TRUE;
  455. return(!ans->Required);
  456. }
  457. }
  458. //
  459. // Success.
  460. //
  461. return(TRUE);
  462. }
  463. VOID
  464. UnattendInitialize(
  465. VOID
  466. )
  467. /*++
  468. Routine Description:
  469. Initialize unattended mode support by loading all answers
  470. from the unattend file.
  471. Arguments:
  472. None.
  473. Return Value:
  474. None.
  475. --*/
  476. {
  477. WCHAR p[MAX_BUF];
  478. DWORD Result;
  479. BOOL Success = TRUE;
  480. UINT i;
  481. //
  482. // If we haven't calculated the path to $winnt$.sif yet, do so now
  483. //
  484. if(!AnswerFile[0]) {
  485. GetSystemDirectory(AnswerFile,MAX_PATH);
  486. pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
  487. SetEnvironmentVariable( L"UnattendFile", AnswerFile );
  488. }
  489. if( MiniSetup ) {
  490. WCHAR MyAnswerFile[MAX_PATH];
  491. //
  492. // First, see if there's a sysprep.inf on the a:\ drive. If so, use it.
  493. //
  494. lstrcpy( MyAnswerFile, TEXT("a:\\sysprep.inf") );
  495. if( !FileExists( MyAnswerFile, NULL ) ) {
  496. //
  497. // Nope. Check for a \sysprep\sysprep.inf.
  498. //
  499. Result = GetWindowsDirectory( MyAnswerFile, MAX_PATH );
  500. if( Result == 0) {
  501. MYASSERT(FALSE);
  502. return;
  503. }
  504. MyAnswerFile[3] = 0;
  505. pSetupConcatenatePaths( MyAnswerFile, TEXT("sysprep\\sysprep.inf"), MAX_PATH, NULL );
  506. }
  507. //
  508. // We've assumed that we're running unattended, but
  509. // network setup hates it when we pretend to be unattended, but
  510. // don't provide an answer file. So if there is no answer file,
  511. // quit the facade.
  512. //
  513. Unattended = FileExists(MyAnswerFile, NULL);
  514. Preinstall = Unattended;
  515. //
  516. // Now either replace or delete the original unattend file.
  517. // We do this so that we don't erroneously pickup unattend
  518. // entries out of the old answer file. However, if OOBE is
  519. // running, we still need the old answerfile.
  520. //
  521. if( Unattended ) {
  522. CopyFile( MyAnswerFile, AnswerFile, FALSE );
  523. } else if ( !OobeSetup ) {
  524. DeleteFile( AnswerFile );
  525. }
  526. }
  527. //
  528. // We need to make the product id an alias for the product key.
  529. //
  530. if ( GetPrivateProfileString(
  531. pwUserData, pwProdId, pwNull, p, MAX_BUF, AnswerFile)
  532. ) {
  533. if ( !WritePrivateProfileString(
  534. pwUserData, pwProductKey, p, AnswerFile ) ) {
  535. SetupDebugPrint( L"SETUP: WritePrivateProfileString failed to write the product key in UnattendInitialize()." );
  536. }
  537. }
  538. //
  539. // Now get all the answers.
  540. //
  541. MYASSERT(!UnattendWizard.Initialized);
  542. UnattendWizard.Initialized = TRUE;
  543. for(i=0; i<UnattendWizard.AnswerCount; i++) {
  544. //
  545. // Check to make sure that the table order hasn't changed
  546. // and load the appropriate answer
  547. //
  548. MYASSERT((UINT)UnattendWizard.Answer[i].AnswerId == i);
  549. Success &= UnattendFindAnswer(&UnattendWizard.Answer[i]);
  550. }
  551. UnattendWizard.ShowWizard = !Success;
  552. }
  553. LRESULT
  554. SendDlgMessage (
  555. HWND hdlg,
  556. UINT Message,
  557. WPARAM wParam,
  558. LPARAM lParam
  559. )
  560. {
  561. LRESULT OldResult;
  562. LRESULT Result;
  563. OldResult = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
  564. SendMessage (hdlg, Message, wParam, lParam);
  565. Result = GetWindowLongPtr (hdlg, DWLP_MSGRESULT);
  566. SetWindowLongPtr (hdlg, DWLP_MSGRESULT, OldResult);
  567. return Result;
  568. }
  569. BOOL
  570. ReturnDlgResult (
  571. HWND hdlg,
  572. LRESULT Result
  573. )
  574. {
  575. SetWindowLongPtr (hdlg, DWLP_MSGRESULT, Result);
  576. return TRUE;
  577. }
  578. VOID
  579. UnattendAdvanceIfValid (
  580. IN HWND hwnd
  581. )
  582. {
  583. LRESULT ValidationState;
  584. //
  585. // Validate wizard page data with UI
  586. //
  587. ValidationState = SendDlgMessage (hwnd, WMX_VALIDATE, 0, TRUE);
  588. if (ValidationState == VALIDATE_DATA_INVALID) {
  589. SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_DISALLOWED);
  590. } else {
  591. SetWindowLongPtr (hwnd, DWLP_MSGRESULT, WIZARD_NEXT_OK);
  592. }
  593. }
  594. BOOL
  595. UnattendSetActiveDlg(
  596. IN HWND hwnd,
  597. IN DWORD controlid
  598. )
  599. /*++
  600. Routine Description:
  601. Initialize unattended mode support by loading all answers
  602. from the unattend file.
  603. Arguments:
  604. None.
  605. Return Value:
  606. TRUE - Page will become active
  607. FALSE - Page will not become active
  608. --*/
  609. {
  610. PUNATTENDPAGE pPage;
  611. PUNATTENDITEM pItem;
  612. BOOL success;
  613. UINT i,j;
  614. MYASSERT(UnattendWizard.Initialized);
  615. for(i=0; i<UnattendWizard.PageCount; i++) {
  616. if(controlid == UnattendWizard.Page[i].PageId) {
  617. //
  618. // Found Matching Page entry
  619. // Check to see if we have already loaded the page
  620. //
  621. pPage = & (UnattendWizard.Page[i]);
  622. if(!pPage->LoadPage) {
  623. //
  624. // Set the flags that load and display the page and
  625. // the flag that controls wether or not to stop on this page
  626. //
  627. pPage->LoadPage = TRUE;
  628. pPage->ShowPage = (UnattendMode == UAM_PROVIDEDEFAULT);
  629. for(j=0;j<pPage->ItemCount;j++) {
  630. pItem = &(pPage->Item[j]);
  631. if(pItem->pfnSetActive) {
  632. //
  633. // If the item has a call back function then
  634. // execute that function, otherwise try to load
  635. // the answer into the appropriate message box
  636. //
  637. success = pItem->pfnSetActive(hwnd,0,pItem);
  638. pPage->ShowPage |= !success;
  639. } else if (!pItem->Item->Present) {
  640. //
  641. // The answer for this item is missing.
  642. //
  643. pPage->ShowPage |= pItem->Item->Required;
  644. } else {
  645. //
  646. // Switch to set the text of the item on the screen
  647. //
  648. switch(pItem->Item->Type) {
  649. case UAT_STRING:
  650. SetDlgItemText(hwnd,pItem->ControlId,pItem->Item->Answer.String);
  651. break;
  652. case UAT_LONGINT:
  653. case UAT_BOOLEAN:
  654. case UAT_NONE:
  655. default:
  656. break;
  657. }
  658. if( UnattendMode == UAM_PROVIDEDEFAULT ||
  659. UnattendMode == UAM_DEFAULTHIDE) {
  660. EnableWindow(GetDlgItem(hwnd,pItem->ControlId), TRUE);
  661. } else {
  662. EnableWindow(GetDlgItem(hwnd,pItem->ControlId),FALSE);
  663. }
  664. } // if (pItem
  665. } // for(j
  666. //
  667. // Allow the page to become activated
  668. //
  669. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  670. if(!pPage->ShowPage) {
  671. //
  672. // Perform validation, skip activation if validation succeeds.
  673. //
  674. if (SendDlgMessage (hwnd, WMX_VALIDATE, 0, 0) == 1) {
  675. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
  676. return FALSE;
  677. }
  678. //
  679. // Simulate the pressing of the next button, which causes the
  680. // wizard page proc to evaluate the data in its controls, and
  681. // throw up popups to the user.
  682. //
  683. PostMessage(hwnd,WM_SIMULATENEXT,0,0);
  684. } else if (!pPage->NeverSkip) {
  685. //
  686. // Pages which are marked as NeverSkip should not
  687. // cause the unattended status to be considered
  688. // unsuccessful.
  689. //
  690. // We can't skip this page so mark the init as
  691. // unsuccessful. If this is the first error in a fully
  692. // unattended setup, notify the user.
  693. //
  694. if(UnattendMode == UAM_FULLUNATTENDED) {
  695. SetuplogError(
  696. LogSevError,
  697. SETUPLOG_USE_MESSAGEID,
  698. MSG_LOG_BAD_UNATTEND_PARAM,
  699. pItem->Item->Key,
  700. pItem->Item->Section,
  701. NULL,NULL);
  702. if(UnattendWizard.Successful) {
  703. MessageBoxFromMessage(
  704. MainWindowHandle,
  705. MSG_FULLUNATTENDED_ERROR,
  706. NULL,
  707. IDS_ERROR,
  708. MB_ICONERROR | MB_OK | MB_SYSTEMMODAL
  709. );
  710. }
  711. }
  712. UnattendWizard.Successful = FALSE;
  713. }
  714. return(TRUE);
  715. } else {
  716. //
  717. // The Page has already been loaded, so we don't do that again
  718. // If we are ShowPage is FALSE, then we don't show the page to
  719. // the user, otherwise we do.
  720. //
  721. if(!pPage->ShowPage && !pPage->NeverSkip) {
  722. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,-1);
  723. } else {
  724. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  725. }
  726. return(pPage->ShowPage);
  727. }
  728. }
  729. }
  730. //
  731. // We didn't find a matching id, stop at the page that called us.
  732. //
  733. SetWindowLongPtr(hwnd,DWLP_MSGRESULT,0);
  734. return(TRUE);
  735. }
  736. BOOL
  737. UnattendErrorDlg(
  738. IN HWND hwnd,
  739. IN DWORD controlid
  740. )
  741. /*++
  742. Routine Description:
  743. Called when an error occurs in a DLG. Enables all windows
  744. in the dialog and turns off the successful flag for the
  745. unattend wizard
  746. Arguments:
  747. Return Value:
  748. Boolean value indicating outcome.
  749. --*/
  750. {
  751. PUNATTENDPAGE pPage;
  752. PUNATTENDITEM pItem;
  753. BOOL success;
  754. BOOL stop;
  755. UINT i,j;
  756. MYASSERT(UnattendWizard.Initialized);
  757. for(i=0; i<UnattendWizard.PageCount; i++) {
  758. if(controlid == UnattendWizard.Page[i].PageId) {
  759. //
  760. // Found Matching Page entry
  761. //
  762. pPage = &UnattendWizard.Page[i];
  763. if(!pPage->LoadPage) {
  764. //
  765. // The Page hasn't been loaded, so it isn't correct
  766. //
  767. continue;
  768. }
  769. //
  770. // Always display the page from now on
  771. //
  772. pPage->ShowPage = TRUE;
  773. //
  774. // Enable all the items
  775. //
  776. for (j=0;j<pPage->ItemCount;j++) {
  777. pItem = &(pPage->Item[j]);
  778. if(pItem->pfnSetActive) {
  779. //
  780. // if this is present then we assume that the
  781. // callback handled itself properly already
  782. //
  783. continue;
  784. }
  785. EnableWindow( GetDlgItem(hwnd,pItem->ControlId), TRUE);
  786. }
  787. }
  788. }
  789. UnattendWizard.Successful = FALSE;
  790. return(TRUE);
  791. }
  792. PWSTR
  793. UnattendFetchString(
  794. IN UNATTENDENTRIES entry
  795. )
  796. /*++
  797. Routine Description:
  798. Finds the string which corresponds to 'entry' in the answer
  799. table and returns a pointer to a copy of that string
  800. Arguments:
  801. entry - which answer do you want?
  802. Return Value:
  803. NULL - if any errors occur
  804. string - if a normal string
  805. Note: if the answer is an int or a bool or some other type,
  806. the behavior of this function is undefined (for now it will
  807. return NULL -- in the future it might make sense to turn these
  808. into strings...)
  809. --*/
  810. {
  811. MYASSERT(UnattendWizard.Initialized);
  812. //
  813. // Sanity check to make sure that the order of the answers is
  814. // what we expect.
  815. //
  816. MYASSERT(UnattendWizard.Answer[entry].AnswerId == entry);
  817. if(!UnattendWizard.Answer[entry].Present
  818. || (UnattendWizard.Answer[entry].Type != UAT_STRING)) {
  819. //
  820. // There is no string to return
  821. //
  822. return NULL;
  823. }
  824. return(pSetupDuplicateString(UnattendWizard.Answer[entry].Answer.String));
  825. }
  826. BOOL
  827. CheckServer(
  828. struct _UNATTENDANSWER *rec
  829. )
  830. /*++
  831. Routine Description:
  832. Callback to check that the string used for the server type is valid
  833. Arguments:
  834. Return Value:
  835. TRUE - Answer is valid
  836. FALSE - Answer is invalid
  837. --*/
  838. {
  839. MYASSERT(rec);
  840. //
  841. // Check to make sure that we have a string
  842. //
  843. if(rec->Type != UAT_STRING) {
  844. return(FALSE);
  845. }
  846. //
  847. // Check to see if we have one of the valid strings
  848. //
  849. if(lstrcmpi(rec->Answer.String,WINNT_A_LANMANNT)
  850. && lstrcmpi(rec->Answer.String,WINNT_A_SERVERNT)) {
  851. //
  852. // We don't have a valid string, so we can clean up the answer
  853. //
  854. MyFree(rec->Answer.String);
  855. rec->Present = FALSE;
  856. rec->ParseErrors = TRUE;
  857. return(FALSE);
  858. }
  859. return(TRUE);
  860. }
  861. BOOL
  862. CheckComputerName(
  863. struct _UNATTENDANSWER *rec
  864. )
  865. /*+
  866. Routine Description:
  867. Uppercase the computer name that comes out of the unattended file.
  868. Arguments:
  869. Returns:
  870. Always TRUE.
  871. --*/
  872. {
  873. if((rec->Type == UAT_STRING) && rec->Answer.String) {
  874. CharUpper(rec->Answer.String);
  875. }
  876. return(TRUE);
  877. }
  878. BOOL
  879. CheckAdminPassword(
  880. struct _UNATTENDANSWER *rec
  881. )
  882. /*+
  883. Routine Description:
  884. Check for the "NoChange" keyword.
  885. Arguments:
  886. Returns:
  887. Always TRUE.
  888. --*/
  889. {
  890. //Ignore the check for 'No Change' in the encrypted password case.
  891. if( !IsEncryptedAdminPasswordPresent() ){
  892. if((rec->Type == UAT_STRING) && rec->Answer.String &&
  893. !lstrcmpi(rec->Answer.String, L"NoChange")) {
  894. DontChangeAdminPassword = TRUE;
  895. rec->Answer.String[0] = (WCHAR)'\0';
  896. }
  897. }
  898. return(TRUE);
  899. }
  900. BOOL
  901. CheckMode(
  902. struct _UNATTENDANSWER *rec
  903. )
  904. /*+
  905. Routine Description:
  906. Callback to check that the string used for the setup type is valid
  907. Arguments:
  908. Returns:
  909. TRUE - Answer is valid
  910. FALSE - Answer is invalid
  911. --*/
  912. {
  913. MYASSERT(rec);
  914. //
  915. // Check to make sure that we have a string
  916. //
  917. if(rec->Type != UAT_STRING) {
  918. return(FALSE);
  919. }
  920. //
  921. // Check to see if the string is the custom or express one
  922. //
  923. if(lstrcmpi(rec->Answer.String,WINNT_A_CUSTOM)
  924. && lstrcmpi(rec->Answer.String,WINNT_A_EXPRESS)) {
  925. //
  926. // Free the old string and allocate a new one
  927. //
  928. MyFree(rec->Answer.String);
  929. rec->Answer.String = pSetupDuplicateString(WINNT_A_EXPRESS);
  930. rec->ParseErrors = TRUE;
  931. }
  932. return(TRUE);
  933. }
  934. BOOL
  935. SetPid(
  936. HWND hwnd,
  937. DWORD contextinfo,
  938. struct _UNATTENDITEM *item
  939. )
  940. /*++
  941. Routine Description:
  942. Callback for both the OEM and CD dialog boxes that split the
  943. product string into the proper location boxes.
  944. Arguments:
  945. Returns:
  946. TRUE - success
  947. FALSE - failure
  948. --*/
  949. {
  950. WCHAR *ptr;
  951. UINT length;
  952. WCHAR Buf[MAX_BUF];
  953. WCHAR szPid[MAX_BUF];
  954. MYASSERT(item);
  955. MYASSERT(item->Item);
  956. //
  957. // Check to see if we found the pid and make sure that we have a string
  958. //
  959. if(!item->Item->Present || (item->Item->Type != UAT_STRING)) {
  960. return(FALSE);
  961. }
  962. //
  963. // oem and cd installs are both the same case for pid3.0
  964. //
  965. lstrcpyn(szPid, item->Item->Answer.String, MAX_BUF);
  966. szPid[MAX_BUF - 1] = L'\0';
  967. if ( ( lstrlen( szPid ) != (4 + MAX_PID30_EDIT*5) ) ||
  968. ( szPid[5] != (WCHAR)L'-' ) ||
  969. ( szPid[11] != (WCHAR)L'-' ) ||
  970. ( szPid[17] != (WCHAR)L'-' ) ||
  971. ( szPid[23] != (WCHAR)L'-' )
  972. ) {
  973. MyFree(item->Item->Answer.String);
  974. item->Item->Present = FALSE;
  975. return(FALSE);
  976. }
  977. if (item->Reserved1 > 5) {
  978. MyFree(item->Item->Answer.String);
  979. item->Item->Present = FALSE;
  980. return(FALSE);
  981. }
  982. ptr = &szPid[item->Reserved1*(MAX_PID30_EDIT+1)];
  983. lstrcpyn(Pid30Text[item->Reserved1], ptr, MAX_PID30_EDIT+1 );
  984. Pid30Text[item->Reserved1][MAX_PID30_EDIT] = (WCHAR)L'\0';
  985. //
  986. // Copy the string to a buffer, set the dialog text and return success.
  987. //
  988. lstrcpyn(Buf,ptr,MAX_PID30_EDIT+1);
  989. SetDlgItemText(hwnd,item->ControlId,Buf);
  990. return(TRUE);
  991. }
  992. BOOL
  993. SetSetupMode(
  994. HWND hwnd,
  995. DWORD contextinfo,
  996. struct _UNATTENDITEM *item
  997. )
  998. {
  999. MYASSERT(item);
  1000. MYASSERT(item->Item);
  1001. //
  1002. // Make sure that we have a string
  1003. //
  1004. if(item->Item->Type != UAT_STRING) {
  1005. return(FALSE);
  1006. }
  1007. //
  1008. // Did we get a parse error? if so display something that the user can
  1009. // see so that the problem gets corrected in the future
  1010. //
  1011. if(item->Item->ParseErrors) {
  1012. PostMessage(hwnd,WM_IAMVISIBLE,0,0);
  1013. }
  1014. SetupMode = lstrcmpi(item->Item->Answer.String,WINNT_A_CUSTOM)
  1015. ? SETUPMODE_TYPICAL
  1016. : SETUPMODE_CUSTOM;
  1017. return(!item->Item->ParseErrors);
  1018. }
  1019. #ifdef _X86_
  1020. BOOL
  1021. SetPentium(
  1022. HWND hwnd,
  1023. DWORD contextinfo,
  1024. struct _UNATTENDITEM *item
  1025. )
  1026. {
  1027. //
  1028. // Do nothing. The dialog procedure takes care of all the logic.
  1029. // See i386\fpu.c.
  1030. //
  1031. UNREFERENCED_PARAMETER(hwnd);
  1032. UNREFERENCED_PARAMETER(contextinfo);
  1033. UNREFERENCED_PARAMETER(item);
  1034. return(TRUE);
  1035. }
  1036. #endif
  1037. BOOL
  1038. SetStepsPage(
  1039. HWND hwnd,
  1040. DWORD contextinfo,
  1041. struct _UNATTENDITEM *item
  1042. )
  1043. {
  1044. return(TRUE);
  1045. }
  1046. BOOL
  1047. SetLastPage(
  1048. HWND hwnd,
  1049. DWORD contextinfo,
  1050. struct _UNATTENDITEM *item
  1051. )
  1052. {
  1053. return(TRUE);
  1054. }