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

793 lines
19 KiB

  1. #include "setupp.h"
  2. #pragma hdrstop
  3. //
  4. // List of oem preinstall/unattend values we can fetch from profile files.
  5. // Do NOT change the order of this without changing the order of the
  6. // PreinstUnattendData array.
  7. //
  8. typedef enum {
  9. OemDatumBackgroundBitmap,
  10. OemDatumBannerText,
  11. OemDatumLogoBitmap,
  12. OemDatumMax
  13. } OemPreinstallDatum;
  14. //
  15. // Define structure that represents a single bit of data read
  16. // from a profile file relating to preinstallation.
  17. //
  18. typedef struct _PREINSTALL_UNATTEND_DATUM {
  19. //
  20. // Filename. If NULL, use the system answer file $winnt$.inf.
  21. // Otherwise this is relative to the root of the source drive.
  22. //
  23. PCWSTR Filename;
  24. //
  25. // Section name.
  26. //
  27. PCWSTR Section;
  28. //
  29. // Key name.
  30. //
  31. PCWSTR Key;
  32. //
  33. // Default value. Can be NULL but that will be translated to
  34. // "" when the profile API is called to retrieve the data.
  35. //
  36. PCWSTR Default;
  37. //
  38. // Where to put the actual value. The actual value may be
  39. // a string, or NULL.
  40. //
  41. PWSTR *Value;
  42. //
  43. // Value for sanity checking.
  44. //
  45. OemPreinstallDatum WhichValue;
  46. } PREINSTALL_UNATTEND_DATUM, *PPREINSTALL_UNATTEND_DATUM;
  47. //
  48. // Name of oem background bitmap file and logo bitmap file.
  49. // Replacement banner text.
  50. // Read from unattend.txt
  51. //
  52. PWSTR OemBackgroundBitmapFile;
  53. PWSTR OemLogoBitmapFile;
  54. PWSTR OemBannerText;
  55. //
  56. // If this is non-NULL, it is a replacement bitmap
  57. // to use for the background.
  58. //
  59. HBITMAP OemBackgroundBitmap;
  60. PREINSTALL_UNATTEND_DATUM PreinstUnattendData[OemDatumMax] = {
  61. //
  62. // Background bitmap
  63. //
  64. { NULL,
  65. WINNT_OEM_ADS,
  66. WINNT_OEM_ADS_BACKGROUND,
  67. NULL,
  68. &OemBackgroundBitmapFile,
  69. OemDatumBackgroundBitmap
  70. },
  71. //
  72. // Banner text
  73. //
  74. {
  75. NULL,
  76. WINNT_OEM_ADS,
  77. WINNT_OEM_ADS_BANNER,
  78. NULL,
  79. &OemBannerText,
  80. OemDatumBannerText
  81. },
  82. //
  83. // Logo bitmap
  84. //
  85. {
  86. NULL,
  87. WINNT_OEM_ADS,
  88. WINNT_OEM_ADS_LOGO,
  89. NULL,
  90. &OemLogoBitmapFile,
  91. OemDatumLogoBitmap
  92. }
  93. };
  94. //
  95. // Path to the registry key that contains the list of preinstalled
  96. // drivers (SCSI, keyboard and mouse)
  97. //
  98. PCWSTR szPreinstallKeyName = L"SYSTEM\\Setup\\Preinstall";
  99. //
  100. // Forward references
  101. //
  102. VOID
  103. ProcessOemBitmap(
  104. IN PWSTR FilenameAndResId,
  105. IN SetupBm WhichOne
  106. );
  107. BOOL
  108. CleanupPreinstalledComponents(
  109. );
  110. BOOL
  111. InitializePreinstall(
  112. VOID
  113. )
  114. {
  115. WCHAR Buffer[2*MAX_PATH];
  116. DWORD d;
  117. int i;
  118. //
  119. // Must be run after main initialization. We rely on stuff that is
  120. // set up there.
  121. //
  122. MYASSERT(AnswerFile[0]);
  123. //
  124. // Special skip-eula value. Note that we want this value even if we're not
  125. // doing a Preinstall.
  126. //
  127. GetPrivateProfileString(
  128. WINNT_UNATTENDED,
  129. L"OemSkipEula",
  130. pwNo,
  131. Buffer,
  132. sizeof(Buffer)/sizeof(Buffer[0]),
  133. AnswerFile
  134. );
  135. OemSkipEula = (lstrcmpi(Buffer,pwYes) == 0);
  136. //
  137. // For the mini-setup case, always be preinstall.
  138. //
  139. if( MiniSetup ) {
  140. Preinstall = TRUE;
  141. } else {
  142. MYASSERT(SourcePath[0]);
  143. //
  144. // First, figure out whether this is an OEM preinstallation.
  145. //
  146. GetPrivateProfileString(
  147. WINNT_UNATTENDED,
  148. WINNT_OEMPREINSTALL,
  149. pwNo,
  150. Buffer,
  151. sizeof(Buffer)/sizeof(Buffer[0]),
  152. AnswerFile
  153. );
  154. Preinstall = (lstrcmpi(Buffer,pwYes) == 0);
  155. //
  156. // If not preinstallation, nothing more to do.
  157. //
  158. if(!Preinstall) {
  159. return(TRUE);
  160. }
  161. }
  162. //
  163. // OK, it's preinstsall. Fill in our data table.
  164. //
  165. for(i=0; i<OemDatumMax; i++) {
  166. //
  167. // Sanity check
  168. //
  169. MYASSERT(PreinstUnattendData[i].WhichValue == i);
  170. //
  171. // Retrieve data and duplicate. If the value comes back as ""
  172. // assume it means there is no value in there.
  173. //
  174. GetPrivateProfileString(
  175. PreinstUnattendData[i].Section,
  176. PreinstUnattendData[i].Key,
  177. PreinstUnattendData[i].Default ? PreinstUnattendData[i].Default : NULL,
  178. Buffer,
  179. sizeof(Buffer)/sizeof(Buffer[0]),
  180. PreinstUnattendData[i].Filename ? PreinstUnattendData[i].Filename : AnswerFile
  181. );
  182. *PreinstUnattendData[i].Value = Buffer[0] ? pSetupDuplicateString(Buffer) : NULL;
  183. if(Buffer[0] && (*PreinstUnattendData[i].Value == NULL)) {
  184. //
  185. // Out of memory.
  186. //
  187. pSetupOutOfMemory(MainWindowHandle);
  188. return(FALSE);
  189. }
  190. }
  191. //
  192. // Change the banner text, if the OEM supplied new text.
  193. // Make sure our product name is in there.
  194. //
  195. if(OemBannerText) {
  196. if(wcsstr(OemBannerText,L"Windows NT") ||
  197. wcsstr(OemBannerText,L"BackOffice")) {
  198. //
  199. // Substitute * with \n
  200. //
  201. for(i=0; OemBannerText[i]; i++) {
  202. if(OemBannerText[i] == L'*') {
  203. OemBannerText[i] = L'\n';
  204. }
  205. }
  206. #if 0
  207. //
  208. // Disable the banner for now.
  209. //
  210. SendMessage(MainWindowHandle,WM_NEWBITMAP,SetupBmBanner,(LPARAM)OemBannerText);
  211. #endif
  212. } else {
  213. MyFree(OemBannerText);
  214. OemBannerText = NULL;
  215. }
  216. }
  217. //
  218. // Load the OEM background bitmap, if any.
  219. // Load the OEM logo bitmap, if any.
  220. //
  221. ProcessOemBitmap(OemBackgroundBitmapFile,SetupBmBackground);
  222. ProcessOemBitmap(OemLogoBitmapFile,SetupBmLogo);
  223. //
  224. // Repaint the main window. Specify that the background should be erased
  225. // because the main window relies on this behavior.
  226. //
  227. InvalidateRect(MainWindowHandle,NULL,TRUE);
  228. UpdateWindow(MainWindowHandle);
  229. CleanupPreinstalledComponents();
  230. return(TRUE);
  231. }
  232. VOID
  233. ProcessOemBitmap(
  234. IN PWSTR FilenameAndResId,
  235. IN SetupBm WhichOne
  236. )
  237. /*++
  238. Routine Description:
  239. This routine processes a single oem bitmap specification.
  240. The OEM bitmap may either be in a resource file or in a standalone
  241. bitmap file. Once the bitmap has been loaded the main window
  242. is told about it.
  243. Arguments:
  244. FileNameAndResId - specifies a profile string with either one
  245. or 2 fields. If the string contains a comma, it is assumed to be
  246. the name of a DLL followed by a resource ID. The dll is loaded
  247. from the $OEM$\OEMFILES directory and then we call LoadBitmap
  248. with the given resource id, which is a base-10 string of ascii digits.
  249. The string is split at the comma during this routine.
  250. If this parameter does not contain a comma then it is assumed to be
  251. the name of a .bmp in $OEM$\OEMFILES and we load it via LoadImage().
  252. WhichOne - supplies a value indicating which bitmap this is.
  253. Return Value:
  254. None.
  255. --*/
  256. {
  257. HINSTANCE ModuleHandle;
  258. PWCHAR p,q;
  259. HBITMAP Bitmap;
  260. WCHAR Buffer[MAX_PATH];
  261. DWORD Result;
  262. if(FilenameAndResId) {
  263. Bitmap = NULL;
  264. if( !MiniSetup ) {
  265. lstrcpy(Buffer,SourcePath);
  266. pSetupConcatenatePaths(Buffer,WINNT_OEM_DIR,MAX_PATH,NULL);
  267. } else {
  268. //
  269. // If we're doing a mini-install, look for the bmp in
  270. // the \sysprep directory on the drive where NT is
  271. // installed, not $OEM$
  272. //
  273. Result = GetWindowsDirectory( Buffer, MAX_PATH );
  274. if( Result == 0) {
  275. MYASSERT(FALSE);
  276. return;
  277. }
  278. Buffer[3] = 0;
  279. pSetupConcatenatePaths( Buffer, TEXT("sysprep"), MAX_PATH, NULL );
  280. }
  281. if(p = wcschr(FilenameAndResId,L',')) {
  282. q = p;
  283. //
  284. // Skip backwards over spaces and quotes. The text setup ini file writer
  285. // will create a line like
  286. //
  287. // a = "b","c"
  288. //
  289. // whose RHS comes back as
  290. //
  291. // b","c
  292. //
  293. // since the profile APIs strip off the outermost quotes.
  294. //
  295. //
  296. while((q > FilenameAndResId) && ((*(q-1) == L'\"') || iswspace(*(q-1)))) {
  297. q--;
  298. }
  299. *q = 0;
  300. q = p+1;
  301. while(*q && ((*q == L'\"') || iswspace(*q))) {
  302. q++;
  303. }
  304. pSetupConcatenatePaths(Buffer,FilenameAndResId,MAX_PATH,NULL);
  305. if(ModuleHandle = LoadLibraryEx(Buffer,NULL,LOAD_LIBRARY_AS_DATAFILE)) {
  306. Bitmap = LoadBitmap(ModuleHandle,MAKEINTRESOURCE(wcstoul(q,NULL,10)));
  307. FreeLibrary(ModuleHandle);
  308. }
  309. } else {
  310. pSetupConcatenatePaths(Buffer,FilenameAndResId,MAX_PATH,NULL);
  311. Bitmap = (HBITMAP)LoadImage(NULL,Buffer,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
  312. }
  313. if(Bitmap) {
  314. //
  315. // Got a valid bitmap. Tell main window about it.
  316. //
  317. SendMessage(MainWindowHandle,WM_NEWBITMAP,WhichOne,(LPARAM)Bitmap);
  318. }
  319. }
  320. }
  321. LONG
  322. ExaminePreinstalledComponent(
  323. IN HKEY hPreinstall,
  324. IN SC_HANDLE hSC,
  325. IN PCWSTR ServiceName
  326. )
  327. /*++
  328. Routine Description:
  329. Query a preinstalled component, and disable it if necessary.
  330. If the component is an OEM component, and is running, then disable
  331. any associated service, if necessary.
  332. Arguments:
  333. hPreinstall - Handle to the key SYSTEM\Setup\Preinstall.
  334. hSC - Handle to the Service Control Manager.
  335. ServiceName - Name of the service to be examined.
  336. Return Value:
  337. Returns a Win32 error code indicating the outcome of the operation.
  338. --*/
  339. {
  340. BOOL OemComponent;
  341. HKEY Key;
  342. LONG Error;
  343. DWORD cbData;
  344. WCHAR Data[ MAX_PATH + 1];
  345. DWORD Type;
  346. SC_HANDLE hSCService;
  347. SERVICE_STATUS ServiceStatus;
  348. //
  349. // Open the key that contains the info about the preinstalled component.
  350. //
  351. Error = RegOpenKeyEx( hPreinstall,
  352. ServiceName,
  353. 0,
  354. KEY_READ,
  355. &Key );
  356. if( Error != ERROR_SUCCESS ) {
  357. return( Error );
  358. }
  359. //
  360. // Find out if the component is an OEM or RETAIL
  361. //
  362. cbData = sizeof(Data);
  363. Error = RegQueryValueEx( Key,
  364. L"OemComponent",
  365. 0,
  366. &Type,
  367. ( LPBYTE )Data,
  368. &cbData );
  369. if( Error != ERROR_SUCCESS ) {
  370. RegCloseKey( Key );
  371. return( Error );
  372. }
  373. OemComponent = (*((PULONG)Data) == 1);
  374. if( OemComponent ) {
  375. //
  376. // Get the name of the retail service to disable
  377. //
  378. cbData = sizeof(Data);
  379. Error = RegQueryValueEx( Key,
  380. L"RetailClassToDisable",
  381. 0,
  382. &Type,
  383. ( LPBYTE )Data,
  384. &cbData );
  385. if( Error != ERROR_SUCCESS ) {
  386. *(( PWCHAR )Data) = '\0';
  387. }
  388. }
  389. RegCloseKey( Key );
  390. //
  391. // Query the service
  392. //
  393. hSCService = OpenService( hSC,
  394. ServiceName,
  395. SERVICE_QUERY_STATUS );
  396. if( hSCService == NULL ) {
  397. Error = GetLastError();
  398. return( Error );
  399. }
  400. if( !QueryServiceStatus( hSCService,
  401. &ServiceStatus ) ) {
  402. Error = GetLastError();
  403. return( Error );
  404. }
  405. CloseServiceHandle( hSCService );
  406. if( ServiceStatus.dwCurrentState == SERVICE_STOPPED ) {
  407. //
  408. // Due to the nature of the services that were pre-installed,
  409. // we can assume that the service failed to initialize, and that
  410. // it can be disabled.
  411. //
  412. MyChangeServiceStart( ServiceName,
  413. SERVICE_DISABLED );
  414. } else {
  415. if( OemComponent &&
  416. ( lstrlen( (PCWSTR)Data ) != 0 ) ) {
  417. MyChangeServiceStart( (PCWSTR)Data,
  418. SERVICE_DISABLED );
  419. }
  420. }
  421. return( ERROR_SUCCESS );
  422. }
  423. BOOL
  424. CleanupPreinstalledComponents(
  425. )
  426. /*++
  427. Routine Description:
  428. Query the preinstalled components, and disable the ones that
  429. failed to start.
  430. This is done by enumerating the subkeys of SYSTEM\Setup\Preinstall.
  431. Each subkey represents a SCSI, Keyboard or Mouse installed.
  432. The video drivers are not listed here. The "Display" applet will
  433. determine and disable the video drivers that were preinstalled, but
  434. failed to start.
  435. Arguments:
  436. None.
  437. Return Value:
  438. Returns TRUE if the operation succeeded, or FALSE otherwise.
  439. --*/
  440. {
  441. LONG Error;
  442. LONG SavedError;
  443. HKEY Key;
  444. HKEY SubKeyHandle;
  445. ULONG i;
  446. ULONG SubKeys;
  447. WCHAR SubKeyName[ MAX_PATH + 1 ];
  448. ULONG NameSize;
  449. FILETIME LastWriteTime;
  450. SC_HANDLE hSC;
  451. EnableEventlogPopup();
  452. //
  453. // Find out the number of subkeys of SYSTEM\Setup\Preinstall
  454. //
  455. Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  456. szPreinstallKeyName,
  457. 0,
  458. KEY_READ,
  459. &Key );
  460. if( Error != ERROR_SUCCESS ) {
  461. //
  462. // If the key doesn't exist, then assume no driver to preinstall,
  463. // and return TRUE.
  464. //
  465. return( Error == ERROR_FILE_NOT_FOUND );
  466. }
  467. Error = RegQueryInfoKey( Key,
  468. NULL,
  469. NULL,
  470. NULL,
  471. &SubKeys,
  472. NULL,
  473. NULL,
  474. NULL,
  475. NULL,
  476. NULL,
  477. NULL,
  478. NULL );
  479. if( Error != ERROR_SUCCESS ) {
  480. return( FALSE );
  481. }
  482. //
  483. // If there are no subkeys, then assume no driver to preinstall
  484. //
  485. if( SubKeys == 0 ) {
  486. return( TRUE );
  487. }
  488. //
  489. // Get a handle to the service control manager
  490. //
  491. hSC = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
  492. if(hSC == NULL) {
  493. Error = GetLastError();
  494. return( FALSE );
  495. }
  496. //
  497. // Query each SCSI, keyboard and mouse driver that was preinstalled
  498. // and disable it if necessary.
  499. //
  500. SavedError = ERROR_SUCCESS;
  501. for( i = 0; i < SubKeys; i++ ) {
  502. NameSize = sizeof( SubKeyName ) / sizeof( WCHAR );
  503. Error = RegEnumKeyEx( Key,
  504. i,
  505. SubKeyName,
  506. &NameSize,
  507. NULL,
  508. NULL,
  509. NULL,
  510. &LastWriteTime );
  511. if( Error != ERROR_SUCCESS ) {
  512. if( SavedError == ERROR_SUCCESS ) {
  513. SavedError = Error;
  514. }
  515. continue;
  516. }
  517. Error = ExaminePreinstalledComponent( Key,
  518. hSC,
  519. SubKeyName );
  520. if( Error != ERROR_SUCCESS ) {
  521. if( SavedError == ERROR_SUCCESS ) {
  522. SavedError = Error;
  523. }
  524. // continue;
  525. }
  526. }
  527. RegCloseKey( Key );
  528. //
  529. // At this point we can remove the Setup\Preinstall key
  530. //
  531. // DeletePreinstallKey();
  532. Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  533. L"SYSTEM\\Setup",
  534. 0,
  535. MAXIMUM_ALLOWED,
  536. &Key );
  537. if( Error == ERROR_SUCCESS ) {
  538. pSetupRegistryDelnode( Key, L"Preinstall" );
  539. RegCloseKey( Key );
  540. }
  541. return( TRUE );
  542. }
  543. BOOL
  544. EnableEventlogPopup(
  545. VOID
  546. )
  547. /*++
  548. Routine Description:
  549. Delete from the registry the value entry that disables the error
  550. popups displayed by the eventlog, if one or more pre-installed
  551. driver failed to load.
  552. This value entry is created in the registry during textmode setup.
  553. Arguments:
  554. None.
  555. Return Value:
  556. Returns TRUE if the operation succeeded, or FALSE otherwise.
  557. --*/
  558. {
  559. HKEY hKey = 0;
  560. ULONG Error;
  561. //
  562. // Delete the 'NoPopupsOnBoot' value
  563. //
  564. Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  565. L"SYSTEM\\CurrentControlSet\\Control\\Windows",
  566. 0,
  567. KEY_SET_VALUE,
  568. &hKey );
  569. if(Error == NO_ERROR) {
  570. Error = RegDeleteValue(hKey,
  571. L"NoPopupsOnBoot");
  572. }
  573. if (hKey) {
  574. RegCloseKey(hKey);
  575. }
  576. return( Error == ERROR_SUCCESS );
  577. }
  578. BOOL
  579. ExecutePreinstallCommands(
  580. VOID
  581. )
  582. /*++
  583. Routine Description:
  584. Executes all commands specified in the file $OEM$\OEMFILES\cmdlines.txt.
  585. Arguments:
  586. None.
  587. Return Value:
  588. Returns TRUE if the operation succeeded, or FALSE otherwise.
  589. --*/
  590. {
  591. WCHAR OldCurrentDir[MAX_PATH];
  592. WCHAR FileName[MAX_PATH];
  593. HINF CmdlinesTxtInf;
  594. LONG LineCount,LineNo;
  595. INFCONTEXT InfContext;
  596. PCWSTR CommandLine;
  597. DWORD DontCare;
  598. BOOL AnyError;
  599. PCWSTR SectionName;
  600. //
  601. // Set current directory to $OEM$.
  602. // Preserve current directory to minimize side-effects.
  603. //
  604. if(!GetCurrentDirectory(MAX_PATH,OldCurrentDir)) {
  605. OldCurrentDir[0] = 0;
  606. }
  607. lstrcpy(FileName,SourcePath);
  608. pSetupConcatenatePaths(FileName,WINNT_OEM_DIR,MAX_PATH,NULL);
  609. SetCurrentDirectory(FileName);
  610. //
  611. // Form name of cmdlines.txt and see if it exists.
  612. //
  613. pSetupConcatenatePaths(FileName,WINNT_OEM_CMDLINE_LIST,MAX_PATH,NULL);
  614. AnyError = FALSE;
  615. if(FileExists(FileName,NULL)) {
  616. CmdlinesTxtInf = SetupOpenInfFile(FileName,NULL,INF_STYLE_OLDNT,NULL);
  617. if(CmdlinesTxtInf == INVALID_HANDLE_VALUE) {
  618. //
  619. // The file exists but is invalid.
  620. //
  621. AnyError = TRUE;
  622. } else {
  623. //
  624. // Get the number of lines in the section that contains the commands to
  625. // be executed. The section may be empty or non-existant; this is not
  626. // an error condition. In that case LineCount may be -1 or 0.
  627. //
  628. SectionName = L"Commands";
  629. LineCount = SetupGetLineCount(CmdlinesTxtInf,SectionName);
  630. for(LineNo=0; LineNo<LineCount; LineNo++) {
  631. if(SetupGetLineByIndex(CmdlinesTxtInf,SectionName,(DWORD)LineNo,&InfContext)
  632. && (CommandLine = pSetupGetField(&InfContext,1))) {
  633. if(!InvokeExternalApplication(NULL,CommandLine,&DontCare)) {
  634. AnyError = TRUE;
  635. }
  636. } else {
  637. //
  638. // Strange case, inf is messed up
  639. //
  640. AnyError = TRUE;
  641. }
  642. }
  643. }
  644. }
  645. //
  646. // Reset current directory and return.
  647. //
  648. if(OldCurrentDir[0]) {
  649. SetCurrentDirectory(OldCurrentDir);
  650. }
  651. if(AnyError) {
  652. SetuplogError(
  653. LogSevError,
  654. SETUPLOG_USE_MESSAGEID,
  655. MSG_LOG_OEMPRE_FAIL,
  656. NULL,NULL);
  657. }
  658. return(!AnyError);
  659. }