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.

780 lines
23 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. factory.c
  5. Abstract:
  6. Factory Pre-install application. This application will be used to perform
  7. pre-installation task in an OEM factory, or system builder (SB) setting.
  8. The task performed will be:
  9. Minimal boot (minimal device and services loaded)
  10. WinBOM processing
  11. Download updated device drivers from NET
  12. Process OOBE info
  13. Process User/Customer specific settings
  14. Process OEM user specific customization
  15. Process Application pre-installations
  16. PnPDevice enumeration
  17. Exit to Windows for Audit mode work.
  18. Author:
  19. Donald McNamara (donaldm) 2/8/2000
  20. Revision History:
  21. --*/
  22. //
  23. // Include File(s):
  24. //
  25. #include "factoryp.h"
  26. #include "shlobj.h"
  27. #include "states.h" // should only ever be included by one c file.
  28. //
  29. // Defined Value(s):
  30. //
  31. #define FILE_WINBOM _T("winbom")
  32. #define FILE_OOBE _T("oobe")
  33. #define FILE_BAT _T(".bat")
  34. #define FILE_CMD _T(".cmd")
  35. #define REG_VAL_FIRSTPNP _T("PnPDetection")
  36. #define PNP_INSTALL_TIMEOUT 600000 // 10 minutes
  37. #define SZ_ENV_RESOURCE _T("ResourceDir")
  38. #define SZ_ENV_RESOURCEL _T("ResourceDirL")
  39. //
  40. // Defined Macro(s):
  41. //
  42. #define CHECK_PARAM(lpCmdLine, lpOption) ( LSTRCMPI(lpCmdLine, lpOption) == 0 )
  43. //
  44. // Type Definition(s):
  45. //
  46. //
  47. // External Global Variable(s):
  48. //
  49. // UI stuff...
  50. //
  51. HINSTANCE g_hInstance = NULL;
  52. // Global factory flags.
  53. DWORD g_dwFactoryFlags = 0;
  54. // Debug Level - used for logging.
  55. //
  56. #ifdef DBG
  57. DWORD g_dwDebugLevel = LOG_DEBUG;
  58. #else
  59. DWORD g_dwDebugLevel = 0;
  60. #endif
  61. // Path to the WinBOM file.
  62. //
  63. TCHAR g_szWinBOMPath[MAX_PATH] = NULLSTR;
  64. // Path to the WinBOM log file.
  65. //
  66. TCHAR g_szLogFile[MAX_PATH] = NULLSTR;
  67. // Path to FACTORY.EXE.
  68. //
  69. TCHAR g_szFactoryPath[MAX_PATH] = NULLSTR;
  70. // Path to the sysprep directory (where factory.exe must be located).
  71. //
  72. TCHAR g_szSysprepDir[MAX_PATH] = NULLSTR;
  73. //
  74. // Internal Golbal Variable(s):
  75. //
  76. // This determines the mode that factory will run in and is set based on
  77. // the command line parameters.
  78. //
  79. FACTMODE g_fm = modeUnknown;
  80. //
  81. // Internal Function Prototype(s):
  82. //
  83. static BOOL ParseCmdLine();
  84. static BOOL IsUserAdmin();
  85. static BOOL RunBatchFile(LPTSTR lpszSysprepFolder, LPTSTR lpszBaseFileName);
  86. static BOOL CheckSetEnv(LPCTSTR lpName, LPCTSTR lpValue);
  87. static void SetupFactoryEnvironment();
  88. /*++
  89. ===============================================================================
  90. Routine Description:
  91. This routine is the main entry point for the program.
  92. We do a bit of error checking, then, if all goes well, we update the
  93. registry to enable execution of our second half.
  94. ===============================================================================
  95. --*/
  96. int APIENTRY WinMain(HINSTANCE hInstance,
  97. HINSTANCE hPrevInstance,
  98. LPSTR lpCmdLine,
  99. int nCmdShow)
  100. {
  101. HANDLE hMutex;
  102. LPTSTR lpFilePart = NULL,
  103. lpMode = NULL,
  104. lpBatchFile = NULL;
  105. DWORD dwLocate,
  106. cbStates = 0;
  107. LPSTATES lpStates = NULL;
  108. BOOL bBadCmdLine,
  109. bOldVersion = FALSE;
  110. // Save the instance handle globally.
  111. //
  112. g_hInstance = hInstance;
  113. // This causes the system not to display the critical-error-handler
  114. // message box. Instead, the system sends the error to the calling
  115. // process.
  116. //
  117. SetErrorMode(SEM_FAILCRITICALERRORS);
  118. // We need the path to factory.exe and where it is located.
  119. //
  120. if ( GetModuleFileName(NULL, g_szFactoryPath, AS ( g_szFactoryPath ) ) &&
  121. GetFullPathName(g_szFactoryPath, AS(g_szSysprepDir), g_szSysprepDir, &lpFilePart) && g_szSysprepDir[0] && lpFilePart )
  122. {
  123. // Chop off the file name.
  124. //
  125. *lpFilePart = NULLCHR;
  126. }
  127. // If either of those file, we must quit (can't imagine that every happening).
  128. //
  129. // ISSUE-2002/02/25-acosma,robertko - why are we checking for g_szFactoryPath here when we already used it above?
  130. //
  131. if ( ( g_szFactoryPath[0] == NULLCHR ) || ( g_szSysprepDir[0] == NULLCHR ) )
  132. {
  133. // Can we log this failure?
  134. //
  135. return 0;
  136. }
  137. // This will setup special factory environment variables.
  138. //
  139. SetupFactoryEnvironment();
  140. //
  141. // Check to see if we are allowed to run on this build of the OS.
  142. //
  143. if ( !OpklibCheckVersion( VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE ) )
  144. {
  145. bOldVersion = TRUE;
  146. }
  147. #ifdef DBG
  148. // In debug builds, lets always try to log right away. In
  149. // the retail case we want to wait until after we locate the
  150. // winbom before we start our logging.
  151. //
  152. InitLogging(NULL);
  153. FacLogFileStr(3, _T("DEBUG: Starting factory (%s)."), GetCommandLine());
  154. #endif
  155. // Check the command line for options (but don't error
  156. // till we have the log file up).
  157. //
  158. bBadCmdLine = ( !ParseCmdLine() || ( g_fm == modeUnknown ) );
  159. // Need to find the mode stuff: string, flags, and states.
  160. //
  161. dwLocate = LOCATE_NORMAL;
  162. switch ( g_fm )
  163. {
  164. case modeLogon:
  165. dwLocate = LOCATE_AGAIN;
  166. SET_FLAG(g_dwFactoryFlags, FLAG_LOGGEDON);
  167. lpStates = &g_FactoryStates[0];
  168. cbStates = AS(g_FactoryStates);
  169. lpMode = INI_VAL_WBOM_TYPE_FACTORY;
  170. break;
  171. case modeSetup:
  172. lpStates = &g_FactoryStates[0];
  173. cbStates = AS(g_FactoryStates);
  174. lpMode = INI_VAL_WBOM_TYPE_FACTORY;
  175. lpBatchFile = FILE_WINBOM;
  176. break;
  177. case modeWinPe:
  178. lpStates = &g_MiniNtStates[0];
  179. cbStates = AS(g_MiniNtStates);
  180. // Fall through...
  181. case modeMiniNt:
  182. lpMode = INI_VAL_WBOM_TYPE_WINPE;
  183. break;
  184. case modeOobe:
  185. dwLocate = LOCATE_NONET;
  186. SET_FLAG(g_dwFactoryFlags, FLAG_NOUI);
  187. SET_FLAG(g_dwFactoryFlags, FLAG_OOBE);
  188. lpStates = &g_OobeStates[0];
  189. cbStates = AS(g_OobeStates);
  190. lpMode = INI_VAL_WBOM_TYPE_OOBE;
  191. lpBatchFile = FILE_OOBE;
  192. break;
  193. default:
  194. lpMode = NULL;
  195. }
  196. // If the mode isn't setup, then pnp is already started.
  197. // Otherwise if this is the first run of factory, wait
  198. // for pnp before all else.
  199. //
  200. if ( modeSetup != g_fm )
  201. {
  202. SET_FLAG(g_dwFactoryFlags, FLAG_PNP_STARTED);
  203. }
  204. else if ( !bBadCmdLine && !bOldVersion && !RegCheck(HKLM, REG_FACTORY_STATE, REG_VAL_FIRSTPNP) )
  205. {
  206. // Kick off pnp this first time so we can get the winbom
  207. // off the floppy or cd-rom.
  208. //
  209. if ( StartPnP() )
  210. {
  211. WaitForPnp(PNP_INSTALL_TIMEOUT);
  212. }
  213. // Make sure we don't do this every boot.
  214. //
  215. // ISSUE-2002/02/25-acosma,robertko - We should only set this if PNP successfully started. Move into above block?
  216. //
  217. RegSetString(HKLM, REG_FACTORY_STATE, REG_VAL_FIRSTPNP, _T("1"));
  218. }
  219. // Run the batch file if we are running from the setup key.
  220. //
  221. if ( !bBadCmdLine && !bOldVersion && lpBatchFile )
  222. {
  223. RunBatchFile(g_szSysprepDir, lpBatchFile);
  224. }
  225. // Find the WinBOM (just use the one previously found if we
  226. // are in the logon mode).
  227. //
  228. LocateWinBom(g_szWinBOMPath, AS(g_szWinBOMPath), g_szSysprepDir, lpMode, dwLocate);
  229. // Find out if we're running on IA64.
  230. //
  231. if ( IsIA64() )
  232. SET_FLAG(g_dwFactoryFlags, FLAG_IA64_MODE);
  233. // Try to enable logging. This checks the WinBOM.
  234. //
  235. // ISSUE-2002/02/25-acosma,robertko - in debug mode we have already done this. We end up doing this twice. Make sure this is ok.
  236. //
  237. InitLogging(g_szWinBOMPath);
  238. // Only let one of this guy run.
  239. //
  240. hMutex = CreateMutex(NULL,FALSE,TEXT("FactoryPre Is Running"));
  241. if ( hMutex == NULL )
  242. {
  243. FacLogFile(0 | LOG_ERR, MSG_OUT_OF_MEMORY);
  244. return 0;
  245. }
  246. // Make sure we are the only process with a handle to our named mutex.
  247. //
  248. if ( GetLastError() == ERROR_ALREADY_EXISTS )
  249. {
  250. FacLogFile(0 | LOG_ERR, MSG_ALREADY_RUNNING);
  251. // Destroy the mutex and bail.
  252. //
  253. CloseHandle(hMutex);
  254. return 0;
  255. }
  256. // Now we can log and return if there was a
  257. // bad command line passed to factory.
  258. //
  259. if ( bBadCmdLine )
  260. {
  261. FacLogFile(0 | LOG_ERR, IDS_ERR_INVALIDCMDLINE);
  262. // Destroy the mutex and bail.
  263. //
  264. CloseHandle(hMutex);
  265. return 0;
  266. }
  267. //
  268. // Now we can log and put up an error message if necessary in case the version of tool is too old.
  269. //
  270. if ( bOldVersion )
  271. {
  272. FacLogFile(0 | LOG_ERR, IDS_ERR_NOT_ALLOWED);
  273. CloseHandle(hMutex);
  274. return 0;
  275. }
  276. // Make sure we have a WinBOM file.
  277. //
  278. if ( g_szWinBOMPath[0] == NULLCHR )
  279. FacLogFile(( g_fm == modeLogon ) ? (2 | LOG_ERR) : (0 | LOG_ERR), IDS_ERR_MISSINGWINBOM);
  280. else
  281. FacLogFile(( g_fm == modeLogon ) ? 2 : 0, IDS_LOG_WINBOMLOCATION, g_szWinBOMPath);
  282. // Ensure that the user is in the admin group.
  283. //
  284. if ( ( g_fm != modeMiniNt ) && ( g_fm != modeWinPe ) && ( !IsUserAdmin() ) )
  285. {
  286. FacLogFile(0 | LOG_ERR, MSG_NOT_AN_ADMINISTRATOR);
  287. // Destroy the mutex and bail.
  288. //
  289. CloseHandle(hMutex);
  290. return 0;
  291. }
  292. // We don't do the state thing in MiniNT mode right now (but we could).
  293. // The modeMiniNt mode is only temporary the real mode in modeWinPe.
  294. //
  295. if ( g_fm == modeMiniNt )
  296. {
  297. // ISSUE-2002/02/25-acosma,robertko - This function does not check if we are running on WinPE, so users can just run factory -mini on any
  298. // machine.
  299. //
  300. if ( !SetupMiniNT() )
  301. {
  302. FacLogFileStr(0 | LOG_ERR | LOG_MSG_BOX, L"Failed to install network adapter -- check WINBOM");
  303. }
  304. }
  305. else
  306. {
  307. // Make sure factory will always run.
  308. //
  309. if ( modeWinPe == g_fm )
  310. {
  311. HKEY hKey;
  312. // Make sure that if we are in "-winpe" mode we only run under WinPE
  313. //
  314. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\MiniNT"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
  315. {
  316. RegCloseKey(hKey);
  317. }
  318. else
  319. {
  320. FacLogFile(0 | LOG_ERR, IDS_ERR_NOT_WINPE);
  321. // Destroy the mutex and bail.
  322. CloseHandle(hMutex);
  323. return 0;
  324. }
  325. }
  326. else if ( modeOobe != g_fm )
  327. {
  328. HKEY hKey;
  329. // Open the key, and set the proper SetupType value.
  330. //
  331. // Very important not to ever change this value in OOBE
  332. // mode!
  333. //
  334. if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\Setup"), 0, KEY_ALL_ACCESS, &hKey ) == ERROR_SUCCESS )
  335. {
  336. DWORD dwValue = SETUPTYPE_NOREBOOT;
  337. RegSetValueEx(hKey, TEXT("SetupType"), 0, REG_DWORD, (CONST LPBYTE) &dwValue, sizeof(DWORD));
  338. }
  339. }
  340. // Now process the winbom.ini file.
  341. //
  342. if ( lpStates && cbStates )
  343. {
  344. ProcessWinBOM(g_szWinBOMPath, lpStates, cbStates);
  345. }
  346. #ifdef DBG
  347. else
  348. {
  349. FacLogFileStr(3, _T("DEBUG: ProcessWinBOM() error... lpStates or cbStates not set."));
  350. }
  351. #endif
  352. }
  353. // Close the Mutex.
  354. //
  355. CloseHandle(hMutex);
  356. return 0;
  357. }
  358. //
  359. // Internal Function(s):
  360. //
  361. static BOOL ParseCmdLine()
  362. {
  363. DWORD dwArgs;
  364. LPTSTR *lpArgs;
  365. BOOL bError = FALSE;
  366. // ISSUE-2002/02/25-acosma,robertko - this is really contorted, we seem to have our own implementation of CommandLineToArgvW inside this
  367. // GetCommandLineArgs() function. Just use the Win32 function. Should be safer.
  368. //
  369. if ( (dwArgs = GetCommandLineArgs(&lpArgs) ) && lpArgs )
  370. {
  371. LPTSTR lpArg;
  372. DWORD dwArg;
  373. // We want to skip over the first argument (it is the path
  374. // to the command being executed.
  375. //
  376. if ( dwArgs > 1 )
  377. {
  378. dwArg = 1;
  379. lpArg = *(lpArgs + dwArg);
  380. }
  381. else
  382. lpArg = NULL;
  383. // Loop through all the arguments.
  384. //
  385. while ( lpArg && !bError )
  386. {
  387. // Now we check to see if the first char is a dash or not.
  388. //
  389. if ( *lpArg == _T('-') )
  390. {
  391. LPTSTR lpOption = CharNext(lpArg);
  392. // This is where you add command line options that start with a dash (-).
  393. //
  394. // ISSUE-2002/02/25-acosma,robertko - We don't validate correct combinations of arguments. I can run
  395. // "factory -setup -logon -winpe -oobe" and the last argument would be the one that is
  396. // picked up. We should fix this and make it smarter.
  397. //
  398. if ( CHECK_PARAM(lpOption, _T("setup")) )
  399. g_fm = modeSetup;
  400. else if ( CHECK_PARAM(lpOption, _T("logon")) )
  401. g_fm = modeLogon;
  402. else if ( CHECK_PARAM(lpOption, _T("minint")) )
  403. g_fm = modeMiniNt;
  404. else if ( CHECK_PARAM(lpOption, _T("winpe")) )
  405. g_fm = modeWinPe;
  406. else if ( CHECK_PARAM(lpOption, _T("oobe")) )
  407. g_fm = modeOobe;
  408. else
  409. bError = TRUE;
  410. }
  411. else if ( *lpArg )
  412. {
  413. bError = TRUE;
  414. }
  415. // Setup the pointer to the next argument in the command line.
  416. //
  417. if ( ++dwArg < dwArgs )
  418. lpArg = *(lpArgs + dwArg);
  419. else
  420. lpArg = NULL;
  421. }
  422. // Make sure to free the two buffers allocated by the GetCommandLineArgs() function.
  423. //
  424. FREE(*lpArgs);
  425. FREE(lpArgs);
  426. }
  427. return !bError;
  428. }
  429. /*++
  430. Routine Description:
  431. This routine returns TRUE if the caller's process is a
  432. member of the Administrators local group. Caller is NOT
  433. expected to be impersonating anyone and is expected to
  434. be able to open their own process and process token.
  435. Arguments:
  436. None.
  437. Return Value:
  438. TRUE - Caller has Administrators local group.
  439. FALSE - Caller does not have Administrators local group. --
  440. */
  441. static BOOL IsUserAdmin(VOID)
  442. {
  443. BOOL b;
  444. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  445. PSID AdministratorsGroup;
  446. b = AllocateAndInitializeSid(
  447. &NtAuthority,
  448. 2,
  449. SECURITY_BUILTIN_DOMAIN_RID,
  450. DOMAIN_ALIAS_RID_ADMINS,
  451. 0, 0, 0, 0, 0, 0,
  452. &AdministratorsGroup);
  453. if(b)
  454. {
  455. if (!CheckTokenMembership( NULL, AdministratorsGroup, &b))
  456. {
  457. b = FALSE;
  458. }
  459. FreeSid(AdministratorsGroup);
  460. }
  461. return(b);
  462. }
  463. /*++
  464. Routine Description:
  465. This routine ckecks WinBOM setting for logging. Logging
  466. is enabled by default if nothing is specified in the
  467. WinBOM. Disables logging by setting g_szLogFile = NULL.
  468. Arguments:
  469. None.
  470. Return Value:
  471. None.
  472. --*/
  473. VOID InitLogging(LPTSTR lpszWinBOMPath)
  474. {
  475. TCHAR szScratch[MAX_PATH] = NULLSTR;
  476. LPTSTR lpszScratch;
  477. BOOL bWinbom = ( lpszWinBOMPath && *lpszWinBOMPath );
  478. // First check if logging is disabled in the WinBOM.
  479. //
  480. if ( ( bWinbom ) &&
  481. ( GetPrivateProfileString(WBOM_FACTORY_SECTION, WBOM_FACTORY_LOGGING, _T("YES"), szScratch, AS(szScratch), lpszWinBOMPath) ) &&
  482. ( LSTRCMPI(szScratch, _T("NO")) == 0 ) )
  483. {
  484. g_szLogFile[0] = NULLCHR;
  485. }
  486. else
  487. {
  488. // All these checks can only be done if we have a winbom.
  489. //
  490. if ( bWinbom )
  491. {
  492. // Check for quiet mode. If we are in quiet mode don't display any MessageBoxes.
  493. // This only works for WinPE mode.
  494. //
  495. if ( (GetPrivateProfileString(WBOM_WINPE_SECTION, INI_KEY_WBOM_QUIET, NULLSTR, szScratch, AS(szScratch), lpszWinBOMPath) ) &&
  496. (0 == LSTRCMPI(szScratch, WBOM_YES))
  497. )
  498. {
  499. SET_FLAG(g_dwFactoryFlags, FLAG_QUIET_MODE);
  500. }
  501. // See if they want to turn on perf logging.
  502. //
  503. szScratch[0] = NULLCHR;
  504. if ( ( GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_KEY_WBOM_LOGPERF, NULLSTR, szScratch, AS(szScratch), lpszWinBOMPath) ) &&
  505. ( 0 == LSTRCMPI(szScratch, WBOM_YES) ) )
  506. {
  507. SET_FLAG(g_dwFactoryFlags, FLAG_LOG_PERF);
  508. }
  509. // Set the logging level.
  510. //
  511. g_dwDebugLevel = (DWORD) GetPrivateProfileInt(WBOM_FACTORY_SECTION, INI_KEY_WBOM_LOGLEVEL, (DWORD) g_dwDebugLevel, lpszWinBOMPath);
  512. }
  513. //
  514. // In non-debug builds we do not want the log level to be set at LOG_DEBUG. Force it
  515. // to drop down by one level if set at LOG_DEBUG or higher.
  516. //
  517. #ifndef DBG
  518. if ( g_dwDebugLevel >= LOG_DEBUG )
  519. g_dwDebugLevel = LOG_DEBUG - 1;
  520. #endif
  521. // Check to see if they have a custom log file they want to use.
  522. //
  523. if ( ( bWinbom ) &&
  524. ( lpszScratch = IniGetExpand(lpszWinBOMPath, INI_SEC_WBOM_FACTORY, INI_KEY_WBOM_FACTORY_LOGFILE, NULL) ) )
  525. {
  526. TCHAR szFullPath[MAX_PATH] = NULLSTR;
  527. LPTSTR lpFind = NULL;
  528. // Turn the ini key into a full path.
  529. //
  530. lstrcpyn( g_szLogFile, lpszScratch, AS( g_szLogFile ) );
  531. if (GetFullPathName(g_szLogFile, AS(szFullPath), szFullPath, &lpFind) && szFullPath[0] && lpFind)
  532. {
  533. // Copy the full path into the global.
  534. //
  535. lstrcpyn(g_szLogFile, szFullPath, AS(g_szLogFile));
  536. // Chop off the file part so we can create the
  537. // path if it doesn't exist.
  538. //
  539. *lpFind = NULLCHR;
  540. // If the directory cannot be created or doesn't exist turn off logging.
  541. //
  542. if (!CreatePath(szFullPath))
  543. g_szLogFile[0] = NULLCHR;
  544. }
  545. // Free the original path buffer from the ini file.
  546. //
  547. FREE(lpszScratch);
  548. }
  549. else // default case
  550. {
  551. // Create it in the current directory (g_szSysprepDir)
  552. //
  553. lstrcpyn(g_szLogFile, g_szSysprepDir, AS ( g_szLogFile ) );
  554. AddPathN(g_szLogFile, WINBOM_LOGFILE, AS ( g_szLogFile ));
  555. }
  556. // Check to see if we have write access to the logfile. If we don't, turn off logging.
  557. // If we're running in WinPE we'll call this function again once the drive becomes
  558. // writable.
  559. //
  560. // Write an FFFE header to the file to identify this as a Unicode text file.
  561. //
  562. if ( g_szLogFile[0] )
  563. {
  564. HANDLE hFile;
  565. DWORD dwWritten = 0;
  566. WCHAR cHeader = 0xFEFF;
  567. SetLastError(ERROR_SUCCESS);
  568. if ( INVALID_HANDLE_VALUE != (hFile = CreateFile(g_szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)))
  569. {
  570. // BUBBUG: This should check for an existing header in the file. There could be an empty
  571. // file with no header.
  572. //
  573. if ( ERROR_ALREADY_EXISTS != GetLastError() )
  574. WriteFile(hFile, &cHeader, sizeof(cHeader), &dwWritten, NULL);
  575. CloseHandle(hFile);
  576. }
  577. else
  578. { // There was a problem opening the file. Most of the time this means that the media is not writable.
  579. // Disable logging in that case.
  580. //
  581. g_szLogFile[0] = NULLCHR;
  582. }
  583. }
  584. }
  585. }
  586. static BOOL RunBatchFile(LPTSTR lpszSysprepFolder, LPTSTR lpszBaseFileName)
  587. {
  588. BOOL bRet = FALSE;
  589. TCHAR szCmdLine[] = NULLSTR,
  590. szWinbomBat[MAX_PATH];
  591. LPTSTR lpExtension;
  592. DWORD dwExitCode;
  593. // First make the fullpath to where the batch file should be.
  594. //
  595. lstrcpyn(szWinbomBat, lpszSysprepFolder, AS(szWinbomBat));
  596. AddPathN(szWinbomBat, lpszBaseFileName, AS(szWinbomBat) );
  597. lpExtension = szWinbomBat + lstrlen(szWinbomBat);
  598. // Make sure there is still enough room for the extension.
  599. //
  600. if ( ((lpExtension + 4) - szWinbomBat ) >= AS(szWinbomBat) )
  601. {
  602. return FALSE;
  603. }
  604. // First try winbom.cmd.
  605. //
  606. lstrcpyn(lpExtension, FILE_CMD, AS ( szWinbomBat ) - lstrlen ( szWinbomBat ) );
  607. if ( FileExists(szWinbomBat) )
  608. {
  609. bRet = InvokeExternalApplicationEx(szWinbomBat, szCmdLine, &dwExitCode, INFINITE, GET_FLAG(g_dwFactoryFlags, FLAG_NOUI));
  610. }
  611. else
  612. {
  613. // Also try winbom.bat if that one didn't exist.
  614. //
  615. lstrcpyn(lpExtension, FILE_BAT, AS ( szWinbomBat ) - lstrlen ( szWinbomBat ) );
  616. if ( FileExists(szWinbomBat) )
  617. {
  618. bRet = InvokeExternalApplicationEx(szWinbomBat, szCmdLine, &dwExitCode, INFINITE, GET_FLAG(g_dwFactoryFlags, FLAG_NOUI));
  619. }
  620. }
  621. return bRet;
  622. }
  623. static BOOL CheckSetEnv(LPCTSTR lpName, LPCTSTR lpValue)
  624. {
  625. if ( 0 == GetEnvironmentVariable(lpName, NULL, 0) )
  626. {
  627. SetEnvironmentVariable(lpName, lpValue);
  628. return TRUE;
  629. }
  630. return FALSE;
  631. }
  632. static void SetupFactoryEnvironment()
  633. {
  634. TCHAR szPath[MAX_PATH];
  635. szPath[0] = NULLCHR;
  636. if ( SHGetSpecialFolderPath(NULL, szPath, CSIDL_RESOURCES, 0) && szPath[0] )
  637. {
  638. CheckSetEnv(SZ_ENV_RESOURCE, szPath);
  639. }
  640. szPath[0] = NULLCHR;
  641. if ( SHGetSpecialFolderPath(NULL, szPath, CSIDL_RESOURCES_LOCALIZED, 0) && szPath[0] )
  642. {
  643. CheckSetEnv(SZ_ENV_RESOURCEL, szPath);
  644. }
  645. }