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.

3484 lines
99 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <windows.h>
  5. #include <stdio.h>
  6. #include <conio.h>
  7. #include <stdlib.h>
  8. #include <ntddsac.h>
  9. #include <emsapi.h>
  10. #include <ASSERT.h>
  11. #include <initguid.h>
  12. #include <spidgen.h>
  13. #include <compliance.h>
  14. #include <winnt32.h>
  15. #include <syssetup.h>
  16. #include <setupbat.h>
  17. #include "resource.h"
  18. #include "ems.h"
  19. //
  20. // winnt32.h wants to define this to MyWritePrivateProfileString.
  21. // Undo that.
  22. //
  23. #ifdef WritePrivateProfileStringW
  24. #undef WritePrivateProfileStringW
  25. #endif
  26. //
  27. // status globals for keeping track of what the user wanted and entered
  28. //
  29. BOOL gMiniSetup = FALSE;
  30. BOOL gRejectedEula = FALSE;
  31. //
  32. // EMS channel globals
  33. //
  34. SAC_CHANNEL_OPEN_ATTRIBUTES GlobalChannelAttributes;
  35. EMSVTUTF8Channel *gEMSChannel = NULL;
  36. BOOL
  37. IsHeadlessPresent(
  38. OUT EMSVTUTF8Channel **Channel
  39. )
  40. /*++
  41. Routine Description:
  42. Determine if EMS is present by attempting to create the Unattend channel
  43. note: this must be called after InitializeGlobalChannelAttributes
  44. Arguments:
  45. Channel - on success, contains a pointer to a channel object
  46. Return Value:
  47. TRUE - headless is active and we have a channel
  48. FALSE - otherwise
  49. --*/
  50. {
  51. BOOL RetVal;
  52. *Channel = EMSVTUTF8Channel::Construct(GlobalChannelAttributes);
  53. RetVal = (*Channel != NULL);
  54. return(RetVal);
  55. }
  56. BOOL
  57. InitializeGlobalChannelAttributes(
  58. PSAC_CHANNEL_OPEN_ATTRIBUTES ChannelAttributes
  59. )
  60. /*++
  61. Routine Description:
  62. populate the EMS channel attributes
  63. note: this must be called before IsHeadlessPresent
  64. Arguments:
  65. ChannelAttributes - on success, contains a pointer to initialized channel attrs.
  66. Return Value:
  67. TRUE - headless is active and we have a channel
  68. FALSE - otherwise
  69. --*/
  70. {
  71. UNICODE_STRING Name,Description;
  72. BOOL RetVal = FALSE;
  73. RtlZeroMemory(ChannelAttributes,sizeof(SAC_CHANNEL_OPEN_ATTRIBUTES));
  74. if (!LoadStringResource( &Name, IDS_CHANNEL_NAME )) {
  75. goto e0;
  76. }
  77. if (!LoadStringResource( &Description, IDS_CHANNEL_DESCRIPTION)) {
  78. goto e1;
  79. }
  80. ChannelAttributes->Type = ChannelTypeVTUTF8;
  81. wcsncpy(ChannelAttributes->Name, Name.Buffer, SAC_MAX_CHANNEL_NAME_LENGTH);
  82. wcsncpy(ChannelAttributes->Description, Description.Buffer, SAC_MAX_CHANNEL_DESCRIPTION_LENGTH);
  83. ChannelAttributes->Flags = SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT;
  84. ChannelAttributes->HasNewDataEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  85. ChannelAttributes->ApplicationType = SAC_CHANNEL_GUI_SETUP_PROMPT;
  86. RetVal = ((ChannelAttributes->HasNewDataEvent != NULL)
  87. ? TRUE
  88. : FALSE);
  89. RtlFreeUnicodeString(&Description);
  90. e1:
  91. RtlFreeUnicodeString(&Name);
  92. e0:
  93. return(RetVal);
  94. }
  95. inline
  96. BOOL
  97. IsAsyncCancelSignalled(
  98. HANDLE hEvent
  99. )
  100. /*++
  101. Routine Description:
  102. Test the given event to see if it is signaled
  103. Arguments:
  104. hEvent - event to be tested
  105. Return Value:
  106. TRUE - signaled
  107. FALSE - otherwise
  108. --*/
  109. {
  110. //
  111. // check if the async cacnel signal fired.
  112. //
  113. if( (hEvent) &&
  114. (hEvent != INVALID_HANDLE_VALUE) ) {
  115. return (WaitForSingleObject(hEvent,0) == WAIT_OBJECT_0);
  116. } else {
  117. return (FALSE);
  118. }
  119. }
  120. //
  121. // ====================================
  122. // EMS-specific communication functions
  123. // ====================================
  124. //
  125. BOOL
  126. WaitForUserInputFromEMS(
  127. IN DWORD TimeOut,
  128. OUT BOOL *TimedOut OPTIONAL,
  129. IN HANDLE hCancelEvent OPTIONAL
  130. )
  131. /*++
  132. Routine Description:
  133. Wait for input from the EMS port
  134. Note: this routine does not read any data from the port
  135. Arguments:
  136. TimeOut - timeout parameter for user input
  137. TimedOut- OPTIONAL parameter denoting if we timed out or not
  138. hCancelEvent - if supplied, then we wait on this event too
  139. this lets us not block if timeout is infinite, etc.
  140. Return Value:
  141. returns TRUE for user input or timeout (non-error)
  142. --*/
  143. {
  144. DWORD dwRetVal;
  145. BOOL bSuccess = FALSE;
  146. HANDLE handles[2];
  147. ULONG handleCount = 0;
  148. if (TimedOut) {
  149. *TimedOut = FALSE;
  150. }
  151. handles[0] = GlobalChannelAttributes.HasNewDataEvent;
  152. handleCount++;
  153. if ((hCancelEvent != NULL) &&
  154. (hCancelEvent != INVALID_HANDLE_VALUE)) {
  155. handles[1] = hCancelEvent;
  156. handleCount++;
  157. }
  158. //
  159. // Wait for our event
  160. //
  161. dwRetVal = WaitForMultipleObjects(
  162. handleCount,
  163. handles,
  164. FALSE,
  165. TimeOut
  166. );
  167. switch ( dwRetVal ) {
  168. case WAIT_OBJECT_0: {
  169. //
  170. // EMS port got data
  171. //
  172. bSuccess = TRUE;
  173. break;
  174. }
  175. case (WAIT_OBJECT_0+1): {
  176. //
  177. // if the hCancelEvent occured then we
  178. // need to report an error status, hence
  179. // we return FALSE status if we get here
  180. //
  181. bSuccess = FALSE;
  182. break;
  183. }
  184. case WAIT_TIMEOUT: {
  185. if (TimedOut) {
  186. *TimedOut = TRUE;
  187. }
  188. bSuccess = TRUE;
  189. break;
  190. }
  191. default:
  192. //
  193. // we return FALSE status if we get here
  194. //
  195. break;
  196. }
  197. return bSuccess;
  198. }
  199. BOOL
  200. ReadCharFromEMS(
  201. OUT PWCHAR awc,
  202. IN HANDLE hCancelEvent OPTIONAL
  203. )
  204. /*++
  205. Routine Description:
  206. This routine will read a single char from the EMS Channel
  207. Arguments:
  208. awc - pointer to a wchar
  209. hCancelEvent - if supplied, then we wait on this event too
  210. this lets us not block if timeout is infinite, etc.
  211. Return Value:
  212. status
  213. --*/
  214. {
  215. BOOL bSuccess;
  216. ULONG BytesRead = 0;
  217. //
  218. // wait for input
  219. //
  220. bSuccess = WaitForUserInputFromEMS(
  221. INFINITE,
  222. NULL,
  223. hCancelEvent
  224. );
  225. if (IsAsyncCancelSignalled(hCancelEvent)) {
  226. bSuccess = FALSE;
  227. goto exit;
  228. }
  229. if (bSuccess) {
  230. //
  231. // consume character
  232. //
  233. bSuccess = gEMSChannel->Read(
  234. (PWSTR)awc,
  235. sizeof(WCHAR),
  236. &BytesRead
  237. );
  238. }
  239. exit:
  240. return bSuccess;
  241. }
  242. BOOL
  243. GetStringFromEMS(
  244. OUT PWSTR String,
  245. IN ULONG BufferSize,
  246. IN BOOL GetAllChars,
  247. IN BOOL EchoClearText,
  248. IN HANDLE hCancelEvent OPTIONAL
  249. )
  250. /*++
  251. Routine Description:
  252. This routine will read in a string from the EMS port.
  253. Arguments:
  254. String - on success, contains the credential
  255. BufferSize - the # of BYTES in the String buffer
  256. this should include space for null-termination
  257. GetAllChars - the user must enter StringLength Chars
  258. EchoClearText - TRUE: echo user input in clear text
  259. FALSE: echo user input as '*'
  260. hCancelEvent - if supplied, then we wait on this event too
  261. this lets us not block if timeout is infinite, etc.
  262. Return Value:
  263. TRUE - we got a valid string
  264. FALSE - otherwise
  265. --*/
  266. {
  267. BOOL Done = FALSE;
  268. WCHAR InputBuffer[MY_MAX_STRING_LENGTH+1];
  269. BOOL GotAString = FALSE;
  270. ULONG BytesRead = 0;
  271. BOOL bSuccess;
  272. ULONG CurrentCharacterIndex = 0;
  273. ULONG InputBufferIndex = 0;
  274. ULONG StringLength = (BufferSize / sizeof(WCHAR)) - 1;
  275. if( (String == NULL)) {
  276. return FALSE;
  277. }
  278. bSuccess = TRUE;
  279. //
  280. // Keep asking the user until we get what we want.
  281. //
  282. Done = FALSE;
  283. memset( String,
  284. 0,
  285. BufferSize
  286. );
  287. //
  288. // Start reading input until we get something good.
  289. //
  290. GotAString = FALSE;
  291. CurrentCharacterIndex = 0;
  292. while( !GotAString &&
  293. bSuccess
  294. ) {
  295. //
  296. // wait for input
  297. //
  298. bSuccess = WaitForUserInputFromEMS(
  299. INFINITE,
  300. NULL,
  301. hCancelEvent
  302. );
  303. if (IsAsyncCancelSignalled(hCancelEvent)) {
  304. bSuccess = FALSE;
  305. goto exit;
  306. }
  307. if (bSuccess) {
  308. //
  309. // consume character
  310. //
  311. bSuccess = gEMSChannel->Read(
  312. (PWSTR)InputBuffer,
  313. MY_MAX_STRING_LENGTH * sizeof(WCHAR),
  314. &BytesRead
  315. );
  316. if( (bSuccess) &&
  317. (BytesRead > 0) ) {
  318. ULONG WCharsRead = BytesRead / sizeof(WCHAR);
  319. //
  320. // Append these characters onto the end of our string.
  321. //
  322. InputBufferIndex = 0;
  323. while( (InputBufferIndex < WCharsRead) &&
  324. (CurrentCharacterIndex < StringLength) &&
  325. (!GotAString) &&
  326. bSuccess
  327. ) {
  328. if( (InputBuffer[InputBufferIndex] == 0x0D) ||
  329. (InputBuffer[InputBufferIndex] == 0x0A) ) {
  330. // ignore cr/lf until we get all the chars
  331. if (!GetAllChars) {
  332. GotAString = TRUE;
  333. }
  334. } else {
  335. if( InputBuffer[InputBufferIndex] == '\b' ) {
  336. //
  337. // If the user gave us a backspace, we need to:
  338. // 1. cover up the last character on the screen.
  339. // 2. ignore the previous character he gave us.
  340. //
  341. if( CurrentCharacterIndex > 0 ) {
  342. CurrentCharacterIndex--;
  343. String[CurrentCharacterIndex] = '\0';
  344. gEMSChannel->Write( (PWSTR)L"\b \b",
  345. (ULONG)(wcslen(L"\b \b") * sizeof(WCHAR)) );
  346. }
  347. } else {
  348. //
  349. // Record this character.
  350. //
  351. String[CurrentCharacterIndex] = InputBuffer[InputBufferIndex];
  352. CurrentCharacterIndex++;
  353. //
  354. // Echo 1 character
  355. //
  356. gEMSChannel->Write(
  357. (EchoClearText ? (PWSTR)&InputBuffer[InputBufferIndex] : (PWSTR)L"*"),
  358. sizeof(WCHAR)
  359. );
  360. }
  361. }
  362. //
  363. // Go to the next letter of input.
  364. //
  365. InputBufferIndex++;
  366. }
  367. }
  368. }
  369. if( CurrentCharacterIndex == StringLength ) {
  370. GotAString = TRUE;
  371. }
  372. }
  373. exit:
  374. return bSuccess;
  375. }
  376. VOID
  377. ClearEMSScreen()
  378. /*++
  379. Routine Description:
  380. This routine will clear the EMS channel screen
  381. Arguments:
  382. none
  383. Return Value:
  384. none
  385. --*/
  386. {
  387. gEMSChannel->Write( (PWSTR)VTUTF8_CLEAR_SCREEN,
  388. (ULONG)(wcslen( VTUTF8_CLEAR_SCREEN ) * sizeof(WCHAR)) );
  389. }
  390. #define ESC_CTRL_SEQUENCE_TIMEOUT (2 * 1000)
  391. BOOL
  392. GetDecodedKeyPressFromEMS(
  393. OUT PULONG KeyPress,
  394. IN HANDLE hCancelEvent OPTIONAL
  395. )
  396. /*++
  397. Routine Description:
  398. Read in a (possible) sequence of keystrokes and return a Key value.
  399. Arguments:
  400. KeyPress - on success contains a decoded input value:
  401. lower 16bits contains unicode value
  402. upper 16bits contains <esc> key sequence ids
  403. hCancelEvent - if supplied, then we wait on this event too
  404. this lets us not block if timeout is infinite, etc.
  405. Return Value:
  406. TRUE - we got a valid key press
  407. FALSE - otherwise
  408. --*/
  409. {
  410. BOOL bSuccess = FALSE;
  411. WCHAR wc = 0;
  412. BOOL bTimedOut;
  413. if (!KeyPress) {
  414. return FALSE;
  415. }
  416. *KeyPress = 0;
  417. do {
  418. //
  419. // Read first character
  420. //
  421. bSuccess = ReadCharFromEMS(
  422. &wc,
  423. hCancelEvent
  424. );
  425. if (IsAsyncCancelSignalled(hCancelEvent)) {
  426. bSuccess = FALSE;
  427. goto exit;
  428. }
  429. if (!bSuccess) {
  430. break;
  431. }
  432. //
  433. // Handle all the special escape codes.
  434. //
  435. if (wc == 0x8) { // backspace (^h)
  436. *KeyPress = ASCI_BS;
  437. }
  438. if (wc == 0x7F) { // delete
  439. *KeyPress = KEY_DELETE;
  440. }
  441. if ((wc == '\r') || (wc == '\n')) { // return
  442. *KeyPress = ASCI_CR;
  443. }
  444. if (wc == 0x1b) { // Escape key
  445. bSuccess = WaitForUserInputFromEMS(
  446. ESC_CTRL_SEQUENCE_TIMEOUT,
  447. &bTimedOut,
  448. hCancelEvent
  449. );
  450. if (IsAsyncCancelSignalled(hCancelEvent)) {
  451. bSuccess = FALSE;
  452. goto exit;
  453. }
  454. if (bSuccess) {
  455. if (bTimedOut) {
  456. *KeyPress = ASCI_ESC;
  457. } else {
  458. //
  459. // the user entered something within in the timeout window
  460. // so lets try to figure out if they are sending some
  461. // esc sequence
  462. //
  463. do {
  464. ULONG BytesRead;
  465. //
  466. // consume character
  467. //
  468. bSuccess = gEMSChannel->Read(
  469. &wc,
  470. sizeof(WCHAR),
  471. &BytesRead
  472. );
  473. if (!bSuccess) {
  474. wc = ASCI_ESC;
  475. break;
  476. }
  477. //
  478. // Some terminals send ESC, or ESC-[ to mean
  479. // they're about to send a control sequence. We've already
  480. // gotten an ESC key, so ignore an '[' if it comes in.
  481. //
  482. } while ( wc == L'[' );
  483. switch (wc) {
  484. case '@':
  485. *KeyPress = KEY_F12;
  486. break;
  487. case '!':
  488. *KeyPress = KEY_F11;
  489. break;
  490. case '0':
  491. *KeyPress = KEY_F10;
  492. break;
  493. case '9':
  494. *KeyPress = KEY_F9;
  495. break;
  496. case '8':
  497. *KeyPress = KEY_F8;
  498. break;
  499. case '7':
  500. *KeyPress = KEY_F7;
  501. break;
  502. case '6':
  503. *KeyPress = KEY_F6;
  504. break;
  505. case '5':
  506. *KeyPress = KEY_F5;
  507. break;
  508. case '4':
  509. *KeyPress = KEY_F4;
  510. break;
  511. case '3':
  512. *KeyPress = KEY_F3;
  513. break;
  514. case '2':
  515. *KeyPress = KEY_F2;
  516. break;
  517. case '1':
  518. *KeyPress = KEY_F1;
  519. break;
  520. case '+':
  521. *KeyPress = KEY_INSERT;
  522. break;
  523. case '-':
  524. *KeyPress = KEY_DELETE;
  525. break;
  526. case 'H':
  527. *KeyPress = KEY_HOME;
  528. break;
  529. case 'K':
  530. *KeyPress = KEY_END;
  531. break;
  532. case '?':
  533. *KeyPress = KEY_PAGEUP;
  534. break;
  535. case '/':
  536. *KeyPress = KEY_PAGEDOWN;
  537. break;
  538. case 'A':
  539. *KeyPress = KEY_UP;
  540. break;
  541. case 'B':
  542. *KeyPress = KEY_DOWN;
  543. break;
  544. case 'C':
  545. *KeyPress = KEY_RIGHT;
  546. break;
  547. case 'D':
  548. *KeyPress = KEY_LEFT;
  549. break;
  550. default:
  551. //
  552. // We didn't get anything we recognized after the
  553. // ESC key. Just return the ESC key.
  554. //
  555. *KeyPress = ASCI_ESC;
  556. break;
  557. }
  558. }
  559. }
  560. } // Escape key
  561. } while ( FALSE );
  562. exit:
  563. return bSuccess;
  564. }
  565. //
  566. // ====================================
  567. // PID helper functions
  568. // ====================================
  569. //
  570. typedef enum {
  571. CDRetail,
  572. CDOem,
  573. CDSelect
  574. } CDTYPE;
  575. PCWSTR szPidKeyName = L"SYSTEM\\Setup\\Pid";
  576. PCWSTR szPidValueName = L"Pid";
  577. PCWSTR szFinalPidKeyName = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
  578. PCWSTR szFinalPidValueName = L"ProductId";
  579. TCHAR Pid30Rpc[8] = TEXT("00000");
  580. TCHAR Pid30Site[4] = {0};
  581. DWORD g_dwGroupID = 0;
  582. CDTYPE CDType = CDRetail;
  583. DWORD InstallVar = 0;
  584. //
  585. // Syssetup apparently needs the sku info
  586. // so turn this define if necessary
  587. //
  588. const WCHAR pwLanmanNt[] = WINNT_A_LANMANNT;
  589. const WCHAR pwServerNt[] = WINNT_A_SERVERNT;
  590. const WCHAR pwWinNt[] = WINNT_A_WINNT;
  591. PCWSTR szPidSelectId = L"270";
  592. PCWSTR szPidOemId = L"OEM";
  593. #define MAX_PARAM_LEN (256)
  594. #define PID_30_LENGTH (29)
  595. #define PID_30_SIZE (30)
  596. LONG ProductType = PRODUCT_SERVER_STANDALONE;
  597. WCHAR TmpData[MAX_PATH+1];
  598. //
  599. // sku info
  600. //
  601. PCWSTR szSkuProfessionalFPP = L"B23-00079";
  602. PCWSTR szSkuProfessionalCCP = L"B23-00082";
  603. PCWSTR szSkuProfessionalSelect = L"B23-00305";
  604. PCWSTR szSkuProfessionalEval = L"B23-00084";
  605. PCWSTR szSkuServerFPP = L"C11-00016";
  606. PCWSTR szSkuServerCCP = L"C11-00027";
  607. PCWSTR szSkuServerSelect = L"C11-00222";
  608. PCWSTR szSkuServerEval = L"C11-00026";
  609. PCWSTR szSkuServerNFR = L"C11-00025";
  610. PCWSTR szSkuAdvServerFPP = L"C10-00010";
  611. PCWSTR szSkuAdvServerCCP = L"C10-00015";
  612. PCWSTR szSkuAdvServerSelect = L"C10-00098";
  613. PCWSTR szSkuAdvServerEval = L"C10-00014";
  614. PCWSTR szSkuAdvServerNFR = L"C10-00013";
  615. PCWSTR szSkuDTCFPP = L"C49-00001";
  616. PCWSTR szSkuDTCSelect = L"C49-00023";
  617. PCWSTR szSkuUnknown = L"A22-00001";
  618. PCWSTR szSkuOEM = L"OEM-93523";
  619. PCWSTR GetStockKeepingUnit(
  620. PWCHAR pMPC,
  621. UINT ProductType,
  622. CDTYPE MediaType
  623. )
  624. /*++
  625. Routine Description:
  626. This returns the Stock Keeping Unit based off the MPC.
  627. Arguments:
  628. pMPC - pointer to 5 digit MPC code, null terminated.
  629. ProductType - Product type flag, tells us if this is a workataion or server sku.
  630. CdType - one of InstallType enum
  631. Return Value:
  632. Returns pointer to sku.
  633. If no match found returns szSkuUnknown.
  634. --*/
  635. {
  636. // check for eval
  637. if (!_wcsicmp(pMPC,EVAL_MPC) || !_wcsicmp(pMPC,DOTNET_EVAL_MPC)){
  638. // this is eval media ...
  639. if (ProductType == PRODUCT_WORKSTATION){
  640. return (szSkuProfessionalEval);
  641. } // else
  642. // else it is server or advanced server. I don't think that at this point
  643. // we can easily tell the difference. Since it's been said that having the
  644. // correct sku is not critically important, I shall give them both the sku
  645. // code of server
  646. return (szSkuServerEval);
  647. }
  648. // check for NFR
  649. if (!_wcsicmp(pMPC,SRV_NFR_MPC)){
  650. return (szSkuServerNFR);
  651. }
  652. if (!_wcsicmp(pMPC,ASRV_NFR_MPC)){
  653. return (szSkuAdvServerNFR);
  654. }
  655. if (MediaType == CDRetail) {
  656. if (!_wcsicmp(pMPC,L"51873")){
  657. return (szSkuProfessionalFPP);
  658. }
  659. if (!_wcsicmp(pMPC,L"51874")){
  660. return (szSkuProfessionalCCP);
  661. }
  662. if (!_wcsicmp(pMPC,L"51876")){
  663. return (szSkuServerFPP);
  664. }
  665. if (!_wcsicmp(pMPC,L"51877")){
  666. return (szSkuServerCCP);
  667. }
  668. if (!_wcsicmp(pMPC,L"51879")){
  669. return (szSkuAdvServerFPP);
  670. }
  671. if (!_wcsicmp(pMPC,L"51880")){
  672. return (szSkuAdvServerCCP);
  673. }
  674. if (!_wcsicmp(pMPC,L"51891")){
  675. return (szSkuDTCFPP);
  676. }
  677. } else if (MediaType == CDSelect) {
  678. if (!_wcsicmp(pMPC,L"51873")){
  679. return (szSkuProfessionalSelect);
  680. }
  681. if (!_wcsicmp(pMPC,L"51876")){
  682. return (szSkuServerSelect);
  683. }
  684. if (!_wcsicmp(pMPC,L"51879")){
  685. return (szSkuAdvServerSelect);
  686. }
  687. if (!_wcsicmp(pMPC,L"51891")){
  688. return (szSkuDTCSelect);
  689. }
  690. }
  691. return (szSkuUnknown);
  692. }
  693. BOOL
  694. GetProductTypeFromRegistry(
  695. VOID
  696. )
  697. /*++
  698. Routine Description:
  699. Reads the Product Type from the parameters files and sets up
  700. the ProductType global variable.
  701. Arguments:
  702. None
  703. Returns:
  704. Bool value indicating outcome.
  705. --*/
  706. {
  707. WCHAR p[MAX_PARAM_LEN] = {0};
  708. DWORD rc = 0;
  709. DWORD d = 0;
  710. DWORD Type = 0;
  711. HKEY hKey = (HKEY)INVALID_HANDLE_VALUE;
  712. rc = 0;
  713. if( !gMiniSetup ) {
  714. WCHAR AnswerFilePath[MAX_PATH] = {0};
  715. //
  716. // Go try and get the product type out of the [data] section
  717. // of $winnt$.sif
  718. //
  719. rc = GetWindowsDirectory( AnswerFilePath, MAX_PATH );
  720. wcsncat( AnswerFilePath, TEXT("\\system32\\$winnt$.inf"), MAX_PATH );
  721. AnswerFilePath[MAX_PATH-1] = TEXT('\0');
  722. rc = GetPrivateProfileString( WINNT_DATA,
  723. WINNT_D_PRODUCT,
  724. L"",
  725. p,
  726. MAX_PARAM_LEN,
  727. AnswerFilePath );
  728. }
  729. //
  730. // Either this is a MiniSetup, or we failed to get the key out of
  731. // the unattend file. Go look in the registry.
  732. //
  733. if( rc == 0 ) {
  734. //
  735. // Open the key.
  736. //
  737. rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  738. L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
  739. 0,
  740. KEY_READ,
  741. &hKey );
  742. if( rc != NO_ERROR ) {
  743. return( FALSE );
  744. }
  745. //
  746. // Get the size of the ProductType entry.
  747. //
  748. rc = RegQueryValueEx( hKey,
  749. L"ProductType",
  750. NULL,
  751. &Type,
  752. NULL,
  753. &d );
  754. if( rc != NO_ERROR ) {
  755. return( FALSE );
  756. }
  757. //
  758. // Get the ProductType entry.
  759. //
  760. rc = RegQueryValueEx( hKey,
  761. L"ProductType",
  762. NULL,
  763. &Type,
  764. (LPBYTE)p,
  765. &d );
  766. if( rc != NO_ERROR ) {
  767. return( FALSE );
  768. }
  769. }
  770. //
  771. // We managed to find an entry in the parameters file
  772. // so we *should* be able to decode it
  773. //
  774. if(!lstrcmpi(p,pwWinNt)) {
  775. //
  776. // We have a WINNT product
  777. //
  778. ProductType = PRODUCT_WORKSTATION;
  779. } else if(!lstrcmpi(p,pwLanmanNt)) {
  780. //
  781. // We have a PRIMARY SERVER product
  782. //
  783. ProductType = PRODUCT_SERVER_PRIMARY;
  784. } else if(!lstrcmpi(p,pwServerNt)) {
  785. //
  786. // We have a STANDALONE SERVER product
  787. // NOTE: this case can currently never occur, since text mode
  788. // always sets WINNT_D_PRODUCT to lanmannt or winnt.
  789. //
  790. ProductType = PRODUCT_SERVER_STANDALONE;
  791. } else {
  792. //
  793. // We can't determine what we are, so fail
  794. //
  795. return (FALSE);
  796. }
  797. return (TRUE);
  798. }
  799. BOOL
  800. ValidatePidEx(
  801. LPTSTR PID
  802. )
  803. /*++
  804. Routine Description:
  805. This routine validates the given PID string using the PID Gen DLL.
  806. Note: this routine loads the pidgen.dll and therefore makes setup.exe
  807. dependent upon pidgen.dll
  808. Arguments:
  809. PID - the PID to be validated [should be in PID 30 format]
  810. Returns:
  811. TRUE - valid
  812. FALSE - otherwise
  813. --*/
  814. {
  815. BOOL bRet = FALSE;
  816. TCHAR Pid20Id[MAX_PATH];
  817. BYTE Pid30[1024]={0};
  818. TCHAR pszSkuCode[10];
  819. HINSTANCE hPidgenDll;
  820. SETUPPIDGENW pfnSetupPIDGen;
  821. DWORD Error = ERROR_SUCCESS;
  822. DWORD cbData = 0;
  823. PWSTR p;
  824. HKEY Key;
  825. DWORD Type;
  826. // Load library pidgen.dll
  827. hPidgenDll = LoadLibrary ( L"pidgen.dll" );
  828. if ( hPidgenDll )
  829. {
  830. // Get the function pointer
  831. pfnSetupPIDGen = (SETUPPIDGENW)GetProcAddress(hPidgenDll, "SetupPIDGenW");
  832. if ( pfnSetupPIDGen )
  833. {
  834. GetProductTypeFromRegistry();
  835. //
  836. // Derive the release type and media type we're installing from.
  837. //
  838. Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  839. (gMiniSetup) ? L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" : L"SYSTEM\\Setup\\Pid",
  840. 0,
  841. KEY_READ,
  842. &Key );
  843. if( Error == ERROR_SUCCESS ) {
  844. cbData = sizeof(TmpData);
  845. Error = RegQueryValueEx( Key,
  846. (gMiniSetup) ? L"ProductId" : L"Pid",
  847. 0,
  848. &Type,
  849. ( LPBYTE )TmpData,
  850. &cbData );
  851. RegCloseKey( Key );
  852. //
  853. // If we're in MiniSetup, then the value in TmpData
  854. // looks like: 12345-xxx-67890...
  855. // We want the 12345 to go into Pid30Rpc
  856. // and xxx to go into Pid30Site.
  857. //
  858. // If we're not in MiniSetup, then the value in
  859. // Tmpdata looks like: 12345XXX67890...
  860. //
  861. wcsncpy( Pid30Rpc, TmpData, MAX_PID30_RPC );
  862. Pid30Rpc[MAX_PID30_RPC] = (WCHAR)'\0';
  863. if( gMiniSetup ) {
  864. p = TmpData + (MAX_PID30_RPC + 1);
  865. } else {
  866. p = TmpData + (MAX_PID30_RPC);
  867. }
  868. wcsncpy(Pid30Site,p,MAX_PID30_SITE+1);
  869. Pid30Site[MAX_PID30_SITE] = (WCHAR)'\0';
  870. //
  871. // Derive the media type.
  872. //
  873. if( _wcsicmp(Pid30Site, szPidSelectId) == 0) {
  874. CDType = CDSelect;
  875. } else if( _wcsicmp(Pid30Site, szPidOemId) == 0) {
  876. CDType = CDOem;
  877. } else {
  878. // no idea... Assume retail.
  879. CDType = CDRetail;
  880. }
  881. }
  882. PCWSTR tmpP = GetStockKeepingUnit(
  883. Pid30Rpc,
  884. ProductType,
  885. CDType
  886. );
  887. lstrcpy(pszSkuCode, tmpP);
  888. *(LPDWORD)Pid30 = sizeof(Pid30);
  889. //
  890. // attempt to validate the PID
  891. //
  892. if ( pfnSetupPIDGen(
  893. PID, // [IN] 25-character Secure CD-Key (gets U-Cased)
  894. Pid30Rpc, // [IN] 5-character Release Product Code
  895. pszSkuCode, // [IN] Stock Keeping Unit (formatted like 123-12345)
  896. (CDType == CDOem), // [IN] is this an OEM install?
  897. Pid20Id, // [OUT] PID 2.0, pass in ptr to 24 character array
  898. Pid30, // [OUT] pointer to binary PID3 buffer. First DWORD is the length
  899. NULL // [OUT] optional ptr to Compliance Checking flag (can be NULL)
  900. ) )
  901. {
  902. // The Group ID is the dword starting at offset 0x20
  903. g_dwGroupID = (DWORD) ( Pid30[ 0x20 ] );
  904. // Set the return Value to true
  905. bRet = TRUE;
  906. }
  907. }
  908. FreeLibrary ( hPidgenDll ) ;
  909. }
  910. // if the caller wants, return if this is a Volume License PID
  911. return bRet;
  912. }
  913. BOOL
  914. GetPid( PWSTR PidString,
  915. ULONG BufferSize,
  916. HANDLE hCancelEvent
  917. )
  918. /*++
  919. Routine Description:
  920. Prompts the user for a valid PID.
  921. Arguments:
  922. PidString - Buffer that will recieve the PID. The resulting
  923. string has the form: VVVVV-WWWWW-XXXXX-YYYYY-ZZZZZ.
  924. BufferSize - specifies the # of bytes in the PidString buffer
  925. (including null termination)
  926. hCancelEvent - an event, which if signalled indicates that this routine
  927. should exit and return failure.
  928. Return Value:
  929. Win32 Error code. Should be ERROR_SUCCESS if everything goes well.
  930. --*/
  931. {
  932. BOOL Done = FALSE;
  933. BOOL bSuccess = FALSE;
  934. ULONG i = 0;
  935. ULONG PidStringLength = (BufferSize / sizeof(WCHAR)) - 1;
  936. if( (PidString == NULL) || PidStringLength < PID_30_LENGTH) {
  937. return FALSE;
  938. }
  939. //
  940. // Keep asking the user until we get what we want.
  941. //
  942. Done = FALSE;
  943. memset( PidString,
  944. 0,
  945. BufferSize
  946. );
  947. do {
  948. //
  949. // Clear the screen.
  950. //
  951. ClearEMSScreen();
  952. //
  953. // Write some instructions/information.
  954. //
  955. WriteResourceMessage( IDS_PID_BANNER_1 );
  956. WriteResourceMessage( IDS_PID_BANNER_2 );
  957. //
  958. // get the PID entry
  959. //
  960. bSuccess = GetStringFromEMS(
  961. PidString,
  962. BufferSize,
  963. FALSE,
  964. TRUE,
  965. hCancelEvent
  966. );
  967. if (IsAsyncCancelSignalled(hCancelEvent)) {
  968. bSuccess = FALSE;
  969. goto exit;
  970. }
  971. if (!bSuccess) {
  972. goto exit;
  973. }
  974. //
  975. // Make sure the PID is in the form we're expecting. Actually
  976. // make sure it's in the form that guimode setup is expecting.
  977. //
  978. // Then go validate it.
  979. //
  980. if( (wcslen(PidString) == PID_30_LENGTH) && ValidatePidEx(PidString) ) {
  981. Done = TRUE;
  982. } else {
  983. WCHAR wc;
  984. Done = FALSE;
  985. //
  986. // invalid pid. inform the user and try again.
  987. //
  988. WriteResourceMessage( IDS_PID_INVALID );
  989. bSuccess = ReadCharFromEMS(&wc, hCancelEvent);
  990. if (IsAsyncCancelSignalled(hCancelEvent)) {
  991. bSuccess = FALSE;
  992. goto exit;
  993. }
  994. if (!bSuccess) {
  995. goto exit;
  996. }
  997. }
  998. } while ( !Done );
  999. exit:
  1000. return(bSuccess);
  1001. }
  1002. BOOL
  1003. PresentEula(
  1004. HANDLE hCancelEvent
  1005. )
  1006. /*++
  1007. Routine Description:
  1008. This function will present the user with an end-user-license-agreement (EULA).
  1009. If the user rejects the EULA, then the function will return FALSE, otherwise
  1010. TRUE.
  1011. Arguments:
  1012. hCancelEvent - an event, which if signalled indicates that this routine
  1013. should exit, returning error immediately.
  1014. Return Value:
  1015. TRUE - the EULA was accepted.
  1016. FALSE - the EULA was rejected.
  1017. --*/
  1018. {
  1019. WCHAR EulaPath[MAX_PATH];
  1020. HANDLE hFile = INVALID_HANDLE_VALUE;
  1021. HANDLE hFileMapping = INVALID_HANDLE_VALUE;
  1022. DWORD FileSize;
  1023. BYTE *pbFile = NULL;
  1024. PWSTR EulaText = NULL;
  1025. ULONG i;
  1026. ULONG j;
  1027. BOOL bSuccess;
  1028. BOOL Done;
  1029. BOOL bValidUserInput;
  1030. BOOL bAtEULAEnd;
  1031. BOOL ConvertResult;
  1032. //
  1033. // default: EULA was not accepted
  1034. //
  1035. bSuccess = FALSE;
  1036. //
  1037. // Load the EULA
  1038. //
  1039. //
  1040. // Map the file containing the licensing agreement.
  1041. //
  1042. if(!GetSystemDirectory(EulaPath, MAX_PATH)){
  1043. goto exit;
  1044. }
  1045. wcsncat( EulaPath, TEXT("\\eula.txt"), MAX_PATH );
  1046. EulaPath[MAX_PATH-1] = TEXT('\0');
  1047. hFile = CreateFile (
  1048. EulaPath,
  1049. GENERIC_READ,
  1050. FILE_SHARE_READ,
  1051. NULL,
  1052. OPEN_EXISTING,
  1053. FILE_ATTRIBUTE_NORMAL,
  1054. NULL
  1055. );
  1056. if(hFile == INVALID_HANDLE_VALUE) {
  1057. goto exit;
  1058. }
  1059. hFileMapping = CreateFileMapping (
  1060. hFile,
  1061. NULL,
  1062. PAGE_READONLY,
  1063. 0, 0,
  1064. NULL
  1065. );
  1066. if(hFileMapping == NULL) {
  1067. goto exit;
  1068. }
  1069. pbFile = (BYTE*)MapViewOfFile (
  1070. hFileMapping,
  1071. FILE_MAP_READ,
  1072. 0, 0,
  1073. 0
  1074. );
  1075. if(pbFile == NULL) {
  1076. goto exit;
  1077. }
  1078. //
  1079. // Translate the text from ANSI to Unicode.
  1080. //
  1081. FileSize = GetFileSize (hFile, NULL);
  1082. if(FileSize == 0xFFFFFFFF) {
  1083. goto exit;
  1084. }
  1085. EulaText = (PWSTR)malloc ((FileSize+1) * sizeof(WCHAR));
  1086. if(EulaText == NULL) {
  1087. goto exit;
  1088. }
  1089. ConvertResult = MultiByteToWideChar(
  1090. CP_ACP,
  1091. 0,
  1092. (LPCSTR)pbFile,
  1093. FileSize,
  1094. EulaText,
  1095. FileSize+1
  1096. );
  1097. if (!ConvertResult) {
  1098. goto exit;
  1099. }
  1100. //
  1101. // make sure there is a null terminator.
  1102. //
  1103. EulaText[FileSize] = 0;
  1104. //
  1105. // present the EULA to the EMS user
  1106. //
  1107. j=0;
  1108. Done = FALSE;
  1109. bAtEULAEnd = FALSE;
  1110. do {
  1111. //
  1112. // Clear the screen.
  1113. //
  1114. ClearEMSScreen();
  1115. i=0;
  1116. do {
  1117. gEMSChannel->Write( (PWSTR)(&(EulaText[j])), sizeof(WCHAR) );
  1118. // look for 0x0D0x0A pairs to denote EOL
  1119. if (EulaText[j] == 0x0D) {
  1120. if (j+1 < FileSize) {
  1121. if (EulaText[j+1] == 0x0A) {
  1122. i++;
  1123. if (i == EULA_LINES_PER_SCREEN) {
  1124. j++; // skip 0x0A if this is the last line on the screen
  1125. // so that the next screen doesnt start with a lf
  1126. gEMSChannel->Write( (PWSTR)(&(EulaText[j])), sizeof(WCHAR) );
  1127. }
  1128. }
  1129. }
  1130. }
  1131. j++;
  1132. } while ( (i < EULA_LINES_PER_SCREEN) && (j < FileSize));
  1133. //
  1134. // Write some instructions/information.
  1135. //
  1136. WriteResourceMessage( IDS_EULA_ACCEPT_DECLINE );
  1137. if (j < FileSize) {
  1138. WriteResourceMessage( IDS_EULA_MORE );
  1139. } else {
  1140. // there are no more pages to display
  1141. bAtEULAEnd = TRUE;
  1142. gEMSChannel->Write( (PWSTR)(L"\r\n"), sizeof(WCHAR)*2 );
  1143. }
  1144. //
  1145. // attempt to get a valid response from the user
  1146. //
  1147. // F8 == accept EULA
  1148. // ESC == decline EULA
  1149. // PAGE DOWN == go to next page if there is one
  1150. // else just loop
  1151. //
  1152. do {
  1153. ULONG UserInputChar;
  1154. BOOL bHaveChar;
  1155. bValidUserInput = FALSE;
  1156. //
  1157. // see what the user wants to do
  1158. //
  1159. bHaveChar = GetDecodedKeyPressFromEMS(
  1160. &UserInputChar,
  1161. hCancelEvent
  1162. );
  1163. if (IsAsyncCancelSignalled(hCancelEvent)) {
  1164. bSuccess = FALSE;
  1165. goto exit;
  1166. }
  1167. if (!bHaveChar) {
  1168. bSuccess = FALSE;
  1169. goto exit;
  1170. }
  1171. switch(UserInputChar) {
  1172. case KEY_F8:
  1173. bSuccess = TRUE;
  1174. Done = TRUE;
  1175. bValidUserInput = TRUE;
  1176. break;
  1177. case ASCI_ESC:
  1178. bSuccess = FALSE;
  1179. Done = TRUE;
  1180. bValidUserInput = TRUE;
  1181. break;
  1182. case KEY_PAGEDOWN:
  1183. if (!bAtEULAEnd) {
  1184. // go to the next page if there is one
  1185. bValidUserInput = TRUE;
  1186. break;
  1187. }
  1188. // else consider this extraneous input and
  1189. // fall through to default
  1190. default:
  1191. //
  1192. // do nothing unless they give us what we want
  1193. //
  1194. NOTHING;
  1195. break;
  1196. }
  1197. } while ( !bValidUserInput);
  1198. } while ( !Done );
  1199. //
  1200. // cleanup
  1201. //
  1202. exit:
  1203. if (pbFile != NULL) {
  1204. UnmapViewOfFile(pbFile);
  1205. }
  1206. if (hFileMapping != INVALID_HANDLE_VALUE) {
  1207. CloseHandle(hFileMapping);
  1208. }
  1209. if (hFile != INVALID_HANDLE_VALUE) {
  1210. CloseHandle(hFile);
  1211. }
  1212. if (EulaText != NULL) {
  1213. free(EulaText);
  1214. }
  1215. return(bSuccess);
  1216. }
  1217. //
  1218. // ====================================
  1219. // Core functionality
  1220. // ====================================
  1221. //
  1222. INT_PTR CALLBACK
  1223. UserInputAbortProc(
  1224. HWND hwndDlg, // handle to dialog box
  1225. UINT uMsg, // message
  1226. WPARAM wParam, // first message parameter
  1227. LPARAM lParam // second message parameter
  1228. )
  1229. {
  1230. BOOL retval = FALSE;
  1231. static UINT_PTR TimerId;
  1232. static HANDLE hRemoveUI;
  1233. switch(uMsg) {
  1234. case WM_INITDIALOG:
  1235. hRemoveUI = (HANDLE)lParam;
  1236. //
  1237. // create a timer that fires every second. we will use this timer to
  1238. // determine if the dialog should go away.
  1239. //
  1240. if (!(TimerId = SetTimer(hwndDlg,0,1000,NULL))) {
  1241. EndDialog(hwndDlg,0);
  1242. }
  1243. break;
  1244. case WM_TIMER:
  1245. //
  1246. // check if the thread that created this dialog should go away.
  1247. //
  1248. if (WaitForSingleObject(hRemoveUI,0) == WAIT_OBJECT_0) {
  1249. //
  1250. // yes, the event signaled. cleanup and exit.
  1251. //
  1252. KillTimer(hwndDlg,TimerId);
  1253. EndDialog(hwndDlg,1);
  1254. }
  1255. break;
  1256. case WM_COMMAND:
  1257. switch (HIWORD( wParam ))
  1258. {
  1259. case BN_CLICKED:
  1260. switch (LOWORD( wParam ))
  1261. {
  1262. case IDOK:
  1263. case IDCANCEL:
  1264. //
  1265. // the user doesn't want prompted over the headless port.
  1266. // kill the timer and exit.
  1267. //
  1268. KillTimer(hwndDlg,TimerId);
  1269. EndDialog(hwndDlg,2);
  1270. }
  1271. };
  1272. }
  1273. return(retval);
  1274. }
  1275. DWORD
  1276. PromptForUserInputThreadOverHeadlessConnection(
  1277. PVOID params
  1278. )
  1279. {
  1280. PUserInputParams Params = (PUserInputParams)params;
  1281. //
  1282. // Go examine the unattend file. If we find keys that either
  1283. // aren't present, or will prevent us from going fully unattended,
  1284. // then we'll fix them.
  1285. //
  1286. // We'll also prompt for a PID and/or EULA if necessary (i.e. if
  1287. // the unattend file says we need to.
  1288. //
  1289. ProcessUnattendFile( TRUE, NULL, &Params->hRemoveUI );
  1290. SetEvent(Params->hInputCompleteEvent);
  1291. return 0;
  1292. }
  1293. DWORD
  1294. PromptForUserInputThreadViaLocalDialog(
  1295. PVOID params
  1296. )
  1297. {
  1298. PUserInputParams Params = (PUserInputParams)params;
  1299. DialogBoxParam(
  1300. GetModuleHandle(NULL),
  1301. MAKEINTRESOURCE(IDD_ABORTDIALOG),
  1302. NULL,
  1303. UserInputAbortProc,
  1304. (LPARAM)Params->hRemoveUI);
  1305. SetEvent(Params->hInputCompleteEvent);
  1306. return 0;
  1307. }
  1308. BOOL LoadStringResource(
  1309. PUNICODE_STRING pUnicodeString,
  1310. INT MsgId
  1311. )
  1312. /*++
  1313. Routine Description:
  1314. This is a simple implementation of LoadString().
  1315. Arguments:
  1316. usString - Returns the resource string.
  1317. MsgId - Supplies the message id of the resource string.
  1318. Return Value:
  1319. FALSE - Failure.
  1320. TRUE - Success.
  1321. --*/
  1322. {
  1323. PWSTR MyString;
  1324. DWORD StringLength,RetVal;
  1325. BOOL bSuccess = FALSE;
  1326. //
  1327. // the compiler clips string table entries at 256,
  1328. // so this should be large enough for all calls
  1329. //
  1330. StringLength = 512;
  1331. RetVal = 0;
  1332. MyString = (PWSTR)malloc(StringLength*sizeof(WCHAR));
  1333. if (MyString) {
  1334. RetVal = LoadString(
  1335. GetModuleHandle(NULL),
  1336. MsgId,
  1337. MyString,
  1338. StringLength
  1339. );
  1340. if (RetVal != 0) {
  1341. RtlCreateUnicodeString(pUnicodeString, (PWSTR)MyString);
  1342. bSuccess = TRUE;
  1343. }
  1344. free(MyString);
  1345. }
  1346. return(bSuccess);
  1347. }
  1348. BOOL
  1349. WriteResourceMessage(
  1350. ULONG MessageID
  1351. )
  1352. /*++
  1353. Routine Description:
  1354. This routine writes a resource string message to
  1355. the global headless channel gEMSChannel.
  1356. Arguments:
  1357. MessageID - the id of the message to write
  1358. Return Value:
  1359. TRUE - the message was loaded and written
  1360. FALSE - failed
  1361. --*/
  1362. {
  1363. UNICODE_STRING UnicodeString = {0};
  1364. BOOL bSuccess = FALSE;
  1365. bSuccess = LoadStringResource( &UnicodeString, MessageID );
  1366. if ( bSuccess ) {
  1367. //
  1368. // Write the message
  1369. //
  1370. gEMSChannel->Write( (PWSTR)UnicodeString.Buffer,
  1371. (ULONG)(wcslen( UnicodeString.Buffer) * sizeof(WCHAR)) );
  1372. RtlFreeUnicodeString(&UnicodeString);
  1373. }
  1374. return bSuccess;
  1375. }
  1376. BOOL
  1377. PromptForPassword(
  1378. PWSTR Password,
  1379. ULONG BufferSize,
  1380. HANDLE hCancelEvent
  1381. )
  1382. /*++
  1383. /*++
  1384. Routine Description:
  1385. This routine asks the user for an administrator password.
  1386. The contents of the response are checked to ensure the password
  1387. is reasonable. If the response is not deemed reasonable, then
  1388. the user is informed and requeried.
  1389. Arguments:
  1390. AdministratorPassword - Pointer to a string which holds the password.
  1391. BufferSize - the # of bytes in the Password buffer
  1392. (including the null termination)
  1393. hCancelEvent - an event that signals that the routine should exit without completing.
  1394. Return Value:
  1395. Returns TRUE if the password is successfully retrieved.
  1396. FALSE otherwise.
  1397. --*/
  1398. {
  1399. //
  1400. // this is an arbitrary length, but needed so that the password UI doesn't wrap on a console screen.
  1401. // if the user really wants a long password, they can change it post setup.
  1402. //
  1403. #define MY_MAX_PASSWORD_LENGTH (20)
  1404. BOOL Done = FALSE;
  1405. WCHAR InputBuffer[MY_MAX_PASSWORD_LENGTH+1];
  1406. WCHAR ConfirmAdministratorPassword[MY_MAX_PASSWORD_LENGTH+1];
  1407. BOOL GotAPassword;
  1408. ULONG MaxPasswordLength = 0;
  1409. ULONG BytesRead = 0;
  1410. UNICODE_STRING UnicodeString = {0};
  1411. BOOL bSuccess;
  1412. ULONG CurrentPasswordCharacterIndex = 0;
  1413. ULONG PasswordLength = (BufferSize / sizeof(WCHAR)) - 1;
  1414. MaxPasswordLength = min( PasswordLength, MY_MAX_PASSWORD_LENGTH );
  1415. if( (Password == NULL) || (MaxPasswordLength == 0) ) {
  1416. return FALSE;
  1417. }
  1418. //
  1419. // Keep asking the user until we get what we want.
  1420. //
  1421. Done = FALSE;
  1422. memset( Password,
  1423. 0,
  1424. BufferSize
  1425. );
  1426. do {
  1427. //
  1428. // Clear the screen.
  1429. //
  1430. ClearEMSScreen();
  1431. //
  1432. // Write some instructions/information.
  1433. //
  1434. WriteResourceMessage( IDS_PASSWORD_BANNER );
  1435. //
  1436. // get the first password entry
  1437. //
  1438. bSuccess = GetStringFromEMS(
  1439. Password,
  1440. MaxPasswordLength * sizeof(WCHAR),
  1441. FALSE,
  1442. FALSE,
  1443. hCancelEvent
  1444. );
  1445. if (IsAsyncCancelSignalled(hCancelEvent)) {
  1446. bSuccess = FALSE;
  1447. goto exit;
  1448. }
  1449. if (!bSuccess) {
  1450. goto exit;
  1451. }
  1452. //
  1453. // Now prompt for it a second time to confirm.
  1454. //
  1455. //
  1456. // Write some instructions/information.
  1457. //
  1458. WriteResourceMessage( IDS_CONFIRM_PASSWORD_BANNER );
  1459. //
  1460. // get the second password entry
  1461. //
  1462. bSuccess = GetStringFromEMS(
  1463. ConfirmAdministratorPassword,
  1464. MaxPasswordLength * sizeof(WCHAR),
  1465. FALSE,
  1466. FALSE,
  1467. hCancelEvent
  1468. );
  1469. if (IsAsyncCancelSignalled(hCancelEvent)) {
  1470. bSuccess = FALSE;
  1471. goto exit;
  1472. }
  1473. if (!bSuccess) {
  1474. goto exit;
  1475. }
  1476. //
  1477. // Now compare the two.
  1478. //
  1479. Done = TRUE;
  1480. if( (wcslen(Password) != wcslen(ConfirmAdministratorPassword)) ||
  1481. wcsncmp(Password, ConfirmAdministratorPassword, wcslen(Password)) ) {
  1482. //
  1483. // They entered 2 different passwords.
  1484. //
  1485. WriteResourceMessage( IDS_DIFFERENT_PASSWORDS_MESSAGE );
  1486. Done = FALSE;
  1487. } else {
  1488. ULONG i = 0;
  1489. //
  1490. // Make sure they entered something decent.
  1491. //
  1492. for( i = 0; i < wcslen(Password); i++ ) {
  1493. if( (Password[i] <= 0x20) ||
  1494. (Password[i] >= 0x7F) ) {
  1495. Done = FALSE;
  1496. break;
  1497. }
  1498. }
  1499. //
  1500. // Also make sure they didn't give me a blank
  1501. //
  1502. if( Password[0] == L'\0' ) {
  1503. Done = FALSE;
  1504. }
  1505. if (!Done) {
  1506. //
  1507. // It's a bad password.
  1508. //
  1509. WriteResourceMessage( IDS_BAD_PASSWORD_CONTENTS );
  1510. }
  1511. }
  1512. if (!Done) {
  1513. WCHAR wc;
  1514. //
  1515. // we posted a message, so
  1516. // wait for them to hit a key and we'll keep going.
  1517. //
  1518. bSuccess = ReadCharFromEMS(&wc, hCancelEvent);
  1519. if (IsAsyncCancelSignalled(hCancelEvent)) {
  1520. bSuccess = FALSE;
  1521. goto exit;
  1522. }
  1523. if (!bSuccess) {
  1524. goto exit;
  1525. }
  1526. }
  1527. } while ( !Done );
  1528. exit:
  1529. return(bSuccess);
  1530. }
  1531. DWORD
  1532. ProcessUnattendFile(
  1533. BOOL FixUnattendFile,
  1534. PBOOL NeedsProcessing, OPTIONAL
  1535. PHANDLE hEvent OPTIONAL
  1536. )
  1537. /*++
  1538. Routine Description:
  1539. This function will the unattend file and determine if guimode
  1540. setup will be able to proceed all the way through without any user input.
  1541. If user input is required, we will either call someone to provide the
  1542. ask for input, or we'll fill in a default value to allow setup to proceed.
  1543. NOTE:
  1544. The interesting bit here is that we need to go search for the unattend file.
  1545. That's because we might be running as a result of sysprep, or we might be
  1546. running as a result of just booting into guimode setup. If we came through
  1547. sysprep, then we need to go modify \sysprep\sysprep.inf. That's because
  1548. Setup will take sysprep.inf and overwrite %windir%\system32\$winnt$.inf
  1549. before proceeding. We'll intercept, fix sysprep.inf, then let it get
  1550. copied on top of $winnt$.inf.
  1551. Arguments:
  1552. FixUnattendFile - Indicates if we should only examine, or actually
  1553. fix the file by writing new values into it.
  1554. If this comes in as false, no updates are made and
  1555. no prompts are sent to the user.
  1556. NeedsProcessing - if FixUnattendFile is FALSE, this gets filled in with
  1557. whether we need to update the unattend file.
  1558. hEvent - a handle, which if supplied and signalled, indicates that the routine
  1559. should exit with status ERROR_CANCELLED.
  1560. Return Value:
  1561. Win32 Error code indicating outcome.
  1562. --*/
  1563. {
  1564. DWORD Result = 0;
  1565. WCHAR AnswerFilePath[MAX_PATH] = {0};
  1566. WCHAR Answer[MAX_PATH] = {0};
  1567. BOOL b = TRUE;
  1568. DWORD ReturnCode = ERROR_SUCCESS;
  1569. BOOL NeedEula = TRUE;
  1570. BOOL OEMPreinstall = FALSE;
  1571. HANDLE hCancelEvent;
  1572. if (hEvent) {
  1573. hCancelEvent = *hEvent;
  1574. } else {
  1575. hEvent = NULL;
  1576. }
  1577. if (NeedsProcessing) {
  1578. *NeedsProcessing = FALSE;
  1579. }
  1580. //
  1581. // Build the path to the unattend file.
  1582. //
  1583. Result = GetWindowsDirectory( AnswerFilePath, MAX_PATH );
  1584. if( Result == 0) {
  1585. // Odd...
  1586. return GetLastError();
  1587. }
  1588. if( gMiniSetup ) {
  1589. //
  1590. // This is a boot into minisetup. Go load \sysprep\sysprep.inf
  1591. //
  1592. AnswerFilePath[3] = 0;
  1593. wcsncat( AnswerFilePath, TEXT("sysprep\\sysprep.inf"), MAX_PATH );
  1594. AnswerFilePath[MAX_PATH-1] = TEXT('\0');
  1595. } else {
  1596. //
  1597. // This is a boot into guimode setup. Go load %windir%\system32\$winnt$.inf
  1598. //
  1599. wcsncat( AnswerFilePath, TEXT("\\system32\\$winnt$.inf"), MAX_PATH );
  1600. AnswerFilePath[MAX_PATH-1] = TEXT('\0');
  1601. }
  1602. //
  1603. // ===================
  1604. // Go check the keys that are required to make the install
  1605. // happen completely unattendedly.
  1606. // ===================
  1607. //
  1608. //
  1609. // First check if it's an upgrade. If so, then there will
  1610. // be no prompts during guimode setup, so we can be done.
  1611. //
  1612. Answer[0] = TEXT('\0');
  1613. Result = GetPrivateProfileString( WINNT_DATA,
  1614. WINNT_D_NTUPGRADE,
  1615. L"",
  1616. Answer,
  1617. MAX_PATH,
  1618. AnswerFilePath );
  1619. if( (Result != 0) &&
  1620. !_wcsicmp(Answer, L"Yes") ) {
  1621. //
  1622. // It's an upgrade so We have zero work
  1623. // to do. Tell our caller that nothing's
  1624. // needed.
  1625. //
  1626. return ERROR_SUCCESS;
  1627. }
  1628. //
  1629. // Check if key present to skip this processing of Unattend file
  1630. //
  1631. Answer[0] = TEXT('\0');
  1632. Result = GetPrivateProfileString( WINNT_UNATTENDED,
  1633. L"EMSSkipUnattendProcessing",
  1634. L"",
  1635. Answer,
  1636. MAX_PATH,
  1637. AnswerFilePath );
  1638. if(Result != 0) {
  1639. //
  1640. // if the flag was set,
  1641. // then we dont need to process anything
  1642. // and we are done
  1643. //
  1644. if (NeedsProcessing) {
  1645. *NeedsProcessing = FALSE;
  1646. }
  1647. return ERROR_SUCCESS;
  1648. }
  1649. //
  1650. // Now see if it's an OEM preinstall. We need this because some
  1651. // unattend keys are ignored if it's a preinstall, so we
  1652. // have to resort to the secret keys to make some wizard
  1653. // pages really go away.
  1654. //
  1655. Answer[0] = TEXT('\0');
  1656. Result = GetPrivateProfileString( WINNT_UNATTENDED,
  1657. WINNT_OEMPREINSTALL,
  1658. L"",
  1659. Answer,
  1660. MAX_PATH,
  1661. AnswerFilePath );
  1662. if( (!_wcsicmp(Answer, L"yes")) || (gMiniSetup) ) {
  1663. OEMPreinstall = TRUE;
  1664. }
  1665. //
  1666. // MiniSetup specific fixups/checks.
  1667. //
  1668. if( (gMiniSetup) &&
  1669. (FixUnattendFile) ) {
  1670. WCHAR SysprepDirPath[MAX_PATH];
  1671. HKEY hKeySetup;
  1672. //
  1673. // We need to be careful here. If they're doing a minisetup,
  1674. // and the machine gets restarted before we actually launch into
  1675. // guimode setup, the machine will be wedged.
  1676. // We need to hack the registry to make it so minisetup will
  1677. // restart correctly.
  1678. //
  1679. //
  1680. // Reset the SetupType entry to 1. We'll clear
  1681. // it at the end of gui-mode.
  1682. //
  1683. Result = (DWORD)RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1684. L"System\\Setup",
  1685. 0,
  1686. KEY_SET_VALUE | KEY_QUERY_VALUE,
  1687. &hKeySetup );
  1688. if(Result == NO_ERROR) {
  1689. //
  1690. // Set HKLM\System\Setup\SetupType Key to SETUPTYPE_NOREBOOT
  1691. //
  1692. Result = 1;
  1693. RegSetValueEx( hKeySetup,
  1694. TEXT( "SetupType" ),
  1695. 0,
  1696. REG_DWORD,
  1697. (CONST BYTE *)&Result,
  1698. sizeof(DWORD));
  1699. RegCloseKey(hKeySetup);
  1700. }
  1701. //
  1702. // If FixUnattendFile is set, then we're about to actually
  1703. // start fiddling with their unattend file. It happens that
  1704. // they could have run sysprep in a way that there's no c:\sysprep
  1705. // directory. Before we start fixing the unattend file in
  1706. // there, we should make sure the directory exists. That
  1707. // way our calls to WritePrivateProfileString will work
  1708. // correctly and all will fly through unattendedly.
  1709. //
  1710. Result = GetWindowsDirectory( SysprepDirPath, MAX_PATH );
  1711. if( Result == 0) {
  1712. // Odd...
  1713. return GetLastError();
  1714. }
  1715. SysprepDirPath[3] = 0;
  1716. wcsncat( SysprepDirPath, TEXT("sysprep"), MAX_PATH );
  1717. SysprepDirPath[MAX_PATH-1] = TEXT('\0');
  1718. //
  1719. // If the directory exists, this is a no-op. If it
  1720. // doesn't, we'll create it. Note that permissions don't
  1721. // really matter here because:
  1722. // 1. this is how sysprep itself creates this directory.
  1723. // 2. minisetup will delete this directory very shortly.
  1724. //
  1725. CreateDirectory( SysprepDirPath, NULL );
  1726. }
  1727. //
  1728. // If FixUnattendFile is set, then we're about to actually
  1729. // start fiddling with their unattend file. It happens that
  1730. // they could have run sysprep in a way that there's no c:\sysprep
  1731. // directory. Before we start fixing the unattend file in
  1732. // there, we should make sure the directory exists. That
  1733. // way our calls to WritePrivateProfileString will work
  1734. // correctly and all will fly through unattendedly.
  1735. //
  1736. //
  1737. // Start fixing the individual sections.
  1738. //
  1739. //
  1740. // [Unattended] section.
  1741. // ---------------------
  1742. //
  1743. //
  1744. // NOTE WELL:
  1745. //
  1746. // ====================================
  1747. // Make sure to put all the code that actually prompts the user
  1748. // right up here at the top of the function. It's possible that
  1749. // some of these keys might already exist, so we'll proceed through
  1750. // some before stopping for a user prompt. We want to make
  1751. // sure to get ALL the user input before partying on their
  1752. // unattend file. That way, we never half-way process the unattend
  1753. // file, then wait and give the user a chance to dismiss the dialog
  1754. // on the local console. Thus, leaving a 1/2-baked unattend file.
  1755. //
  1756. // Don't put any code that actually sets any of the unattend
  1757. // settings before going and prompting for the EULA, PID
  1758. // and Administrator password!!
  1759. // ====================================
  1760. //
  1761. //
  1762. // OemSkipEula
  1763. //
  1764. NeedEula = TRUE;
  1765. if( ReturnCode == ERROR_SUCCESS ) {
  1766. Answer[0] = TEXT('\0');
  1767. Result = GetPrivateProfileString( WINNT_UNATTENDED,
  1768. L"OemSkipEula",
  1769. L"No",
  1770. Answer,
  1771. MAX_PATH,
  1772. AnswerFilePath );
  1773. if( !_wcsicmp(Answer, L"Yes") ) {
  1774. //
  1775. // They won't get prompted for a EULA. Make
  1776. // sure we don't present here.
  1777. //
  1778. NeedEula = FALSE;
  1779. }
  1780. }
  1781. //
  1782. // EulaComplete
  1783. //
  1784. if( ReturnCode == ERROR_SUCCESS ) {
  1785. Answer[0] = TEXT('\0');
  1786. Result = GetPrivateProfileString( WINNT_DATA,
  1787. WINNT_D_EULADONE,
  1788. L"",
  1789. Answer,
  1790. MAX_PATH,
  1791. AnswerFilePath );
  1792. if( (Result != 0) &&
  1793. (!gMiniSetup) ) {
  1794. //
  1795. // EulaDone is there, and this isn't minisetup.
  1796. // That means they've already accepted the EULA
  1797. // and it won't be presented during guimode setup.
  1798. // That also means we don't need to present it here.
  1799. //
  1800. NeedEula = FALSE;
  1801. }
  1802. }
  1803. //
  1804. // If we need to present the EULA, now would be a good
  1805. // time.
  1806. //
  1807. if( ReturnCode == ERROR_SUCCESS ) {
  1808. if( NeedEula ) {
  1809. if( FixUnattendFile ) {
  1810. if( PresentEula(hCancelEvent) ) {
  1811. //
  1812. // They read and accepted the EULA. Set a key
  1813. // so they won't get prompted during guimode setup.
  1814. //
  1815. b = WritePrivateProfileString( WINNT_DATA,
  1816. WINNT_D_EULADONE,
  1817. L"1",
  1818. AnswerFilePath );
  1819. if( OEMPreinstall || gMiniSetup ) {
  1820. //
  1821. // This is the only way to make the EULA go away
  1822. // if it's a preinstall.
  1823. //
  1824. b = b & WritePrivateProfileString( WINNT_UNATTENDED,
  1825. L"OemSkipEula",
  1826. L"yes",
  1827. AnswerFilePath );
  1828. }
  1829. if( !b ) {
  1830. //
  1831. // Remember the error and keep going.
  1832. //
  1833. ReturnCode = GetLastError();
  1834. }
  1835. } else {
  1836. //
  1837. // See if they rejected the EULA, or if our UI got
  1838. // cancelled via the local console.
  1839. //
  1840. if( IsAsyncCancelSignalled(hCancelEvent) ) {
  1841. ReturnCode = ERROR_CANCELLED;
  1842. } else {
  1843. ReturnCode = ERROR_CANCELLED;
  1844. gRejectedEula = TRUE;
  1845. }
  1846. }
  1847. } else {
  1848. //
  1849. // Tell someone there's work to do here.
  1850. //
  1851. if (NeedsProcessing) {
  1852. *NeedsProcessing = TRUE;
  1853. }
  1854. }
  1855. }
  1856. }
  1857. //
  1858. // ProductKey
  1859. //
  1860. if( ReturnCode == ERROR_SUCCESS ) {
  1861. Answer[0] = TEXT('\0');
  1862. Result = GetPrivateProfileString( WINNT_USERDATA,
  1863. WINNT_US_PRODUCTKEY,
  1864. L"",
  1865. Answer,
  1866. MAX_PATH,
  1867. AnswerFilePath );
  1868. //
  1869. // If they gave us something (anything), then assume
  1870. // it's okay. We're not in the business of trying to
  1871. // fix their PID in the answer file.
  1872. //
  1873. if( !_wcsicmp(Answer, L"") ) {
  1874. //
  1875. // Either they don't have a PID, or it's
  1876. // a bad PID. Go get a new one.
  1877. //
  1878. if( FixUnattendFile ) {
  1879. Answer[0] = TEXT('\0');
  1880. b = GetPid( Answer, MAX_PATH * sizeof(WCHAR), hCancelEvent );
  1881. //
  1882. // GetPid will only come back when he's got
  1883. // a PID. Write it into the unattend file.
  1884. //
  1885. if( b ) {
  1886. b = WritePrivateProfileString( WINNT_USERDATA,
  1887. WINNT_US_PRODUCTKEY,
  1888. Answer,
  1889. AnswerFilePath );
  1890. if( !b ) {
  1891. //
  1892. // Remember the error and keep going.
  1893. //
  1894. ReturnCode = GetLastError();
  1895. }
  1896. } else {
  1897. //
  1898. // GetPid shouldn't have come back unless we got
  1899. // a valid PID or if we got cancelled via the local
  1900. // console. Either way, just set a cancelled
  1901. // code.
  1902. //
  1903. ReturnCode = ERROR_CANCELLED;
  1904. }
  1905. } else {
  1906. //
  1907. // Tell someone there's work to do here.
  1908. //
  1909. if (NeedsProcessing) {
  1910. *NeedsProcessing = TRUE;
  1911. }
  1912. }
  1913. }
  1914. }
  1915. //
  1916. // AdminPassword
  1917. //
  1918. if( ReturnCode == ERROR_SUCCESS ) {
  1919. Answer[0] = TEXT('\0');
  1920. Result = GetPrivateProfileString( WINNT_GUIUNATTENDED,
  1921. WINNT_US_ADMINPASS,
  1922. L"",
  1923. Answer,
  1924. MAX_PATH,
  1925. AnswerFilePath );
  1926. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  1927. //
  1928. // They don't have a AdminPassword.
  1929. //
  1930. // Maybe need to go prompt for a password!
  1931. //
  1932. if( FixUnattendFile ) {
  1933. b = PromptForPassword( Answer, MAX_PATH * sizeof(WCHAR), hCancelEvent );
  1934. if( b ) {
  1935. b = WritePrivateProfileString( WINNT_GUIUNATTENDED,
  1936. WINNT_US_ADMINPASS,
  1937. Answer,
  1938. AnswerFilePath );
  1939. if( !b ) {
  1940. //
  1941. // Remember the error and keep going.
  1942. //
  1943. ReturnCode = GetLastError();
  1944. }
  1945. } else {
  1946. //
  1947. // We should never come back from PromptForPassword
  1948. // unless we got cancelled from the UI on the local
  1949. // console. Set a cancelled status.
  1950. //
  1951. ReturnCode = ERROR_CANCELLED;
  1952. }
  1953. } else {
  1954. //
  1955. // Tell someone there's work to do here.
  1956. //
  1957. if (NeedsProcessing) {
  1958. *NeedsProcessing = TRUE;
  1959. }
  1960. }
  1961. }
  1962. }
  1963. //
  1964. // ====================================
  1965. // If we get here, then it's okay to start actually modifying their
  1966. // unattend file. From here on out, there should be NO need to
  1967. // ask the user anything.
  1968. // ====================================
  1969. //
  1970. //
  1971. // If we've made it here, and if FixUnattendFile is set,
  1972. // then they've accepted the EULA through the headless port
  1973. // and we're about to start partying on their unattend file.
  1974. // We need to set an entry in their unattend file that tells
  1975. // support folks that they we stepped on their unattend file.
  1976. //
  1977. if( (ReturnCode == ERROR_SUCCESS) &&
  1978. (FixUnattendFile) ) {
  1979. WritePrivateProfileString( WINNT_DATA,
  1980. L"EMSGeneratedAnswers",
  1981. L"1",
  1982. AnswerFilePath );
  1983. }
  1984. //
  1985. // Unattendmode
  1986. //
  1987. if( ReturnCode == ERROR_SUCCESS ) {
  1988. Answer[0] = TEXT('\0');
  1989. Result = GetPrivateProfileString( WINNT_UNATTENDED,
  1990. WINNT_U_UNATTENDMODE,
  1991. L"",
  1992. Answer,
  1993. MAX_PATH,
  1994. AnswerFilePath );
  1995. if( _wcsicmp(Answer, WINNT_A_FULLUNATTENDED) ) {
  1996. //
  1997. // They're not running fully unattended.
  1998. // Set it.
  1999. //
  2000. if( FixUnattendFile ) {
  2001. b = WritePrivateProfileString( WINNT_UNATTENDED,
  2002. WINNT_U_UNATTENDMODE,
  2003. WINNT_A_FULLUNATTENDED,
  2004. AnswerFilePath );
  2005. if( !b ) {
  2006. //
  2007. // Remember the error and keep going.
  2008. //
  2009. ReturnCode = GetLastError();
  2010. }
  2011. } else {
  2012. //
  2013. // Tell someone there's work to do here.
  2014. //
  2015. //
  2016. // Tell someone there's work to do here.
  2017. //
  2018. if (NeedsProcessing) {
  2019. *NeedsProcessing = TRUE;
  2020. }
  2021. }
  2022. }
  2023. }
  2024. //
  2025. // [Data] section
  2026. // --------------
  2027. //
  2028. //
  2029. // unattendedinstall
  2030. //
  2031. if( ReturnCode == ERROR_SUCCESS ) {
  2032. Answer[0] = TEXT('\0');
  2033. Result = GetPrivateProfileString( WINNT_DATA,
  2034. WINNT_D_INSTALL,
  2035. L"",
  2036. Answer,
  2037. MAX_PATH,
  2038. AnswerFilePath );
  2039. if( (Result == 0) || (_wcsicmp(Answer, L"yes")) ) {
  2040. //
  2041. // They don't have the super-double-secret key
  2042. // that tells guimode to go fully unattended.
  2043. //
  2044. if( FixUnattendFile ) {
  2045. b = WritePrivateProfileString( WINNT_DATA,
  2046. WINNT_D_INSTALL,
  2047. L"yes",
  2048. AnswerFilePath );
  2049. if( !b ) {
  2050. //
  2051. // Remember the error and keep going.
  2052. //
  2053. ReturnCode = GetLastError();
  2054. }
  2055. } else {
  2056. //
  2057. // Tell someone there's work to do here.
  2058. //
  2059. if (NeedsProcessing) {
  2060. *NeedsProcessing = TRUE;
  2061. }
  2062. }
  2063. }
  2064. }
  2065. //
  2066. // msdosinitiated
  2067. //
  2068. if( ReturnCode == ERROR_SUCCESS ) {
  2069. Answer[0] = TEXT('\0');
  2070. Result = GetPrivateProfileString( WINNT_DATA,
  2071. WINNT_D_MSDOS,
  2072. L"",
  2073. Answer,
  2074. MAX_PATH,
  2075. AnswerFilePath );
  2076. if( (Result == 0) || (_wcsicmp(Answer, L"1")) ) {
  2077. if( FixUnattendFile ) {
  2078. //
  2079. // They'll get the welcome screen without this.
  2080. //
  2081. b = WritePrivateProfileString( WINNT_DATA,
  2082. WINNT_D_MSDOS,
  2083. L"1",
  2084. AnswerFilePath );
  2085. if( !b ) {
  2086. //
  2087. // Remember the error and keep going.
  2088. //
  2089. ReturnCode = GetLastError();
  2090. }
  2091. } else {
  2092. //
  2093. // Tell someone there's work to do here.
  2094. //
  2095. if (NeedsProcessing) {
  2096. *NeedsProcessing = TRUE;
  2097. }
  2098. }
  2099. }
  2100. }
  2101. //
  2102. // floppyless
  2103. //
  2104. if( ReturnCode == ERROR_SUCCESS ) {
  2105. Answer[0] = TEXT('\0');
  2106. Result = GetPrivateProfileString( WINNT_DATA,
  2107. WINNT_D_FLOPPY,
  2108. L"",
  2109. Answer,
  2110. MAX_PATH,
  2111. AnswerFilePath );
  2112. if( (Result == 0) || (_wcsicmp(Answer, L"1")) ) {
  2113. if( FixUnattendFile ) {
  2114. //
  2115. // They'll get the welcome screen without this.
  2116. //
  2117. b = WritePrivateProfileString( WINNT_DATA,
  2118. WINNT_D_FLOPPY,
  2119. L"1",
  2120. AnswerFilePath );
  2121. if( !b ) {
  2122. //
  2123. // Remember the error and keep going.
  2124. //
  2125. ReturnCode = GetLastError();
  2126. }
  2127. } else {
  2128. //
  2129. // Tell someone there's work to do here.
  2130. //
  2131. if (NeedsProcessing) {
  2132. *NeedsProcessing = TRUE;
  2133. }
  2134. }
  2135. }
  2136. }
  2137. //
  2138. // skipmissingfiles
  2139. //
  2140. if( ReturnCode == ERROR_SUCCESS ) {
  2141. Answer[0] = TEXT('\0');
  2142. Result = GetPrivateProfileString( WINNT_SETUPPARAMS,
  2143. WINNT_S_SKIPMISSING,
  2144. L"",
  2145. Answer,
  2146. MAX_PATH,
  2147. AnswerFilePath );
  2148. if( (Result == 0) || (_wcsicmp(Answer, L"1")) ) {
  2149. //
  2150. // They might get prompted for missing files w/o this setting.
  2151. //
  2152. if( FixUnattendFile ) {
  2153. b = WritePrivateProfileString( WINNT_SETUPPARAMS,
  2154. WINNT_S_SKIPMISSING,
  2155. L"1",
  2156. AnswerFilePath );
  2157. if( !b ) {
  2158. //
  2159. // Remember the error and keep going.
  2160. //
  2161. ReturnCode = GetLastError();
  2162. }
  2163. } else {
  2164. //
  2165. // Tell someone there's work to do here.
  2166. //
  2167. if (NeedsProcessing) {
  2168. *NeedsProcessing = TRUE;
  2169. }
  2170. }
  2171. }
  2172. }
  2173. //
  2174. // [UserData] section
  2175. // ------------------
  2176. //
  2177. //
  2178. // FullName
  2179. //
  2180. if( ReturnCode == ERROR_SUCCESS ) {
  2181. Answer[0] = TEXT('\0');
  2182. Result = GetPrivateProfileString( WINNT_USERDATA,
  2183. WINNT_US_FULLNAME,
  2184. L"",
  2185. Answer,
  2186. MAX_PATH,
  2187. AnswerFilePath );
  2188. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2189. //
  2190. // They don't have a name.
  2191. //
  2192. if( FixUnattendFile ) {
  2193. b = WritePrivateProfileString( WINNT_USERDATA,
  2194. WINNT_US_FULLNAME,
  2195. L"UserName",
  2196. AnswerFilePath );
  2197. if( !b ) {
  2198. //
  2199. // Remember the error and keep going.
  2200. //
  2201. ReturnCode = GetLastError();
  2202. }
  2203. } else {
  2204. //
  2205. // Tell someone there's work to do here.
  2206. //
  2207. if (NeedsProcessing) {
  2208. *NeedsProcessing = TRUE;
  2209. }
  2210. }
  2211. }
  2212. }
  2213. //
  2214. // OrgName
  2215. //
  2216. if( ReturnCode == ERROR_SUCCESS ) {
  2217. Answer[0] = TEXT('\0');
  2218. Result = GetPrivateProfileString( WINNT_USERDATA,
  2219. WINNT_US_ORGNAME,
  2220. L"",
  2221. Answer,
  2222. MAX_PATH,
  2223. AnswerFilePath );
  2224. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2225. //
  2226. // They don't have a name.
  2227. //
  2228. if( FixUnattendFile ) {
  2229. b = WritePrivateProfileString( WINNT_USERDATA,
  2230. WINNT_US_ORGNAME,
  2231. L"OrganizationName",
  2232. AnswerFilePath );
  2233. if( !b ) {
  2234. //
  2235. // Remember the error and keep going.
  2236. //
  2237. ReturnCode = GetLastError();
  2238. }
  2239. } else {
  2240. //
  2241. // Tell someone there's work to do here.
  2242. //
  2243. if (NeedsProcessing) {
  2244. *NeedsProcessing = TRUE;
  2245. }
  2246. }
  2247. }
  2248. }
  2249. //
  2250. // ComputerName
  2251. //
  2252. if( ReturnCode == ERROR_SUCCESS ) {
  2253. Answer[0] = TEXT('\0');
  2254. Result = GetPrivateProfileString( WINNT_USERDATA,
  2255. WINNT_US_COMPNAME,
  2256. L"",
  2257. Answer,
  2258. MAX_PATH,
  2259. AnswerFilePath );
  2260. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2261. //
  2262. // They don't have a ComputerName.
  2263. //
  2264. if( FixUnattendFile ) {
  2265. b = WritePrivateProfileString( WINNT_USERDATA,
  2266. WINNT_US_COMPNAME,
  2267. L"*",
  2268. AnswerFilePath );
  2269. if( !b ) {
  2270. //
  2271. // Remember the error and keep going.
  2272. //
  2273. ReturnCode = GetLastError();
  2274. }
  2275. } else {
  2276. //
  2277. // Tell someone there's work to do here.
  2278. //
  2279. if (NeedsProcessing) {
  2280. *NeedsProcessing = TRUE;
  2281. }
  2282. }
  2283. }
  2284. }
  2285. //
  2286. // [GuiUnattended] section
  2287. // -----------------------
  2288. //
  2289. //
  2290. // TimeZone
  2291. //
  2292. if( ReturnCode == ERROR_SUCCESS ) {
  2293. Answer[0] = TEXT('\0');
  2294. Result = GetPrivateProfileString( WINNT_GUIUNATTENDED,
  2295. WINNT_G_TIMEZONE,
  2296. L"",
  2297. Answer,
  2298. MAX_PATH,
  2299. AnswerFilePath );
  2300. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2301. //
  2302. // They don't have a TimeZone.
  2303. //
  2304. if( FixUnattendFile ) {
  2305. b = WritePrivateProfileString( WINNT_GUIUNATTENDED,
  2306. WINNT_G_TIMEZONE,
  2307. L"004",
  2308. AnswerFilePath );
  2309. if( !b ) {
  2310. //
  2311. // Remember the error and keep going.
  2312. //
  2313. ReturnCode = GetLastError();
  2314. }
  2315. } else {
  2316. //
  2317. // Tell someone there's work to do here.
  2318. //
  2319. if (NeedsProcessing) {
  2320. *NeedsProcessing = TRUE;
  2321. }
  2322. }
  2323. }
  2324. }
  2325. //
  2326. // OEMSkipWelcome
  2327. //
  2328. //
  2329. // If this is a preinstall, or a sysprep/MiniSetup, then
  2330. // they're going to get hit with the welcome screen no
  2331. // matter the unattend mode. If either of those are
  2332. // true, then we need to set this key.
  2333. //
  2334. if( ReturnCode == ERROR_SUCCESS ) {
  2335. if( OEMPreinstall || gMiniSetup ) {
  2336. //
  2337. // It's a preinstall, or it's a sysprep, which means
  2338. // it will be interpreted as a preinstall. Make sure
  2339. // they've got the skip welcome set or guimode will
  2340. // halt on the welcome page.
  2341. //
  2342. Answer[0] = TEXT('\0');
  2343. Result = GetPrivateProfileString( WINNT_GUIUNATTENDED,
  2344. L"OEMSkipWelcome",
  2345. L"",
  2346. Answer,
  2347. MAX_PATH,
  2348. AnswerFilePath );
  2349. if( (Result == 0) || (_wcsicmp(Answer, L"1")) ) {
  2350. //
  2351. // He's going to hit the welcome screen.
  2352. //
  2353. if( FixUnattendFile ) {
  2354. b = WritePrivateProfileString( WINNT_GUIUNATTENDED,
  2355. L"OEMSkipWelcome",
  2356. L"1",
  2357. AnswerFilePath );
  2358. if( !b ) {
  2359. //
  2360. // Remember the error and keep going.
  2361. //
  2362. ReturnCode = GetLastError();
  2363. }
  2364. } else {
  2365. //
  2366. // Tell someone there's work to do here.
  2367. //
  2368. if (NeedsProcessing) {
  2369. *NeedsProcessing = TRUE;
  2370. }
  2371. }
  2372. }
  2373. }
  2374. }
  2375. //
  2376. // OemSkipRegional
  2377. //
  2378. if( ReturnCode == ERROR_SUCCESS ) {
  2379. if( OEMPreinstall || gMiniSetup ) {
  2380. //
  2381. // It's a preinstall, or it's a sysprep, which means
  2382. // it will be interpreted as a preinstall. Make sure
  2383. // they've got the skip regional set or guimode will
  2384. // halt on the regional settings page.
  2385. //
  2386. Answer[0] = TEXT('\0');
  2387. Result = GetPrivateProfileString( WINNT_GUIUNATTENDED,
  2388. L"OEMSkipRegional",
  2389. L"",
  2390. Answer,
  2391. MAX_PATH,
  2392. AnswerFilePath );
  2393. if( (Result == 0) || (_wcsicmp(Answer, L"1")) ) {
  2394. //
  2395. // He's going to hit the welcome screen.
  2396. //
  2397. if( FixUnattendFile ) {
  2398. b = WritePrivateProfileString( WINNT_GUIUNATTENDED,
  2399. L"OEMSkipRegional",
  2400. L"1",
  2401. AnswerFilePath );
  2402. if( !b ) {
  2403. //
  2404. // Remember the error and keep going.
  2405. //
  2406. ReturnCode = GetLastError();
  2407. }
  2408. } else {
  2409. //
  2410. // Tell someone there's work to do here.
  2411. //
  2412. if (NeedsProcessing) {
  2413. *NeedsProcessing = TRUE;
  2414. }
  2415. }
  2416. }
  2417. }
  2418. }
  2419. //
  2420. // [LicenseFilePrintData] section
  2421. // ------------------------------
  2422. //
  2423. //
  2424. // AutoMode
  2425. //
  2426. if( ReturnCode == ERROR_SUCCESS ) {
  2427. Answer[0] = TEXT('\0');
  2428. Result = GetPrivateProfileString( WINNT_LICENSEDATA,
  2429. WINNT_L_AUTOMODE,
  2430. L"",
  2431. Answer,
  2432. MAX_PATH,
  2433. AnswerFilePath );
  2434. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2435. //
  2436. // They don't have a TimeZone.
  2437. //
  2438. if( FixUnattendFile ) {
  2439. b = WritePrivateProfileString( WINNT_LICENSEDATA,
  2440. WINNT_L_AUTOMODE,
  2441. L"PerServer",
  2442. AnswerFilePath );
  2443. if( !b ) {
  2444. //
  2445. // Remember the error and keep going.
  2446. //
  2447. ReturnCode = GetLastError();
  2448. }
  2449. } else {
  2450. //
  2451. // Tell someone there's work to do here.
  2452. //
  2453. if (NeedsProcessing) {
  2454. *NeedsProcessing = TRUE;
  2455. }
  2456. }
  2457. }
  2458. }
  2459. //
  2460. // AutoUsers
  2461. //
  2462. if( ReturnCode == ERROR_SUCCESS ) {
  2463. Answer[0] = TEXT('\0');
  2464. Result = GetPrivateProfileString( WINNT_LICENSEDATA,
  2465. WINNT_L_AUTOUSERS,
  2466. L"",
  2467. Answer,
  2468. MAX_PATH,
  2469. AnswerFilePath );
  2470. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2471. //
  2472. // They don't have a TimeZone.
  2473. //
  2474. if( FixUnattendFile ) {
  2475. b = WritePrivateProfileString( WINNT_LICENSEDATA,
  2476. WINNT_L_AUTOUSERS,
  2477. L"5",
  2478. AnswerFilePath );
  2479. if( !b ) {
  2480. //
  2481. // Remember the error and keep going.
  2482. //
  2483. ReturnCode = GetLastError();
  2484. }
  2485. } else {
  2486. //
  2487. // Tell someone there's work to do here.
  2488. //
  2489. if (NeedsProcessing) {
  2490. *NeedsProcessing = TRUE;
  2491. }
  2492. }
  2493. }
  2494. }
  2495. //
  2496. // [Display] section
  2497. // -----------------
  2498. //
  2499. //
  2500. // BitsPerPel
  2501. //
  2502. if( ReturnCode == ERROR_SUCCESS ) {
  2503. Answer[0] = TEXT('\0');
  2504. Result = GetPrivateProfileString( WINNT_DISPLAY,
  2505. WINNT_DISP_BITSPERPEL,
  2506. L"",
  2507. Answer,
  2508. MAX_PATH,
  2509. AnswerFilePath );
  2510. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2511. //
  2512. // They don't have a TimeZone.
  2513. //
  2514. if( FixUnattendFile ) {
  2515. b = WritePrivateProfileString( WINNT_DISPLAY,
  2516. WINNT_DISP_BITSPERPEL,
  2517. L"16",
  2518. AnswerFilePath );
  2519. if( !b ) {
  2520. //
  2521. // Remember the error and keep going.
  2522. //
  2523. ReturnCode = GetLastError();
  2524. }
  2525. } else {
  2526. //
  2527. // Tell someone there's work to do here.
  2528. //
  2529. if (NeedsProcessing) {
  2530. *NeedsProcessing = TRUE;
  2531. }
  2532. }
  2533. }
  2534. }
  2535. //
  2536. // XResolution
  2537. //
  2538. if( ReturnCode == ERROR_SUCCESS ) {
  2539. Answer[0] = TEXT('\0');
  2540. Result = GetPrivateProfileString( WINNT_DISPLAY,
  2541. WINNT_DISP_XRESOLUTION,
  2542. L"",
  2543. Answer,
  2544. MAX_PATH,
  2545. AnswerFilePath );
  2546. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2547. //
  2548. // They don't have a TimeZone.
  2549. //
  2550. if( FixUnattendFile ) {
  2551. b = WritePrivateProfileString( WINNT_DISPLAY,
  2552. WINNT_DISP_XRESOLUTION,
  2553. L"800",
  2554. AnswerFilePath );
  2555. if( !b ) {
  2556. //
  2557. // Remember the error and keep going.
  2558. //
  2559. ReturnCode = GetLastError();
  2560. }
  2561. } else {
  2562. //
  2563. // Tell someone there's work to do here.
  2564. //
  2565. if (NeedsProcessing) {
  2566. *NeedsProcessing = TRUE;
  2567. }
  2568. }
  2569. }
  2570. }
  2571. //
  2572. // YResolution
  2573. //
  2574. if( ReturnCode == ERROR_SUCCESS ) {
  2575. Answer[0] = TEXT('\0');
  2576. Result = GetPrivateProfileString( WINNT_DISPLAY,
  2577. WINNT_DISP_YRESOLUTION,
  2578. L"",
  2579. Answer,
  2580. MAX_PATH,
  2581. AnswerFilePath );
  2582. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2583. //
  2584. // They don't have a TimeZone.
  2585. //
  2586. if( FixUnattendFile ) {
  2587. b = WritePrivateProfileString( WINNT_DISPLAY,
  2588. WINNT_DISP_YRESOLUTION,
  2589. L"600",
  2590. AnswerFilePath );
  2591. if( !b ) {
  2592. //
  2593. // Remember the error and keep going.
  2594. //
  2595. ReturnCode = GetLastError();
  2596. }
  2597. } else {
  2598. //
  2599. // Tell someone there's work to do here.
  2600. //
  2601. if (NeedsProcessing) {
  2602. *NeedsProcessing = TRUE;
  2603. }
  2604. }
  2605. }
  2606. }
  2607. //
  2608. // VRefresh
  2609. //
  2610. if( ReturnCode == ERROR_SUCCESS ) {
  2611. Answer[0] = TEXT('\0');
  2612. Result = GetPrivateProfileString( WINNT_DISPLAY,
  2613. WINNT_DISP_VREFRESH,
  2614. L"",
  2615. Answer,
  2616. MAX_PATH,
  2617. AnswerFilePath );
  2618. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2619. //
  2620. // They don't have a TimeZone.
  2621. //
  2622. if( FixUnattendFile ) {
  2623. b = WritePrivateProfileString( WINNT_DISPLAY,
  2624. WINNT_DISP_VREFRESH,
  2625. L"70",
  2626. AnswerFilePath );
  2627. if( !b ) {
  2628. //
  2629. // Remember the error and keep going.
  2630. //
  2631. ReturnCode = GetLastError();
  2632. }
  2633. } else {
  2634. //
  2635. // Tell someone there's work to do here.
  2636. //
  2637. if (NeedsProcessing) {
  2638. *NeedsProcessing = TRUE;
  2639. }
  2640. }
  2641. }
  2642. }
  2643. //
  2644. // [Identification] section
  2645. // ------------------------
  2646. //
  2647. //
  2648. // JoinWorkgroup
  2649. //
  2650. if( ReturnCode == ERROR_SUCCESS ) {
  2651. Answer[0] = TEXT('\0');
  2652. Result = GetPrivateProfileString( L"Identification",
  2653. L"JoinWorkgroup",
  2654. L"",
  2655. Answer,
  2656. MAX_PATH,
  2657. AnswerFilePath );
  2658. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2659. //
  2660. // They don't have a JoinWorkgroup. See if they've
  2661. // got JoinDomain?
  2662. //
  2663. Answer[0] = TEXT('\0');
  2664. Result = GetPrivateProfileString( L"Identification",
  2665. L"JoinDomain",
  2666. L"",
  2667. Answer,
  2668. MAX_PATH,
  2669. AnswerFilePath );
  2670. if( (Result == 0) || (!_wcsicmp(Answer, L"")) ) {
  2671. //
  2672. // Nope. Go plug in a JoinWorkgroup entry. That
  2673. // way we don't need to prompt/specify which domain to join.
  2674. //
  2675. if( FixUnattendFile ) {
  2676. b = WritePrivateProfileString( L"Identification",
  2677. L"JoinWorkgroup",
  2678. L"Workgroup",
  2679. AnswerFilePath );
  2680. if( !b ) {
  2681. //
  2682. // Remember the error and keep going.
  2683. //
  2684. ReturnCode = GetLastError();
  2685. }
  2686. } else {
  2687. //
  2688. // Tell someone there's work to do here.
  2689. //
  2690. if (NeedsProcessing) {
  2691. *NeedsProcessing = TRUE;
  2692. }
  2693. }
  2694. }
  2695. }
  2696. }
  2697. //
  2698. // [Networking]
  2699. //
  2700. //
  2701. // This section must at least exist. If it doesn't, we'll
  2702. // get prompted during network configuration. Make sure it's
  2703. // at least there.
  2704. //
  2705. if( ReturnCode == ERROR_SUCCESS ) {
  2706. Answer[0] = TEXT('\0');
  2707. Result = GetPrivateProfileSection( L"Networking",
  2708. Answer,
  2709. MAX_PATH,
  2710. AnswerFilePath );
  2711. if( Result == 0 ) {
  2712. //
  2713. // They don't have a [Networking] section.
  2714. //
  2715. if( FixUnattendFile ) {
  2716. b = WritePrivateProfileString( L"Networking",
  2717. L"unused",
  2718. L"0",
  2719. AnswerFilePath );
  2720. if( !b ) {
  2721. //
  2722. // Remember the error and keep going.
  2723. //
  2724. ReturnCode = GetLastError();
  2725. }
  2726. } else {
  2727. //
  2728. // Tell someone there's work to do here.
  2729. //
  2730. if (NeedsProcessing) {
  2731. *NeedsProcessing = TRUE;
  2732. }
  2733. }
  2734. }
  2735. }
  2736. //
  2737. // We're done.
  2738. //
  2739. //
  2740. // If we've just successfully fixed up their unattend file, give them
  2741. // a little notification here.
  2742. //
  2743. if( (FixUnattendFile) && (ReturnCode == ERROR_SUCCESS) ) {
  2744. //
  2745. // Clear the screen.
  2746. //
  2747. ClearEMSScreen();
  2748. //
  2749. // Write some instructions/information, then pause
  2750. // before proceeding.
  2751. //
  2752. WriteResourceMessage( IDS_UNATTEND_FIXUP_DONE );
  2753. //
  2754. // wait...
  2755. //
  2756. Sleep( 5 * 1000);
  2757. }
  2758. return( ReturnCode );
  2759. }
  2760. extern "C"
  2761. BOOL
  2762. CheckEMS(
  2763. IN int argc,
  2764. WCHAR *argvW[]
  2765. )
  2766. /*++
  2767. Routine Description:
  2768. main entrypoint to code.
  2769. Arguments:
  2770. argc - number of args
  2771. argvW - array of args.
  2772. Return Value:
  2773. Win32 Error code indicating outcome. FALSE means we had a problem.
  2774. --*/
  2775. {
  2776. UserInputParams Params,ParamsDialog;
  2777. DWORD ThreadId;
  2778. HANDLE Handles[2];
  2779. HANDLE hThreadHeadless = NULL,hThreadUI = NULL;
  2780. ULONG i = 0;
  2781. BOOL RetVal;
  2782. BOOL NeedsProcessing;
  2783. RtlZeroMemory(&Params,sizeof(Params));
  2784. RtlZeroMemory(&ParamsDialog,sizeof(ParamsDialog));
  2785. //
  2786. // Check if headless feature is present on this machine. If not, just
  2787. // run setup like normal.
  2788. //
  2789. //
  2790. // initialize our headless channel data which we'll soon need.
  2791. //
  2792. if (!InitializeGlobalChannelAttributes(&GlobalChannelAttributes)) {
  2793. RetVal = FALSE;
  2794. goto exit;
  2795. }
  2796. //
  2797. // Go see if headless is present and if so, create
  2798. // a channel.
  2799. //
  2800. if(!IsHeadlessPresent(&gEMSChannel)) {
  2801. //
  2802. // There's no work to do. Go run Setup.
  2803. //
  2804. RetVal = TRUE;
  2805. goto exit;
  2806. }
  2807. //
  2808. // See if we're running MiniSetup or base Guimode Setup.
  2809. //
  2810. for( i = 0; i < (ULONG)argc; i++ ) {
  2811. if( !_wcsnicmp(argvW[i], L"-mini", wcslen(L"-mini")) ) {
  2812. gMiniSetup = TRUE;
  2813. }
  2814. }
  2815. //
  2816. // Check if there is any work for us to do. If not, just run setup like
  2817. // normal.
  2818. //
  2819. if( ProcessUnattendFile(FALSE,&NeedsProcessing, NULL) != ERROR_SUCCESS ) {
  2820. //
  2821. // something catastrophic happened. exit.
  2822. //
  2823. RetVal = FALSE;
  2824. goto exit;
  2825. }
  2826. if( !NeedsProcessing) {
  2827. //
  2828. // There's no work to do. Go run Setup.
  2829. //
  2830. RetVal = TRUE;
  2831. goto exit;
  2832. }
  2833. //
  2834. // Create a pait of threads for getting data from the user via
  2835. // the headless port or the local UI
  2836. //
  2837. Params.Channel = gEMSChannel;
  2838. Params.hInputCompleteEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  2839. ParamsDialog.hInputCompleteEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  2840. Params.hRemoveUI = ParamsDialog.hRemoveUI = CreateEvent(NULL,TRUE,FALSE,NULL);
  2841. if (!Params.hInputCompleteEvent ||
  2842. !ParamsDialog.hInputCompleteEvent ||
  2843. !Params.hRemoveUI) {
  2844. RetVal = FALSE;
  2845. goto exit;
  2846. }
  2847. if (!(hThreadHeadless = CreateThread(
  2848. NULL,
  2849. 0,
  2850. &PromptForUserInputThreadOverHeadlessConnection,
  2851. &Params,
  2852. 0,
  2853. &ThreadId))) {
  2854. RetVal = FALSE;
  2855. goto exit;
  2856. }
  2857. if (!(hThreadUI = CreateThread(
  2858. NULL,
  2859. 0,
  2860. &PromptForUserInputThreadViaLocalDialog,
  2861. &ParamsDialog,
  2862. 0,
  2863. &ThreadId))) {
  2864. RetVal = FALSE;
  2865. goto exit;
  2866. }
  2867. //
  2868. // wait for either of our named events to fire off which signals that one of the threads is done.
  2869. //
  2870. Handles[0] = Params.hInputCompleteEvent;
  2871. Handles[1] = ParamsDialog.hInputCompleteEvent;
  2872. WaitForMultipleObjects(2,Handles,FALSE,INFINITE);
  2873. //
  2874. // set an event that signals that the other thread should terminate.
  2875. //
  2876. SetEvent(Params.hRemoveUI);
  2877. //
  2878. // now wait for both of the threads to terminate before proceeding.
  2879. //
  2880. Handles[0] = hThreadHeadless;
  2881. Handles[1] = hThreadUI;
  2882. WaitForMultipleObjects(2,Handles,TRUE,INFINITE);
  2883. RetVal = TRUE;
  2884. exit:
  2885. if (hThreadHeadless) {
  2886. CloseHandle(hThreadHeadless);
  2887. }
  2888. if (hThreadUI) {
  2889. CloseHandle(hThreadUI);
  2890. }
  2891. if (Params.hInputCompleteEvent) {
  2892. CloseHandle(Params.hInputCompleteEvent);
  2893. }
  2894. if (ParamsDialog.hInputCompleteEvent) {
  2895. CloseHandle(ParamsDialog.hInputCompleteEvent);
  2896. }
  2897. if (Params.hRemoveUI) {
  2898. CloseHandle(Params.hRemoveUI);
  2899. }
  2900. if (gEMSChannel) {
  2901. delete (gEMSChannel);
  2902. }
  2903. //
  2904. // Careful here. If they actually rejected the
  2905. // EULA through the headless port, then we want
  2906. // to terminate instead of firing off setup.
  2907. // We'll tell our caller about that by returning
  2908. // zero.
  2909. //
  2910. if( gRejectedEula || RetVal == FALSE ) {
  2911. return FALSE ;
  2912. } else {
  2913. return TRUE;
  2914. }
  2915. }