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.

6580 lines
186 KiB

  1. #ifdef DEBUG_LOGLOG
  2. #pragma message("*** Warning! This is a log-generating build.")
  3. #endif
  4. /*++
  5. File Description:
  6. This file contains all the functions required to add a registry entry
  7. to force execution of the system clone worker upon reboot.
  8. --*/
  9. #include <nt.h>
  10. #include <ntrtl.h>
  11. #include <nturtl.h>
  12. #include <ntsam.h>
  13. #include <ntlsa.h>
  14. #include <windows.h>
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <lmcons.h>
  18. #include <lmerr.h>
  19. #include <lmjoin.h>
  20. #include <lmapibuf.h>
  21. #include <setupapi.h>
  22. #include <spapip.h>
  23. #include <ntsetup.h>
  24. #include <imagehlp.h>
  25. #include <coguid.h>
  26. #include <cfg.h>
  27. #include <cfgmgr32.h>
  28. #include <devguid.h>
  29. #include <netcfgx.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <syssetup.h>
  33. #include <spsyslib.h>
  34. #include <sysprep_.h>
  35. #include <userenv.h>
  36. #include <userenvp.h>
  37. #include <shlwapi.h>
  38. #include <shlobj.h>
  39. #include <shellapi.h>
  40. #include <wininet.h>
  41. #include <winineti.h>
  42. #include "resource.h" // shared string resource from riprep/sysprep
  43. #include <strsafe.h>
  44. #include <shlguid.h> // Needed for CLSID_CUrlHistory
  45. #define COBJMACROS
  46. #include <urlhist.h> // Needed for IUrlHistoryStg2 and IID_IUrlHistoryStg2
  47. #if !(defined(AMD64) || defined(IA64))
  48. #include <cleandrm.h>
  49. #define CLEANDRM_LOGFILE TEXT("cleandrm.log")
  50. #endif // #if !(defined(AMD64) || defined(IA64))
  51. extern BOOL NoSidGen;
  52. extern BOOL PnP;
  53. extern BOOL bMiniSetup;
  54. extern HINSTANCE ghInstance;
  55. //
  56. // Internal Defines
  57. //
  58. #define STR_REG_USERASSIST TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{75048700-EF1F-11D0-9888-006097DEACF9}")
  59. #define STR_REG_USERASSIST_SHELL STR_REG_USERASSIST TEXT("\\Count")
  60. #define STR_REG_USERASSIST_IE TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{5E6AB780-7743-11CF-A12B-00AA004AE837}\\Count")
  61. #define STR_REG_USERASSIST_DEFSHELL TEXT(".DEFAULT\\") STR_REG_USERASSIST_SHELL
  62. #define STR_REG_VAL_VERSION TEXT("Version")
  63. #define VAL_UEM_VERSION 0x00000003
  64. #define VAL_MAX_DATA 16384
  65. #define SYSPREPMASSSTORAGE_SECTION TEXT("sysprepmassstorage")
  66. #define SYSPREP_SECTION TEXT("sysprep")
  67. #define SYSPREP_BUILDMASSSTORAGE_KEY TEXT("BuildMassStorageSection")
  68. #define STR_REG_VALUE_LASTALIVESTAMP TEXT("LastAliveStamp")
  69. #define STR_REG_KEY_RELIABILITY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Reliability")
  70. #ifdef NULLSTR
  71. #undef NULLSTR
  72. #endif // NULLSTR
  73. #define NULLSTR TEXT("\0")
  74. #ifdef NULLCHR
  75. #undef NULLCHR
  76. #endif // NULLCHR
  77. #define NULLCHR TEXT('\0')
  78. #ifdef CHR_BACKSLASH
  79. #undef CHR_BACKSLASH
  80. #endif // CHR_BACKSLASH
  81. #define CHR_BACKSLASH TEXT('\\')
  82. #ifdef CHR_SPACE
  83. #undef CHR_SPACE
  84. #endif // CHR_SPACE
  85. #define CHR_SPACE TEXT(' ')
  86. //
  87. // This is a string version of GUID_DEVCLASS_LEGACYDRIVER in devguid.h
  88. //
  89. #define LEGACYDRIVER_STRING L"{8ECC055D-047F-11D1-A537-0000F8753ED1}"
  90. //
  91. // Context for file queues in SysSetup
  92. //
  93. typedef struct _SYSSETUP_QUEUE_CONTEXT {
  94. PVOID DefaultContext;
  95. PWSTR DirectoryOnSourceDevice;
  96. PWSTR DiskDescription;
  97. PWSTR DiskTag;
  98. } SYSPREP_QUEUE_CONTEXT, *PSYSPREP_QUEUE_CONTEXT;
  99. typedef struct _CLEANUP_NODE
  100. {
  101. LPTSTR pszService;
  102. struct _CLEANUP_NODE* pNext;
  103. }CLEANUP_NODE, *PCLEANUP_NODE, **PPCLEANUP_NODE;
  104. PCLEANUP_NODE g_pCleanupListHead = NULL;
  105. // String macros.
  106. //
  107. #ifndef LSTRCMPI
  108. #define LSTRCMPI(x, y) ( ( CompareString( LOCALE_INVARIANT, NORM_IGNORECASE, x, -1, y, -1 ) - CSTR_EQUAL ) )
  109. #endif // LSTRCMPI
  110. #ifdef DEBUG_LOGLOG
  111. /*++
  112. ===============================================================================
  113. Debug logging for populating/depopulating the critical device
  114. database
  115. ===============================================================================
  116. --*/
  117. #define MAX_MSG_LEN 2048
  118. BOOL LOG_Init(LPCTSTR lpszLogFile);
  119. BOOL LOG_DeInit();
  120. BOOL LOG_Write(LPCTSTR lpszFormat,...);
  121. BOOL LOG_WriteLastError();
  122. int GetSystemErrorMessage(LPTSTR lpszMsg, int cbMsg);
  123. static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
  124. BOOL LOG_Init(
  125. LPCTSTR lpszLogFile
  126. )
  127. {
  128. if (g_hLogFile != INVALID_HANDLE_VALUE)
  129. return FALSE;
  130. g_hLogFile = CreateFile(
  131. lpszLogFile,
  132. GENERIC_WRITE,
  133. FILE_SHARE_READ | FILE_SHARE_WRITE,
  134. NULL,
  135. OPEN_ALWAYS,
  136. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
  137. NULL);
  138. if (g_hLogFile == INVALID_HANDLE_VALUE)
  139. return FALSE;
  140. return TRUE;
  141. }
  142. BOOL LOG_DeInit()
  143. {
  144. BOOL bRet = LOG_Write(TEXT("\r\n"));
  145. if (g_hLogFile != INVALID_HANDLE_VALUE) {
  146. bRet = CloseHandle(g_hLogFile) && bRet;
  147. g_hLogFile = INVALID_HANDLE_VALUE;
  148. }
  149. return bRet;
  150. }
  151. BOOL LOG_Write(
  152. LPCTSTR lpszFormat,
  153. ...
  154. )
  155. {
  156. DWORD dwTemp;
  157. TCHAR szBuf[MAX_MSG_LEN];
  158. char szMsg[MAX_MSG_LEN];
  159. va_list arglist;
  160. int len;
  161. if (g_hLogFile == INVALID_HANDLE_VALUE)
  162. return FALSE;
  163. va_start(arglist, lpszFormat);
  164. _vsnwprintf(szBuf, MAX_MSG_LEN, lpszFormat, arglist);
  165. va_end(arglist);
  166. StringCchCat (szBuf, AS ( szBuf ), TEXT("\r\n"));
  167. len = WideCharToMultiByte(
  168. CP_ACP,
  169. 0,
  170. szBuf,
  171. -1,
  172. szMsg,
  173. MAX_MSG_LEN,
  174. NULL,
  175. NULL
  176. );
  177. if (len == 0) {
  178. return FALSE;
  179. }
  180. SetFilePointer(g_hLogFile, 0L, 0L, FILE_END);
  181. return WriteFile(g_hLogFile, szMsg, len - 1, &dwTemp, NULL);
  182. }
  183. BOOL LOG_WriteLastError()
  184. {
  185. TCHAR szBuf[MAX_MSG_LEN];
  186. GetSystemErrorMessage(szBuf, MAX_MSG_LEN);
  187. return LOG_Write(TEXT("ERROR - %s"), szBuf);
  188. }
  189. int
  190. GetSystemErrorMessage(
  191. LPWSTR lpszMsg,
  192. int cbMsg
  193. )
  194. {
  195. LPVOID lpMsgBuf;
  196. DWORD dwError = GetLastError();
  197. int len;
  198. len = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  199. FORMAT_MESSAGE_FROM_SYSTEM |
  200. FORMAT_MESSAGE_IGNORE_INSERTS,
  201. NULL,
  202. dwError,
  203. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  204. (LPWSTR) &lpMsgBuf,
  205. 0,
  206. NULL );
  207. if( len == 0 ) {
  208. //
  209. // We failed to get a message. Just spew the error
  210. // code.
  211. //
  212. StringCchPrintf( lpszMsg, cbMsg, ( L"(0x%08X)", dwError);
  213. len = lstrlen((LPCWSTR) lpMsgBuf);
  214. } else {
  215. len = lstrlen((LPCWSTR) lpMsgBuf);
  216. StringCchPrintf( lpszMsg, cbMsg, L"(0x%08X) ", dwError);
  217. lpszMsg += lstrlen(lpszMsg);
  218. cbMsg -= lstrlen(lpszMsg);
  219. lstrcpyn(lpszMsg, (LPCWSTR) lpMsgBuf, cbMsg);
  220. if (len >= cbMsg)
  221. lpszMsg[cbMsg - 1] = L'\0';
  222. LocalFree(lpMsgBuf);
  223. }
  224. // Reset the last error incase someone after logging wants
  225. // to get last error again
  226. //
  227. SetLastError(dwError);
  228. return len;
  229. }
  230. #endif // DEBUG_LOGLOG
  231. #define PRO 0
  232. #define SRV 1
  233. #define ADS 2
  234. #define DAT 3
  235. #define PER 4
  236. #define BLA 5
  237. // Returns 0 - Professional, 1 - Server, 2 - ADS, 3 - Data, 4 - Personal, 5 - Blade
  238. //
  239. DWORD GetProductFlavor()
  240. {
  241. DWORD ProductFlavor = PRO; // Default Professional
  242. OSVERSIONINFOEX osvi;
  243. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  244. GetVersionEx((OSVERSIONINFO*)&osvi);
  245. if (osvi.wProductType == VER_NT_WORKSTATION)
  246. {
  247. if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
  248. {
  249. ProductFlavor = PER; // Personal
  250. }
  251. }
  252. else
  253. {
  254. ProductFlavor = SRV; // In the server case assume normal server
  255. if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
  256. {
  257. ProductFlavor = DAT; // Datacenter
  258. }
  259. else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
  260. {
  261. ProductFlavor = ADS; // Advanced server
  262. }
  263. else if (osvi.wSuiteMask & VER_SUITE_BLADE)
  264. {
  265. ProductFlavor = BLA; // Blade server
  266. }
  267. }
  268. return ProductFlavor;
  269. }
  270. // Check if Personal SKU
  271. //
  272. BOOL IsPersonalSKU()
  273. {
  274. if (PER == GetProductFlavor())
  275. return TRUE;
  276. return FALSE;
  277. }
  278. // Check if Professional SKU
  279. //
  280. BOOL IsProfessionalSKU()
  281. {
  282. if (PRO == GetProductFlavor())
  283. return TRUE;
  284. return FALSE;
  285. }
  286. // Check if Server SKU
  287. //
  288. BOOL IsServerSKU()
  289. {
  290. int OS = GetProductFlavor();
  291. if (SRV == OS ||
  292. BLA == OS ||
  293. DAT == OS ||
  294. ADS == OS)
  295. return TRUE;
  296. return FALSE;
  297. }
  298. BOOL
  299. IsDomainMember(
  300. VOID
  301. )
  302. /*++
  303. ===============================================================================
  304. Routine Description:
  305. Detect if we're a member of a domain or not.
  306. Arguments:
  307. Return Value:
  308. TRUE - We're in a domain.
  309. FALSE - We're not in a domain.
  310. ===============================================================================
  311. --*/
  312. {
  313. DWORD rc;
  314. PWSTR SpecifiedDomain = NULL;
  315. NETSETUP_JOIN_STATUS JoinStatus;
  316. rc = NetGetJoinInformation( NULL,
  317. &SpecifiedDomain,
  318. &JoinStatus );
  319. if( SpecifiedDomain ) {
  320. NetApiBufferFree( SpecifiedDomain );
  321. }
  322. if( rc == NO_ERROR ) {
  323. if( JoinStatus == NetSetupDomainName ) {
  324. return TRUE;
  325. }
  326. }
  327. return FALSE;
  328. }
  329. BOOL
  330. ResetRegistryKey(
  331. IN HKEY Rootkey,
  332. IN PCWSTR Subkey,
  333. IN PCWSTR Delkey
  334. )
  335. /*++
  336. ===============================================================================
  337. Routine Description:
  338. Reset a registry key by deleting the key and all subvalues
  339. then recreate the key
  340. Arguments:
  341. Return Value:
  342. ===============================================================================
  343. --*/
  344. {
  345. HKEY hkey;
  346. HKEY nkey;
  347. DWORD rc;
  348. BOOL AnyErrors;
  349. DWORD disp;
  350. AnyErrors = FALSE;
  351. rc = RegCreateKeyEx(Rootkey, Subkey, 0L, NULL,
  352. REG_OPTION_BACKUP_RESTORE,
  353. KEY_CREATE_SUB_KEY, NULL, &hkey, NULL);
  354. if ( rc == NO_ERROR )
  355. {
  356. rc = SHDeleteKey(hkey, Delkey);
  357. if( (rc != NO_ERROR) && (rc != ERROR_FILE_NOT_FOUND) )
  358. {
  359. AnyErrors = TRUE;
  360. }
  361. else
  362. {
  363. rc = RegCreateKeyEx(hkey, Delkey, 0L, NULL,
  364. REG_OPTION_NON_VOLATILE,
  365. KEY_ALL_ACCESS, NULL, &nkey, &disp);
  366. if ( rc != NO_ERROR )
  367. {
  368. AnyErrors = TRUE;
  369. }
  370. //
  371. // BUGUG - Tries to close key even if rc != NO_ERROR
  372. //
  373. RegCloseKey(nkey);
  374. }
  375. //
  376. // BUGUG - Tries to close key even if rc != NO_ERROR
  377. //
  378. RegCloseKey(hkey);
  379. }
  380. else
  381. {
  382. AnyErrors = TRUE;
  383. }
  384. return (!AnyErrors);
  385. }
  386. BOOL
  387. GetAdminAccountName(
  388. PWSTR AccountName
  389. )
  390. /*++
  391. ===============================================================================
  392. Routine Description:
  393. This routine retrieves the name of the Adminstrator account
  394. Arguments:
  395. AccountName This is a buffer that will recieve the name of the account.
  396. Return Value:
  397. TRUE - success.
  398. FALSE - failed.
  399. ===============================================================================
  400. --*/
  401. {
  402. BOOL b = TRUE;
  403. LSA_HANDLE hPolicy;
  404. NTSTATUS ntStatus;
  405. OBJECT_ATTRIBUTES ObjectAttributes;
  406. PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
  407. UCHAR SubAuthCount;
  408. DWORD sidlen;
  409. PSID psid = NULL;
  410. WCHAR domainname[MAX_PATH];
  411. DWORD adminlen= MAX_PATH;
  412. DWORD domlen=MAX_PATH;
  413. SID_NAME_USE sidtype;
  414. InitializeObjectAttributes( &ObjectAttributes,
  415. NULL,
  416. 0L,
  417. NULL,
  418. NULL );
  419. ntStatus = LsaOpenPolicy( NULL,
  420. &ObjectAttributes,
  421. POLICY_ALL_ACCESS,
  422. &hPolicy );
  423. if (!NT_SUCCESS(ntStatus)) {
  424. //
  425. // ISSUE-2002/02/26-brucegr: Do you close the handle if LsaOpenPolicy fails?
  426. //
  427. LsaClose(hPolicy);
  428. b = FALSE;
  429. }
  430. if( b ) {
  431. ntStatus = LsaQueryInformationPolicy( hPolicy,
  432. PolicyAccountDomainInformation,
  433. (PVOID *) &AccountDomainInfo );
  434. LsaClose( hPolicy );
  435. if (!NT_SUCCESS(ntStatus)) {
  436. if ( AccountDomainInfo != NULL ) {
  437. (VOID) LsaFreeMemory( AccountDomainInfo );
  438. }
  439. b = FALSE;
  440. }
  441. }
  442. if( b ) {
  443. //
  444. // calculate the size of a new sid with one more SubAuthority
  445. //
  446. SubAuthCount = *(GetSidSubAuthorityCount ( AccountDomainInfo->DomainSid ));
  447. SubAuthCount++; // for admin
  448. sidlen = GetSidLengthRequired ( SubAuthCount );
  449. //
  450. // allocate and copy the new new sid from the Domain SID
  451. //
  452. psid = (PSID)malloc(sidlen);
  453. if (psid) {
  454. memcpy(psid, AccountDomainInfo->DomainSid, GetLengthSid(AccountDomainInfo->DomainSid) );
  455. //
  456. // increment SubAuthority count and add Domain Admin RID
  457. //
  458. *(GetSidSubAuthorityCount( psid )) = SubAuthCount;
  459. *(GetSidSubAuthority( psid, SubAuthCount-1 )) = DOMAIN_USER_RID_ADMIN;
  460. if ( AccountDomainInfo != NULL ) {
  461. (VOID) LsaFreeMemory( AccountDomainInfo );
  462. }
  463. //
  464. // get the admin account name from the new SID
  465. //
  466. b = LookupAccountSid( NULL,
  467. psid,
  468. AccountName,
  469. &adminlen,
  470. domainname,
  471. &domlen,
  472. &sidtype );
  473. free(psid);
  474. }
  475. }
  476. return( b );
  477. }
  478. BOOL
  479. DeleteWinlogonDefaults(
  480. VOID
  481. )
  482. /*++
  483. ===============================================================================
  484. Routine Description:
  485. Delete the following registry values:
  486. HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultDomainName
  487. HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\DefaultUserName
  488. Arguments:
  489. Return Value:
  490. ===============================================================================
  491. --*/
  492. {
  493. HKEY hkey;
  494. DWORD rc;
  495. BOOL AnyErrors;
  496. WCHAR AccountName[MAX_PATH];
  497. AnyErrors = FALSE;
  498. rc = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  499. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  500. 0L, NULL,
  501. REG_OPTION_BACKUP_RESTORE,
  502. KEY_SET_VALUE, NULL, &hkey, NULL);
  503. if (rc == NO_ERROR)
  504. {
  505. //
  506. // If Personal then reset the values
  507. //
  508. if (IsPersonalSKU()) {
  509. DWORD dwSize = MAX_PATH * sizeof(TCHAR);
  510. StringCchCopy ( AccountName, AS ( AccountName ), TEXT("Owner"));
  511. rc = RegSetValueEx( hkey,
  512. TEXT("DefaultUserName"),
  513. 0,
  514. REG_SZ,
  515. (CONST BYTE *)AccountName,
  516. (lstrlen( AccountName ) + 1) * sizeof(TCHAR) );
  517. if((rc != NO_ERROR) && (rc != ERROR_FILE_NOT_FOUND)) {
  518. AnyErrors = TRUE;
  519. }
  520. }
  521. else {
  522. //
  523. // All others sku
  524. //
  525. if(rc == NO_ERROR) {
  526. rc = RegDeleteValue( hkey, TEXT("DefaultDomainName") );
  527. if((rc != NO_ERROR) && (rc != ERROR_FILE_NOT_FOUND)) {
  528. AnyErrors = TRUE;
  529. } else {
  530. //
  531. // Before we whack the DefaultUserName value, let's
  532. // make sure we can replace it with the name of the
  533. // administrator account. So first go retrieve that
  534. // name.
  535. //
  536. if( GetAdminAccountName( AccountName ) ) {
  537. //
  538. // Got it. Reset the value key.
  539. //
  540. rc = RegSetValueEx( hkey,
  541. TEXT("DefaultUserName"),
  542. 0,
  543. REG_SZ,
  544. (CONST BYTE *)AccountName,
  545. (lstrlen( AccountName ) + 1) * sizeof(TCHAR) );
  546. if((rc != NO_ERROR) && (rc != ERROR_FILE_NOT_FOUND)) {
  547. AnyErrors = TRUE;
  548. }
  549. } else {
  550. //
  551. // Sniff... We couldn't retrieve the name of the
  552. // administrator account. Very odd. Better
  553. // be safe and just leave the key as it is.
  554. //
  555. }
  556. }
  557. }
  558. }
  559. RegCloseKey(hkey);
  560. }
  561. else {
  562. AnyErrors = TRUE;
  563. }
  564. return (!AnyErrors);
  565. }
  566. VOID
  567. FixDevicePaths(
  568. VOID
  569. )
  570. /*++
  571. ===============================================================================
  572. Routine Description:
  573. This routine checks to see if the user specified an oempnpdriverspath in
  574. his unattend file. If so, we need to append it onto the DevicePath
  575. entry in the registry.
  576. If the user specified an InstallFilesPath in the unattend file, we
  577. will plug that value into the registry so that Lang files, ...
  578. can be obtained from this new directory.
  579. Arguments:
  580. None.
  581. Return Value:
  582. ===============================================================================
  583. --*/
  584. {
  585. LPTSTR lpNewPath = NULL,
  586. lpOldPath,
  587. lpSearch;
  588. DWORD dwChars = 512,
  589. dwReturn;
  590. TCHAR NewPath[2048];
  591. TCHAR FileName[2048];
  592. HKEY hKey;
  593. DWORD l;
  594. DWORD Size;
  595. DWORD Type;
  596. //
  597. // NOTE: This function should call UpdateDevicePath() and UpdateSourcePath()
  598. // from OPKLIB. Those fuctions do the exact thing that the following
  599. // code does. But for now because I don't want to deal the whole riprep
  600. // linking with OPKLIB, this duplicate code will just have to remain.
  601. //
  602. //
  603. // =================================
  604. // OemPnpDriversPath
  605. // =================================
  606. //
  607. //
  608. // First see if he's got the entry in the unattend file.
  609. //
  610. if (!GetWindowsDirectory( FileName, MAX_PATH ))
  611. return;
  612. StringCchCopy ( &FileName[3], AS ( FileName ) - 3, TEXT("sysprep\\sysprep.inf") );
  613. // Get the new string from the INF file.
  614. //
  615. do
  616. {
  617. // Start with 1k of characters, doubling each time.
  618. //
  619. dwChars *= 2;
  620. // Free the previous buffer, if there was one.
  621. //
  622. if ( lpNewPath )
  623. free(lpNewPath);
  624. // Allocate a new buffer.
  625. //
  626. if ( lpNewPath = (LPTSTR) malloc(dwChars * sizeof(TCHAR)) )
  627. {
  628. *lpNewPath = L'\0';
  629. dwReturn = GetPrivateProfileString(L"Unattended", L"OemPnPDriversPath", L"", lpNewPath, dwChars, FileName);
  630. }
  631. else
  632. dwReturn = 0;
  633. }
  634. while ( dwReturn >= (dwChars - 1) );
  635. if ( lpNewPath && *lpNewPath )
  636. {
  637. //
  638. // Got it. Open the registry and get the original value.
  639. //
  640. //
  641. // Open HKLM\Software\Microsoft\Windows\CurrentVersion
  642. //
  643. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  644. TEXT("Software\\Microsoft\\Windows\\CurrentVersion"),
  645. 0,
  646. KEY_ALL_ACCESS,
  647. &hKey );
  648. if( l == NO_ERROR ) {
  649. //
  650. // Query the value of the DevicePath Key.
  651. //
  652. Size = 0;
  653. l = RegQueryValueEx( hKey,
  654. TEXT("DevicePath"),
  655. NULL,
  656. &Type,
  657. NULL,
  658. &Size );
  659. if ( ERROR_SUCCESS != l )
  660. Size = 0;
  661. // Need to count the number of paths in the new path buffer.
  662. //
  663. for ( dwChars = 1, lpSearch = lpNewPath; *lpSearch; lpSearch++ )
  664. {
  665. if ( L';' == *lpSearch )
  666. dwChars++;
  667. }
  668. // The size of the old buffer needs to be the size of the registry key,
  669. // plus the size of the new buffer, plus room for the ";%systemdrive%\" we
  670. // are going to add to each path in the new buffer.
  671. //
  672. Size += (lstrlen(lpNewPath) + (dwChars * 16) + 1) * sizeof(TCHAR);
  673. if ( lpOldPath = (LPTSTR) malloc(Size) )
  674. {
  675. TCHAR *BeginStrPtr;
  676. TCHAR *EndStrPtr;
  677. BOOL Done = FALSE;
  678. LPTSTR lpAdd;
  679. DWORD dwBufSize = Size;
  680. l = RegQueryValueEx( hKey,
  681. TEXT("DevicePath"),
  682. NULL,
  683. &Type,
  684. (LPBYTE) lpOldPath,
  685. &dwBufSize );
  686. if ( ERROR_SUCCESS != l )
  687. *lpOldPath = L'\0';
  688. //
  689. // OemPnpDriversDirPath can have several entries, separated by
  690. // a semicolon. For each entry, we need to:
  691. // 1. append a semicolon.
  692. // 2. append %SystemDrive%
  693. // 3. concatenate the entry.
  694. //
  695. BeginStrPtr = lpNewPath;
  696. do {
  697. //
  698. // Mark the end of this entry.
  699. //
  700. EndStrPtr = BeginStrPtr;
  701. while( (*EndStrPtr) && (*EndStrPtr != L';') ) {
  702. EndStrPtr++;
  703. }
  704. //
  705. // Is this the last entry?
  706. //
  707. if( *EndStrPtr == 0 ) {
  708. Done = TRUE;
  709. }
  710. *EndStrPtr = 0;
  711. //
  712. // Make sure that if you change anything here that
  713. // has to do with the length of the extra data we add
  714. // to each path in the new buffer, that you change the
  715. // extra padding we give to the old path buffer (currenttly
  716. // 16 chars for every different path in the new buffer).
  717. //
  718. // Save a pointer to part we are adding
  719. // so it can be removed if already there.
  720. //
  721. lpAdd = lpOldPath + lstrlen(lpOldPath);
  722. if ( *lpOldPath )
  723. StringCchCat( lpAdd, ( Size / sizeof ( TCHAR ) ) - lstrlen( lpOldPath ), L";" );
  724. // Save a pointer to the part we are going to
  725. // search for in the old path (after the ;).
  726. //
  727. lpSearch = lpOldPath + lstrlen(lpOldPath);
  728. StringCchCat( lpSearch, ( Size / sizeof ( TCHAR ) ) - lstrlen( lpOldPath ), L"%SystemDrive%\\" );
  729. if ( L'\\' == *BeginStrPtr )
  730. BeginStrPtr++;
  731. lpSearch = lpOldPath + lstrlen(lpOldPath);
  732. StringCchCat( lpSearch, ( Size / sizeof ( TCHAR ) ) - lstrlen( lpOldPath ), BeginStrPtr);
  733. BeginStrPtr = EndStrPtr + 1;
  734. // Check to see if this new string is already
  735. // in the old path.
  736. //
  737. EndStrPtr = lpOldPath;
  738. do
  739. {
  740. // First check for our string we are adding.
  741. //
  742. if ( ( EndStrPtr = StrStrI(EndStrPtr, lpSearch) ) &&
  743. ( EndStrPtr < lpAdd ) )
  744. {
  745. // If found, make sure the next character
  746. // in our old path is a ; or null.
  747. //
  748. EndStrPtr += lstrlen(lpSearch);
  749. if ( ( TEXT('\0') == *EndStrPtr ) ||
  750. ( TEXT(';') == *EndStrPtr ) )
  751. {
  752. // If it is, it is already there and we
  753. // need to get rid of the string we added.
  754. //
  755. *lpAdd = TEXT('\0');
  756. }
  757. else
  758. {
  759. // If it isn't, move the end pointer to the next
  760. // ; so we can search the rest of the old path string.
  761. //
  762. while ( *EndStrPtr && ( TEXT(';') != *EndStrPtr ) )
  763. EndStrPtr++;
  764. }
  765. }
  766. }
  767. while ( EndStrPtr && ( EndStrPtr < lpAdd ) && *lpAdd );
  768. //
  769. // Take care of the case where the user ended the
  770. // OemPnpDriversPath entry with a semicolon.
  771. //
  772. if( *BeginStrPtr == 0 ) {
  773. Done = TRUE;
  774. }
  775. } while( !Done );
  776. //
  777. // Now set the key with our new value.
  778. //
  779. l = RegSetValueEx( hKey,
  780. TEXT("DevicePath"),
  781. 0,
  782. REG_EXPAND_SZ,
  783. (CONST BYTE *)lpOldPath,
  784. (lstrlen( lpOldPath ) + 1) * sizeof(TCHAR));
  785. free(lpOldPath);
  786. }
  787. RegCloseKey(hKey);
  788. }
  789. free(lpNewPath);
  790. }
  791. //
  792. // =================================
  793. // InstallFilesPath
  794. // =================================
  795. //
  796. //
  797. // First see if he's got the entry in the unattend file.
  798. //
  799. if (!GetWindowsDirectory( FileName, MAX_PATH ))
  800. return;
  801. StringCchCopy ( &FileName[3], AS ( FileName ) - 3, TEXT("sysprep\\sysprep.inf") );
  802. //
  803. // ISSUE-2002/02/26-brucegr: NewPath should be zero initialized for "if" check below
  804. //
  805. GetPrivateProfileString( TEXT( "Unattended" ),
  806. TEXT( "InstallFilesPath" ),
  807. L"",
  808. NewPath,
  809. sizeof(NewPath)/sizeof(NewPath[0]),
  810. FileName );
  811. if( NewPath[0] ) {
  812. //
  813. // Got it. Open the registry and get the original value.
  814. //
  815. //
  816. // Open HKLM\Software\Microsoft\Windows\CurrentVersion\\Setup
  817. //
  818. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  819. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup"),
  820. 0,
  821. KEY_ALL_ACCESS,
  822. &hKey );
  823. if( l == NO_ERROR ) {
  824. //
  825. // Now set the key with our new value.
  826. //
  827. l = RegSetValueEx( hKey,
  828. TEXT("SourcePath"),
  829. 0,
  830. REG_SZ,
  831. (CONST BYTE *)NewPath,
  832. (lstrlen( NewPath ) + 1) * sizeof(TCHAR));
  833. //
  834. // ISSUE-2002/02/26-brucegr: Do we care about the return value?
  835. //
  836. RegCloseKey(hKey);
  837. }
  838. }
  839. }
  840. void DeleteAllValues(HKEY hKey)
  841. {
  842. DWORD dwCount = 0;
  843. DWORD dwMaxNameLen = 0;
  844. // Enumerate all the existing values and delete them all.
  845. //Let's get the number of Entries already present and the max size of value name.
  846. if(RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwCount, &dwMaxNameLen, NULL, NULL, NULL) == ERROR_SUCCESS)
  847. {
  848. LPTSTR lpValueName = (LPTSTR) LocalAlloc(LPTR, (dwMaxNameLen + 1)*sizeof(TCHAR));
  849. if(lpValueName)
  850. {
  851. //Let's remove all the values already present in the UEM database.
  852. while(dwCount--)
  853. {
  854. DWORD dwNameLen = dwMaxNameLen + 1;
  855. if(RegEnumValue(hKey, dwCount, lpValueName, &dwNameLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
  856. {
  857. RegDeleteValue(hKey, lpValueName);
  858. }
  859. else
  860. {
  861. //If RegQueryInfoKey worked correct, this should never happen.
  862. ASSERT(0);
  863. }
  864. }
  865. LocalFree((HLOCAL)lpValueName);
  866. }
  867. }
  868. }
  869. void ClearRecentApps()
  870. {
  871. HKEY hKeyCurrentUser,
  872. hKeyDefault;
  873. DWORD dwDisposition;
  874. TCHAR szName[MAX_PATH] = TEXT("");
  875. LPTSTR lpszValue = NULL;
  876. DWORD dwNameSize = (sizeof(szName) / sizeof(TCHAR)),
  877. dwRegIndex = 0,
  878. dwUemVersion = VAL_UEM_VERSION,
  879. dwValueSize,
  880. dwType;
  881. // Open the key for the Shell MFU list
  882. //
  883. if ( RegCreateKeyEx(HKEY_CURRENT_USER, STR_REG_USERASSIST_SHELL, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKeyCurrentUser, &dwDisposition) == ERROR_SUCCESS )
  884. {
  885. // Check to see if we opened an existing key, if so delete all of the values
  886. //
  887. if(dwDisposition == REG_OPENED_EXISTING_KEY)
  888. {
  889. DeleteAllValues(hKeyCurrentUser);
  890. }
  891. // Write out the version value to the parent key
  892. //
  893. SHSetValue(HKEY_CURRENT_USER, STR_REG_USERASSIST, STR_REG_VAL_VERSION, REG_DWORD, &dwUemVersion, sizeof(dwUemVersion));
  894. // Copy all of the values from the .DEFAULT registry
  895. //
  896. if ( RegOpenKeyEx(HKEY_USERS, STR_REG_USERASSIST_DEFSHELL, 0, KEY_READ, &hKeyDefault) == ERROR_SUCCESS )
  897. {
  898. // Allocate the value buffer...
  899. //
  900. lpszValue = malloc(VAL_MAX_DATA * sizeof(TCHAR));
  901. if ( lpszValue )
  902. {
  903. dwValueSize = VAL_MAX_DATA * sizeof(TCHAR);
  904. // Enumerate each value
  905. //
  906. while (RegEnumValue(hKeyDefault, dwRegIndex, szName, &dwNameSize, NULL, &dwType, (LPBYTE)lpszValue, &dwValueSize ) == ERROR_SUCCESS)
  907. {
  908. // Set the value in the current user key
  909. //
  910. RegSetValueEx(hKeyCurrentUser, szName, 0, dwType, (LPBYTE) lpszValue, dwValueSize);
  911. // Reset the size of the name value
  912. //
  913. dwNameSize = sizeof(szName) / sizeof(TCHAR);
  914. dwValueSize = VAL_MAX_DATA * sizeof(TCHAR);
  915. // Increment to the next value
  916. //
  917. dwRegIndex++;
  918. }
  919. free( lpszValue );
  920. }
  921. // Clean up the registry keys
  922. //
  923. RegCloseKey(hKeyDefault);
  924. }
  925. // Clean up the registry keys
  926. //
  927. RegCloseKey(hKeyCurrentUser);
  928. }
  929. // Reset the disposition
  930. //
  931. dwDisposition = 0;
  932. // Open the second key containing information in the IE MFU list
  933. //
  934. if ( RegCreateKeyEx(HKEY_CURRENT_USER, STR_REG_USERASSIST_IE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKeyCurrentUser, &dwDisposition) == ERROR_SUCCESS )
  935. {
  936. // Check to see if we opened an existing key, if so delete all of the values
  937. //
  938. if(dwDisposition == REG_OPENED_EXISTING_KEY)
  939. {
  940. DeleteAllValues(hKeyCurrentUser);
  941. }
  942. // Clean up the registry keys
  943. //
  944. RegCloseKey(hKeyCurrentUser);
  945. }
  946. }
  947. BOOL
  948. NukeUserSettings(
  949. VOID
  950. )
  951. /*++
  952. ===============================================================================
  953. Routine Description:
  954. This routine clears user specific settings from all user profiles on system:
  955. - clears unique settings that identify Media Player.
  956. - resets the ICW Completed flag to force ICW to run again.
  957. - deletes the MS Messenger Software\Microsoft\MessengerService\PassportBalloon value
  958. Arguments:
  959. None.
  960. Return Value:
  961. TRUE - on success
  962. FALSE - if there were any errors
  963. Remarks:
  964. Media Player regenerates these settings when they don't exist, thus
  965. each installation of an image will have unique Media Player IDs.
  966. ===============================================================================
  967. --*/
  968. {
  969. HKEY hKey;
  970. HKEY oKey;
  971. DWORD dwSt;
  972. WCHAR szKeyname[1024];
  973. BOOL AnyErrors = FALSE;
  974. INT i = 0;
  975. INT iElem = 0;
  976. typedef struct _REGVALUES
  977. {
  978. LPTSTR szKey;
  979. LPTSTR szValue;
  980. } REGVALUES;
  981. REGVALUES rvList[] =
  982. {
  983. { TEXT("Software\\Microsoft\\MediaPlayer\\Player\\Settings"), TEXT("Client ID") }, // Delete unique Media Player settings.
  984. { TEXT("Software\\Microsoft\\Windows Media\\WMSDK\\General"), TEXT("UniqueID") }, // Delete unique Media Player settings.
  985. { TEXT("Software\\Microsoft\\Internet Connection Wizard"), TEXT("Completed") }, // Delete this key to cause ICW to run again.
  986. { TEXT("Software\\Microsoft\\MessengerService"), TEXT("PassportBalloon") }, // Cleanup for MS Messenger.
  987. { TEXT("Software\\Microsoft\\MessengerService"), TEXT("FirstTimeUser") }, // Cleanup for MS Messenger.
  988. { TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VisualEffects\\Fontsmoothing"), TEXT("DefaultApplied") }, // Makes it so clear type setting is applied again when user logs on.
  989. };
  990. //
  991. // Enumerate HKEY_USERS
  992. // For each key under HKEY_USERS do the following.
  993. //
  994. while ( RegEnumKey( HKEY_USERS, i++, szKeyname, 1024) == ERROR_SUCCESS )
  995. {
  996. // Open the key for this user.
  997. //
  998. if ( RegOpenKeyEx( HKEY_USERS, szKeyname, 0L, KEY_ALL_ACCESS, &hKey ) == ERROR_SUCCESS )
  999. {
  1000. for ( iElem = 0; iElem < (sizeof( rvList ) / sizeof( rvList[0] )); iElem++ )
  1001. {
  1002. // Delete Values from each key.
  1003. //
  1004. if ( RegOpenKeyEx( hKey, rvList[iElem].szKey, 0L, KEY_ALL_ACCESS, &oKey ) == ERROR_SUCCESS )
  1005. {
  1006. RegDeleteValue( oKey, rvList[iElem].szValue );
  1007. RegCloseKey( oKey );
  1008. }
  1009. else
  1010. AnyErrors = TRUE;
  1011. }
  1012. RegCloseKey(hKey);
  1013. }
  1014. else
  1015. AnyErrors = TRUE;
  1016. }
  1017. return (!AnyErrors);
  1018. }
  1019. BOOL
  1020. NukeMruList(
  1021. VOID
  1022. )
  1023. /*++
  1024. ===============================================================================
  1025. Routine Description:
  1026. This routine clears the MRU lists on the machine.
  1027. Arguments:
  1028. None.
  1029. Return Value:
  1030. ===============================================================================
  1031. --*/
  1032. {
  1033. BOOL AnyErrors = FALSE;
  1034. BOOL b;
  1035. LONG rc;
  1036. WCHAR keyname[1024];
  1037. WCHAR netname[1024];
  1038. HKEY rkey;
  1039. HKEY ukey;
  1040. HKEY nkey;
  1041. HKEY hOpenKey;
  1042. INT i;
  1043. INT j;
  1044. AnyErrors = FALSE;
  1045. //
  1046. // Enumerate HKEY_USERS
  1047. // For each key under HKEY_USERS clean out MRU and Netconnections
  1048. //
  1049. i=0;
  1050. while ( (rc = RegEnumKey( HKEY_USERS, i, keyname, 1024)) == ERROR_SUCCESS ) {
  1051. //
  1052. // open this user key
  1053. //
  1054. rc = RegCreateKeyEx(HKEY_USERS, keyname, 0L, NULL,
  1055. REG_OPTION_BACKUP_RESTORE,
  1056. KEY_CREATE_SUB_KEY, NULL, &ukey, NULL);
  1057. if(rc == NO_ERROR) {
  1058. //
  1059. // special case Network because of subkeys
  1060. //
  1061. rc = RegCreateKeyEx(ukey, L"Network", 0L, NULL,
  1062. REG_OPTION_BACKUP_RESTORE,
  1063. KEY_CREATE_SUB_KEY, NULL, &nkey, NULL);
  1064. if (rc == NO_ERROR) {
  1065. j=0;
  1066. while ( (rc = RegEnumKey( nkey, j, netname, 1024)) == ERROR_SUCCESS ) {
  1067. // HKEY_CURRENT_USER\Network
  1068. rc = RegDeleteKey( nkey, netname );
  1069. if((rc != NO_ERROR) && (rc != ERROR_FILE_NOT_FOUND))
  1070. AnyErrors = TRUE;
  1071. j++; // increment network key
  1072. }
  1073. }
  1074. //
  1075. // HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Network\Persistent Connections
  1076. //
  1077. if (!ResetRegistryKey(
  1078. ukey,
  1079. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Network",
  1080. L"Persistent Connections") )
  1081. AnyErrors = TRUE;
  1082. //
  1083. // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs
  1084. //
  1085. if (!ResetRegistryKey(
  1086. ukey,
  1087. L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
  1088. L"RecentDocs") )
  1089. AnyErrors = TRUE;
  1090. //
  1091. // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced,StartMenuInit
  1092. //
  1093. if ( ERROR_SUCCESS == RegOpenKeyEx(ukey,
  1094. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  1095. 0,
  1096. KEY_ALL_ACCESS,
  1097. &hOpenKey) )
  1098. {
  1099. // Set the value in the registry
  1100. //
  1101. RegDeleteValue(hOpenKey,
  1102. TEXT("StartMenuInit"));
  1103. // Close the key
  1104. //
  1105. RegCloseKey(hOpenKey);
  1106. }
  1107. //
  1108. // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU
  1109. //
  1110. if (!ResetRegistryKey(
  1111. ukey,
  1112. L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
  1113. L"RunMRU") )
  1114. AnyErrors = TRUE;
  1115. }
  1116. i++;
  1117. }
  1118. return (!AnyErrors);
  1119. }
  1120. VOID
  1121. NukeEventLogs(
  1122. VOID
  1123. )
  1124. /*++
  1125. ===============================================================================
  1126. Routine Description:
  1127. This routine clears the eventlogs. Ignore any errors here.
  1128. Arguments:
  1129. None.
  1130. Return Value:
  1131. ===============================================================================
  1132. --*/
  1133. {
  1134. HANDLE hEventLog;
  1135. hEventLog = OpenEventLog( NULL, TEXT("System") );
  1136. if (hEventLog) {
  1137. ClearEventLog( hEventLog, NULL );
  1138. CloseEventLog( hEventLog );
  1139. }
  1140. hEventLog = OpenEventLog( NULL, TEXT("Application") );
  1141. if (hEventLog) {
  1142. ClearEventLog( hEventLog, NULL );
  1143. CloseEventLog( hEventLog );
  1144. }
  1145. hEventLog = OpenEventLog( NULL, TEXT("Security") );
  1146. if (hEventLog) {
  1147. ClearEventLog( hEventLog, NULL );
  1148. CloseEventLog( hEventLog );
  1149. }
  1150. }
  1151. VOID
  1152. NukeSmsSettings(
  1153. VOID
  1154. )
  1155. /*++
  1156. ===============================================================================
  1157. Routine Description:
  1158. This routine clears the SMS client specific settings on system:
  1159. - clears unique settings for SMS from the registry and ini files.
  1160. Arguments:
  1161. None.
  1162. Return Value:
  1163. None.
  1164. Remarks:
  1165. Part of the clearing requires blanking out certain INI files.
  1166. ===============================================================================
  1167. --*/
  1168. {
  1169. HKEY hkSms = NULL;
  1170. TCHAR szWindowsDir[MAX_PATH] = TEXT("\0"),
  1171. szIniFile[MAX_PATH] = TEXT("\0"),
  1172. szDatFile[MAX_PATH] = TEXT("\0"),
  1173. szDefaultValue[] = TEXT("\0");
  1174. // Remove HKLM\Software\Microsoft\Windows\CurrentVersion\Setup
  1175. //
  1176. if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\SMS\\Client\\Configuration\\Client Properties"), &hkSms)) {
  1177. //
  1178. // ISSUE-2002/02/26-brucegr: Should be adding one character to length field for null-terminator
  1179. //
  1180. RegSetValueEx(hkSms, TEXT("SMS Unique Identifier"), 0, REG_SZ, (LPBYTE)szDefaultValue, (lstrlen(szDefaultValue)*sizeof(TCHAR)));
  1181. RegCloseKey(hkSms);
  1182. }
  1183. // Clear the SMS Unique ID from INI files
  1184. //
  1185. if ( GetWindowsDirectory(szWindowsDir, MAX_PATH) && *szWindowsDir )
  1186. {
  1187. StringCchCopy ( szIniFile, AS ( szIniFile ), szWindowsDir);
  1188. OPKAddPathN (szIniFile, TEXT("ms\\sms\\core\\data"), AS ( szIniFile ) );
  1189. if (PathIsDirectory(szIniFile)) {
  1190. OPKAddPathN(szIniFile, TEXT("sms1x.ini"), AS ( szIniFile ));
  1191. WritePrivateProfileString(TEXT("SMS"), TEXT("SMS Unique ID"), TEXT(""), szIniFile);
  1192. }
  1193. StringCchCopy ( szIniFile, AS ( szIniFile ), szWindowsDir);
  1194. if (PathIsDirectory(szIniFile)) {
  1195. OPKAddPathN(szIniFile, TEXT("smscfg.ini"), AS ( szIniFile ) );
  1196. WritePrivateProfileString(TEXT("Configuration - Client Properties"), TEXT("SMS Unique Identifier"), TEXT(""), szIniFile);
  1197. }
  1198. // Make sure we can delete the file SMS Unique ID file
  1199. //
  1200. StringCchCopy (szDatFile, AS ( szDatFile ), szWindowsDir);
  1201. OPKAddPathN(szDatFile, TEXT("ms\\sms\\core\\data"), AS ( szDatFile ) );
  1202. if (PathIsDirectory(szDatFile)) {
  1203. OPKAddPathN(szDatFile, TEXT("smsuid.dat"), AS ( szDatFile ) );
  1204. SetFileAttributes(szDatFile, FILE_ATTRIBUTE_NORMAL);
  1205. DeleteFile(szDatFile);
  1206. }
  1207. }
  1208. }
  1209. void RemoveDir(LPCTSTR lpDirectory, BOOL fDeleteDir)
  1210. {
  1211. WIN32_FIND_DATA FileFound;
  1212. HANDLE hFile;
  1213. // Validate the parameters.
  1214. //
  1215. if ( ( lpDirectory == NULL ) ||
  1216. ( *lpDirectory == TEXT('\0') ) ||
  1217. ( !SetCurrentDirectory(lpDirectory) ) )
  1218. {
  1219. return;
  1220. }
  1221. //
  1222. // ISSUE-2002/02/26-brucegr: We just called SetCurrentDirectory above!
  1223. //
  1224. // Process all the files and directories in the directory passed in.
  1225. //
  1226. SetCurrentDirectory(lpDirectory);
  1227. if ( (hFile = FindFirstFile(TEXT("*"), &FileFound)) != INVALID_HANDLE_VALUE )
  1228. {
  1229. do
  1230. {
  1231. // First check to see if this is a file (not a directory).
  1232. //
  1233. if ( !( FileFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
  1234. {
  1235. // Make sure we clear the readonly flag
  1236. //
  1237. SetFileAttributes(FileFound.cFileName, FILE_ATTRIBUTE_NORMAL);
  1238. DeleteFile(FileFound.cFileName);
  1239. }
  1240. // Otherwise, make sure the directory is not "." or "..".
  1241. //
  1242. else if ( ( lstrcmp(FileFound.cFileName, TEXT(".")) ) &&
  1243. ( lstrcmp(FileFound.cFileName, TEXT("..")) ) )
  1244. {
  1245. RemoveDir(FileFound.cFileName, TRUE);
  1246. }
  1247. }
  1248. while ( FindNextFile(hFile, &FileFound) );
  1249. FindClose(hFile);
  1250. }
  1251. // Go to the parent directory and remove the current one.
  1252. // We have to make sure and reset the readonly attributes
  1253. // on the dir also.
  1254. //
  1255. SetCurrentDirectory(TEXT(".."));
  1256. if (fDeleteDir)
  1257. {
  1258. SetFileAttributes(lpDirectory, FILE_ATTRIBUTE_NORMAL);
  1259. RemoveDirectory(lpDirectory);
  1260. }
  1261. }
  1262. //
  1263. // DeleteCacheCookies was copy'n'pasted from Cachecpl.cpp
  1264. //
  1265. // Any changes to either version should probably be transfered to both.
  1266. // Minor difference from new/delete (.cpp) to LocalAlloc/LocalFree (.c).
  1267. //
  1268. BOOL DeleteCacheCookies()
  1269. {
  1270. BOOL bRetval = TRUE;
  1271. DWORD dwEntrySize, dwLastEntrySize;
  1272. LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntry;
  1273. HANDLE hCacheDir = NULL;
  1274. dwEntrySize = dwLastEntrySize = MAX_CACHE_ENTRY_INFO_SIZE;
  1275. lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA) LocalAlloc(LPTR, sizeof(BYTE) * dwEntrySize);
  1276. if( lpCacheEntry == NULL)
  1277. {
  1278. bRetval = FALSE;
  1279. goto Exit;
  1280. }
  1281. lpCacheEntry->dwStructSize = dwEntrySize;
  1282. Again:
  1283. if (!(hCacheDir = FindFirstUrlCacheEntryA("cookie:",lpCacheEntry,&dwEntrySize)))
  1284. {
  1285. LocalFree(lpCacheEntry);
  1286. switch(GetLastError())
  1287. {
  1288. case ERROR_NO_MORE_ITEMS:
  1289. goto Exit;
  1290. case ERROR_INSUFFICIENT_BUFFER:
  1291. lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA)
  1292. LocalAlloc(LPTR, sizeof(BYTE) * dwEntrySize );
  1293. if( lpCacheEntry == NULL)
  1294. {
  1295. bRetval = FALSE;
  1296. goto Exit;
  1297. }
  1298. lpCacheEntry->dwStructSize = dwLastEntrySize = dwEntrySize;
  1299. goto Again;
  1300. default:
  1301. bRetval = FALSE;
  1302. goto Exit;
  1303. }
  1304. }
  1305. do
  1306. {
  1307. if (lpCacheEntry->CacheEntryType & COOKIE_CACHE_ENTRY)
  1308. DeleteUrlCacheEntryA(lpCacheEntry->lpszSourceUrlName);
  1309. dwEntrySize = dwLastEntrySize;
  1310. Retry:
  1311. if (!FindNextUrlCacheEntryA(hCacheDir,lpCacheEntry, &dwEntrySize))
  1312. {
  1313. LocalFree(lpCacheEntry);
  1314. switch(GetLastError())
  1315. {
  1316. case ERROR_NO_MORE_ITEMS:
  1317. goto Exit;
  1318. case ERROR_INSUFFICIENT_BUFFER:
  1319. lpCacheEntry = (LPINTERNET_CACHE_ENTRY_INFOA)
  1320. LocalAlloc(LPTR, sizeof(BYTE) * dwEntrySize );
  1321. if( lpCacheEntry == NULL)
  1322. {
  1323. bRetval = FALSE;
  1324. goto Exit;
  1325. }
  1326. lpCacheEntry->dwStructSize = dwLastEntrySize = dwEntrySize;
  1327. goto Retry;
  1328. default:
  1329. bRetval = FALSE;
  1330. goto Exit;
  1331. }
  1332. }
  1333. }
  1334. while (TRUE);
  1335. Exit:
  1336. if (hCacheDir)
  1337. FindCloseUrlCache(hCacheDir);
  1338. return bRetval;
  1339. }
  1340. void ClearIEHistory (
  1341. VOID
  1342. )
  1343. /*++
  1344. ===============================================================================
  1345. Routine Description:
  1346. This routine clears the IE History.
  1347. Arguments:
  1348. None.
  1349. Return Value:
  1350. ===============================================================================
  1351. --*/
  1352. {
  1353. IUrlHistoryStg2* pHistory = NULL ; // We need this interface for clearing the history.
  1354. HRESULT hr;
  1355. HKEY hkeyInternational = NULL;
  1356. ULONG_PTR lres = 0;
  1357. // Remove all the entries here.
  1358. RegDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\TypedURLs"));
  1359. RegDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"));
  1360. // this broadcast will nuke the address bars
  1361. SendMessageTimeoutW( HWND_BROADCAST,
  1362. WM_SETTINGCHANGE,
  1363. 0,
  1364. (LPARAM)TEXT("Software\\Microsoft\\Internet Explorer\\TypedURLs"),
  1365. SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG,
  1366. 30 * 1000,
  1367. &lres);
  1368. SendMessageTimeoutW( HWND_BROADCAST,
  1369. WM_SETTINGCHANGE,
  1370. 0,
  1371. (LPARAM)TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"),
  1372. SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG,
  1373. 30 * 1000,
  1374. &lres);
  1375. // we remove these reg values when history is
  1376. // cleared. This will reset the encoding menu UI to the defaults.
  1377. if (ERROR_SUCCESS ==
  1378. RegOpenKeyEx(
  1379. HKEY_CURRENT_USER,
  1380. TEXT("Software\\Microsoft\\Internet Explorer\\International"),
  1381. 0,
  1382. KEY_WRITE,
  1383. &hkeyInternational))
  1384. {
  1385. ASSERT(hkeyInternational);
  1386. RegDeleteValue(hkeyInternational, TEXT("CpCache"));
  1387. RegDeleteValue(hkeyInternational, TEXT("CNum_CpCache"));
  1388. RegCloseKey(hkeyInternational);
  1389. }
  1390. //
  1391. // Init the Com Library
  1392. //
  1393. CoInitialize(NULL);
  1394. // Load the correct Class and request IUrlHistoryStg2
  1395. hr = CoCreateInstance( &CLSID_CUrlHistory,
  1396. NULL,
  1397. CLSCTX_INPROC_SERVER,
  1398. &IID_IUrlHistoryStg2,
  1399. &pHistory );
  1400. //
  1401. // If succeeded Clear the history
  1402. if (SUCCEEDED(hr))
  1403. {
  1404. // Clear the IE History
  1405. hr = IUrlHistoryStg2_ClearHistory(pHistory);
  1406. }
  1407. // Release our reference to the
  1408. if ( pHistory )
  1409. {
  1410. IUrlHistoryStg2_Release(pHistory);
  1411. }
  1412. // Un Init the Com Library
  1413. CoUninitialize();
  1414. }
  1415. void NukeTemporaryFiles(
  1416. VOID
  1417. )
  1418. /*++
  1419. ===============================================================================
  1420. Routine Description:
  1421. This routine clears the temporary folder and recycle bin for template user.
  1422. Arguments:
  1423. None.
  1424. Return Value:
  1425. ===============================================================================
  1426. --*/
  1427. {
  1428. TCHAR szTempDir[MAX_PATH] = TEXT(""),
  1429. szTempInetFilesDir[MAX_PATH] = TEXT(""),
  1430. szProfileDir[MAX_PATH] = TEXT(""),
  1431. szCurrentDir[MAX_PATH] = TEXT("");
  1432. DWORD dwSize;
  1433. HANDLE hFile;
  1434. WIN32_FIND_DATA FileFound;
  1435. //
  1436. // Save our current directory, so we can set it back later.
  1437. //
  1438. GetCurrentDirectory(MAX_PATH, szCurrentDir);
  1439. dwSize = sizeof(szProfileDir)/sizeof(szProfileDir[0]);
  1440. if ( !GetProfilesDirectory(szProfileDir, &dwSize) &&
  1441. !SetCurrentDirectory(szProfileDir) )
  1442. return;
  1443. //
  1444. // ISSUE-2002/02/26-brucegr: We just called SetCurrentDirectory above!
  1445. //
  1446. //
  1447. // Clear the I.E History folder.
  1448. //
  1449. ClearIEHistory ( ) ;
  1450. //
  1451. // Clear tmp files for all profile directories.
  1452. //
  1453. SetCurrentDirectory(szProfileDir);
  1454. if ( (hFile = FindFirstFile(TEXT("*"), &FileFound)) != INVALID_HANDLE_VALUE )
  1455. {
  1456. do
  1457. {
  1458. // Otherwise, make sure the directory is not "." or "..".
  1459. //
  1460. if ( (FileFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1461. ( lstrcmp(FileFound.cFileName, TEXT(".")) ) &&
  1462. ( lstrcmp(FileFound.cFileName, TEXT("..")) ) )
  1463. {
  1464. TCHAR szTemp1[MAX_PATH] = TEXT("");
  1465. //
  1466. // Clear the Temp folder.
  1467. //
  1468. if ( LoadString(ghInstance, IDS_TEMP_DIR, szTemp1, MAX_PATH) )
  1469. {
  1470. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1471. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1472. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1473. RemoveDir(szTempDir, FALSE);
  1474. }
  1475. //
  1476. // Clear the History.IE5 folder.
  1477. //
  1478. if ( LoadString(ghInstance, IDS_HISTORY_DIR_IE5, szTemp1, MAX_PATH) )
  1479. {
  1480. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1481. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1482. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1483. RemoveDir(szTempDir, TRUE);
  1484. }
  1485. //
  1486. // Clear the History folder.
  1487. //
  1488. if ( LoadString(ghInstance, IDS_HISTORY_DIR, szTemp1, MAX_PATH) )
  1489. {
  1490. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1491. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1492. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1493. RemoveDir(szTempDir, FALSE);
  1494. }
  1495. //
  1496. // Clear the Local Settings\Application Data\Microsoft\Credentials.
  1497. //
  1498. if ( LoadString(ghInstance, IDS_SID_DIR1, szTemp1, MAX_PATH) )
  1499. {
  1500. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1501. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1502. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1503. RemoveDir(szTempDir, FALSE);
  1504. }
  1505. //
  1506. // Clear the Application Data\Microsoft\Credentials
  1507. //
  1508. if ( LoadString(ghInstance, IDS_SID_DIR2, szTemp1, MAX_PATH) )
  1509. {
  1510. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1511. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1512. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1513. RemoveDir(szTempDir, FALSE);
  1514. }
  1515. //
  1516. // Clear the Application Data\Microsoft\Crypto\\RSA.
  1517. //
  1518. if ( LoadString(ghInstance, IDS_SID_DIR3, szTemp1, MAX_PATH) )
  1519. {
  1520. StringCchCopy (szTempDir, AS ( szTempDir ), szProfileDir);
  1521. OPKAddPathN(szTempDir, FileFound.cFileName, AS ( szTempDir ) );
  1522. OPKAddPathN(szTempDir, szTemp1, AS ( szTempDir ) );
  1523. RemoveDir(szTempDir, FALSE);
  1524. }
  1525. //
  1526. // Clear the Temporary Internet files and cookies.
  1527. //
  1528. if ( LoadString(ghInstance, IDS_TEMP_INTERNET_DIR, szTemp1, MAX_PATH) )
  1529. {
  1530. StringCchCopy ( szTempInetFilesDir, AS ( szTempInetFilesDir), szProfileDir);
  1531. OPKAddPathN(szTempInetFilesDir, FileFound.cFileName, AS ( szTempInetFilesDir ) );
  1532. OPKAddPathN(szTempInetFilesDir, szTemp1, AS ( szTempInetFilesDir ) );
  1533. FreeUrlCacheSpace(szTempInetFilesDir, 100, 0 /*remove all*/);
  1534. DeleteCacheCookies();
  1535. RemoveDir(szTempInetFilesDir, FALSE);
  1536. }
  1537. }
  1538. }
  1539. while ( FindNextFile(hFile, &FileFound) );
  1540. FindClose(hFile);
  1541. }
  1542. //
  1543. // Set back our current directory.
  1544. //
  1545. SetCurrentDirectory(szCurrentDir);
  1546. //
  1547. // Clear any recycle bin files.
  1548. //
  1549. SHEmptyRecycleBin(NULL, NULL, SHERB_NOSOUND|SHERB_NOCONFIRMATION|SHERB_NOPROGRESSUI);
  1550. }
  1551. DWORD NukeLKGControlSet(
  1552. VOID
  1553. )
  1554. /*++
  1555. ===============================================================================
  1556. Routine Description:
  1557. This routine will delete the last known good control set from the registry.
  1558. The reason is LKG doesn't make sense for the first boot. Also, if the BIOS
  1559. clock of a cloned machine is earlier than the creation time of a cloned
  1560. image, any change made to the CurrentControlSet before adjusting the clock
  1561. will not sync to LKG.
  1562. The code is adapted from base\screg\sc\server\bootcfg.cxx
  1563. Since we can't delete LKG directly because quite a few of the subkeys are
  1564. in use, this routine changes the system\select!LastKnownGood to a new Id
  1565. instead.
  1566. Arguments:
  1567. None.
  1568. Return Value:
  1569. NO_ERROR or other WIN32 error.
  1570. ===============================================================================
  1571. --*/
  1572. {
  1573. //
  1574. // ISSUE-2002/02/26-brucegr: Should rewrite to get highest DWORD value in Select key, then increment and write to LKG.
  1575. //
  1576. #define SELECT_KEY L"system\\select"
  1577. #define CURRENT_ID 0
  1578. #define DEFAULT_ID 1
  1579. #define LKG_ID 2
  1580. #define FAILED_ID 3
  1581. #define NUM_IDS 4
  1582. //
  1583. // ISSUE-2002/02/26-brucegr: Get rid of NUM_IDS and use ARRAYSIZE macro!
  1584. //
  1585. static const LPCWSTR SelectValueNames[NUM_IDS] =
  1586. {
  1587. L"Current",
  1588. L"Default",
  1589. L"LastKnownGood",
  1590. L"Failed"
  1591. };
  1592. DWORD idArray[NUM_IDS];
  1593. HKEY selectKey = 0;
  1594. DWORD status = NO_ERROR;
  1595. DWORD bufferSize = 0;
  1596. DWORD newId = 0;
  1597. DWORD i = 0;
  1598. //
  1599. // Get the Select Key
  1600. //
  1601. status = RegOpenKeyEx(
  1602. HKEY_LOCAL_MACHINE,
  1603. SELECT_KEY,
  1604. 0L,
  1605. KEY_QUERY_VALUE | KEY_SET_VALUE,
  1606. &selectKey);
  1607. if (status != NO_ERROR)
  1608. {
  1609. return status;
  1610. }
  1611. //
  1612. // Fill in the idArray
  1613. //
  1614. for (i = 0; i < NUM_IDS; i++)
  1615. {
  1616. bufferSize = sizeof(DWORD);
  1617. //
  1618. // ISSUE-2002/02/26-brucegr: Check data type matches REG_DWORD
  1619. //
  1620. status = RegQueryValueEx(
  1621. selectKey,
  1622. SelectValueNames[i],
  1623. NULL,
  1624. NULL,
  1625. (LPBYTE)&idArray[i],
  1626. &bufferSize);
  1627. if (status != NO_ERROR)
  1628. {
  1629. idArray[i] = 0;
  1630. }
  1631. }
  1632. status = ERROR_NO_MORE_ITEMS;
  1633. for(newId = 1; newId < 1000; newId++)
  1634. {
  1635. BOOL inArray = FALSE;
  1636. for(i = 0; i < NUM_IDS; i++)
  1637. {
  1638. if(idArray[i] == newId)
  1639. {
  1640. inArray = TRUE;
  1641. break;
  1642. }
  1643. }
  1644. if (!inArray)
  1645. {
  1646. status = RegSetValueEx(
  1647. selectKey,
  1648. SelectValueNames[LKG_ID],
  1649. 0,
  1650. REG_DWORD,
  1651. (LPBYTE)&newId,
  1652. sizeof(DWORD));
  1653. break;
  1654. }
  1655. }
  1656. RegCloseKey(selectKey);
  1657. return status;
  1658. }
  1659. BOOL
  1660. DeleteAdapterGuidsKeys(
  1661. VOID
  1662. )
  1663. {
  1664. HKEY hKey, hSubKey;
  1665. DWORD dwError = NO_ERROR;
  1666. int i = 0;
  1667. TCHAR SubKeyName[MAX_PATH * 2];
  1668. //
  1669. // Open HKLM\System\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}
  1670. //
  1671. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1672. TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"),
  1673. 0,
  1674. KEY_ALL_ACCESS,
  1675. &hKey );
  1676. if(dwError != NO_ERROR)
  1677. {
  1678. SetLastError(dwError);
  1679. return FALSE;
  1680. }
  1681. //
  1682. // Now enumerate all subkeys. For each subkey delete the adapter GUID.
  1683. //
  1684. while( (dwError = RegEnumKey( hKey, i, SubKeyName, sizeof(SubKeyName)/sizeof(SubKeyName[0]))) == ERROR_SUCCESS)
  1685. {
  1686. //
  1687. // Check if the key is a probable GUID.
  1688. // If it's a GUID key, delete the adapter GUIDs only
  1689. //
  1690. if (SubKeyName[0] == TEXT('{'))
  1691. {
  1692. //
  1693. // If we were able to delete the key,then its okay, other wise
  1694. // increment the counter.
  1695. //
  1696. if ( ( dwError = SHDeleteKey(hKey, SubKeyName) ) != ERROR_SUCCESS )
  1697. i++;
  1698. }
  1699. else
  1700. {
  1701. //
  1702. // If we didn't find one go to next subkey.
  1703. //
  1704. i++;
  1705. }
  1706. }
  1707. RegCloseKey( hKey );
  1708. return TRUE;
  1709. }
  1710. BOOL
  1711. RemoveNetworkSettings(
  1712. LPTSTR lpszSysprepINFPath
  1713. )
  1714. /*++
  1715. ===============================================================================
  1716. Routine Description:
  1717. This routine will enumerate each physical NIC, call into the netsetup
  1718. code to save the settings, and then delete the network card.
  1719. When a new NIC is enumerated on the target machine, netsetup will apply the
  1720. settings that were saved away
  1721. If the LegacyNIC != 0 value exists in SYSPREP.INF in the [Unattended] section, then
  1722. the previous behavior will be preserved, since removing a legacy NIC card
  1723. will not work, because it will not be re-enumerated/detected on the next boot
  1724. Arguments:
  1725. lpszSysprepINFPath pointer to the SYSPREP.INF file. Can be NULL, in which case
  1726. a non-legacy NIC is assumed
  1727. Return Value:
  1728. TRUE if successful.
  1729. FALSE if any errors encountered
  1730. ===============================================================================
  1731. --*/
  1732. {
  1733. HDEVINFO DeviceInfoSet;
  1734. DWORD dwIdx;
  1735. SP_DEVINFO_DATA DevInfoData;
  1736. HKEY hDevRegKey;
  1737. DWORD dwChar;
  1738. DWORD dwSize;
  1739. FARPROC pNetSetupPrepareSysPrep = NULL;
  1740. BOOL DoLegacy = FALSE;
  1741. HMODULE hNetShell = LoadLibraryA( "netshell.dll" );
  1742. if (hNetShell) {
  1743. pNetSetupPrepareSysPrep = GetProcAddress( hNetShell, "NetSetupPrepareSysPrep" );
  1744. if (!pNetSetupPrepareSysPrep) {
  1745. DoLegacy = TRUE;
  1746. FreeLibrary( hNetShell );
  1747. }
  1748. }
  1749. else {
  1750. return FALSE;
  1751. }
  1752. // See if we are dealing with a legacy NIC
  1753. if ((lpszSysprepINFPath != NULL)
  1754. && GetPrivateProfileInt( TEXT( "Unattended" ),
  1755. TEXT( "LegacyNIC" ),
  1756. 0,
  1757. lpszSysprepINFPath)) {
  1758. //
  1759. // ISSUE-2002/02/26-brucegr: If we set DoLegacy to TRUE, then we don't free hNetShell!
  1760. //
  1761. DoLegacy = TRUE;
  1762. }
  1763. if (!DoLegacy)
  1764. {
  1765. // Call the netcfg function to save the networking settings
  1766. pNetSetupPrepareSysPrep();
  1767. FreeLibrary( hNetShell );
  1768. // Enumerate and delete all phyiscal NICs
  1769. DeviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_NET,
  1770. NULL,
  1771. NULL,
  1772. DIGCF_PRESENT);
  1773. if(DeviceInfoSet == INVALID_HANDLE_VALUE)
  1774. {
  1775. return FALSE;
  1776. }
  1777. dwIdx = 0;
  1778. DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1779. while (SetupDiEnumDeviceInfo(DeviceInfoSet, dwIdx, &DevInfoData))
  1780. {
  1781. hDevRegKey = SetupDiOpenDevRegKey(DeviceInfoSet,
  1782. &DevInfoData,
  1783. DICS_FLAG_GLOBAL,
  1784. 0,
  1785. DIREG_DRV,
  1786. KEY_READ);
  1787. if (hDevRegKey == INVALID_HANDLE_VALUE)
  1788. {
  1789. // Not sure why it would ever return INVALID_HANDLE_VALUE, but
  1790. // we don't care and should continue.
  1791. ++dwIdx;
  1792. continue;
  1793. }
  1794. dwChar = 0;
  1795. dwSize = sizeof(DWORD);
  1796. RegQueryValueEx(hDevRegKey,
  1797. L"Characteristics",
  1798. NULL,
  1799. NULL,
  1800. (LPBYTE) &dwChar,
  1801. &dwSize);
  1802. RegCloseKey(hDevRegKey);
  1803. if (dwChar & NCF_PHYSICAL)
  1804. {
  1805. // This is one to delete
  1806. SetupDiCallClassInstaller(DIF_REMOVE, DeviceInfoSet, &DevInfoData);
  1807. }
  1808. ++dwIdx;
  1809. }
  1810. // Cleanup
  1811. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  1812. }
  1813. //
  1814. // Delete the adapter GUIDs keys so we don't get multiple Local Area Connection # displays.
  1815. //
  1816. return DeleteAdapterGuidsKeys();
  1817. }
  1818. BOOL
  1819. ProcessUniquenessValue(
  1820. LPTSTR lpszDLLPath
  1821. )
  1822. {
  1823. BOOL bRet = FALSE;
  1824. //
  1825. // Make sure we were passed something valid...
  1826. //
  1827. if ( lpszDLLPath && *lpszDLLPath )
  1828. {
  1829. LPWSTR pSrch;
  1830. //
  1831. // Look for the comma that separates the DLL and the entrypoint...
  1832. //
  1833. if ( pSrch = wcschr( lpszDLLPath, L',' ) )
  1834. {
  1835. CHAR szEntryPointA[MAX_PATH] = {0};
  1836. // We found one, now NULL the string at the comma...
  1837. //
  1838. *(pSrch++) = L'\0';
  1839. //
  1840. // If there's still something after the comma, and we can convert it
  1841. // into ANSI for GetProcAddress, then let's proceed...
  1842. //
  1843. if ( *pSrch &&
  1844. ( 0 != WideCharToMultiByte( CP_ACP,
  1845. 0,
  1846. pSrch,
  1847. -1,
  1848. szEntryPointA,
  1849. ARRAYSIZE(szEntryPointA),
  1850. NULL,
  1851. NULL ) ) )
  1852. {
  1853. HMODULE hModule = NULL;
  1854. try
  1855. {
  1856. //
  1857. // Load and call the entry point.
  1858. //
  1859. if ( hModule = LoadLibrary( lpszDLLPath ) )
  1860. {
  1861. FARPROC fpEntryPoint;
  1862. if ( fpEntryPoint = GetProcAddress(hModule, szEntryPointA) )
  1863. {
  1864. //
  1865. // Do it, ignoring any return value/errors
  1866. //
  1867. fpEntryPoint();
  1868. //
  1869. // We made it this far, consider this a success...
  1870. //
  1871. bRet = TRUE;
  1872. }
  1873. }
  1874. }
  1875. except(EXCEPTION_EXECUTE_HANDLER)
  1876. {
  1877. //
  1878. // We don't do anything with the exception code...
  1879. //
  1880. }
  1881. //
  1882. // Free the library outside the try/except block in case the function faulted.
  1883. //
  1884. if ( hModule )
  1885. {
  1886. FreeLibrary( hModule );
  1887. }
  1888. }
  1889. }
  1890. }
  1891. return bRet;
  1892. }
  1893. VOID
  1894. ProcessUniquenessKey(
  1895. BOOL fBeforeReseal
  1896. )
  1897. {
  1898. HKEY hKey;
  1899. TCHAR szRegPath[MAX_PATH] = {0};
  1900. LPTSTR lpszBasePath = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\SysPrep\\");
  1901. //
  1902. // Build a path to the registry key we want to process...
  1903. //
  1904. lstrcpyn( szRegPath, lpszBasePath, ARRAYSIZE(szRegPath) );
  1905. lstrcpyn( szRegPath + lstrlen(szRegPath),
  1906. fBeforeReseal ? TEXT("SysprepBeforeExecute") : TEXT("SysprepAfterExecute"),
  1907. ARRAYSIZE(szRegPath) - lstrlen(szRegPath) );
  1908. //
  1909. // We want to make sure an Administrator is doing this, so get KEY_ALL_ACCESS
  1910. //
  1911. if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1912. szRegPath,
  1913. 0,
  1914. KEY_ALL_ACCESS,
  1915. &hKey ) )
  1916. {
  1917. DWORD dwValues = 0,
  1918. dwMaxValueLen = 0,
  1919. dwMaxValueNameLen = 0;
  1920. //
  1921. // Query the key to find out some information we care about...
  1922. //
  1923. if ( ( ERROR_SUCCESS == RegQueryInfoKey( hKey, // hKey
  1924. NULL, // lpClass
  1925. NULL, // lpcClass
  1926. NULL, // lpReserved
  1927. NULL, // lpcSubKeys
  1928. NULL, // lpcMaxSubKeyLen
  1929. NULL, // lpcMaxClassLen
  1930. &dwValues, // lpcValues
  1931. &dwMaxValueNameLen, // lpcMaxValueNameLen
  1932. &dwMaxValueLen, // lpcMaxValueLen
  1933. NULL, // lpcbSecurityDescriptor
  1934. NULL ) ) && // lpftLastWriteTime
  1935. ( dwValues > 0 ) &&
  1936. ( dwMaxValueNameLen > 0) &&
  1937. ( dwMaxValueLen > 0 ) )
  1938. {
  1939. //
  1940. // Allocate buffers large enough to hold the data we want...
  1941. //
  1942. LPBYTE lpData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxValueLen );
  1943. LPTSTR lpValueName = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ( dwMaxValueNameLen + 1 ) * sizeof(TCHAR) );
  1944. //
  1945. // Make sure we could allocate our buffers... otherwise bail out
  1946. //
  1947. if ( lpData && lpValueName )
  1948. {
  1949. DWORD dwIndex = 0;
  1950. BOOL bContinue = TRUE;
  1951. //
  1952. // Enumerate through the key values and call the DLL entrypoints...
  1953. //
  1954. while ( bContinue )
  1955. {
  1956. DWORD dwType,
  1957. cbData = dwMaxValueLen,
  1958. dwValueNameLen = dwMaxValueNameLen + 1;
  1959. bContinue = ( ERROR_SUCCESS == RegEnumValue( hKey,
  1960. dwIndex++,
  1961. lpValueName,
  1962. &dwValueNameLen,
  1963. NULL,
  1964. &dwType,
  1965. lpData,
  1966. &cbData ) );
  1967. //
  1968. // Make sure we got some data of the correct format...
  1969. //
  1970. if ( bContinue && ( REG_SZ == dwType ) && ( cbData > 0 ) )
  1971. {
  1972. //
  1973. // Now split up the string and call the entrypoints...
  1974. //
  1975. ProcessUniquenessValue( (LPTSTR) lpData );
  1976. }
  1977. }
  1978. }
  1979. //
  1980. // Clean up any buffers we may have allocated...
  1981. //
  1982. if ( lpData )
  1983. {
  1984. HeapFree( GetProcessHeap(), 0, lpData );
  1985. }
  1986. if ( lpValueName )
  1987. {
  1988. HeapFree( GetProcessHeap(), 0, lpValueName );
  1989. }
  1990. }
  1991. //
  1992. // Close the key...
  1993. //
  1994. RegCloseKey( hKey );
  1995. }
  1996. }
  1997. VOID
  1998. RunExternalUniqueness(
  1999. VOID
  2000. )
  2001. /*++
  2002. ===============================================================================
  2003. Routine Description:
  2004. This routine will call out to any external dlls that will allow
  2005. 3rd party apps to make their stuff unique.
  2006. We'll look in 2 inf files:
  2007. %windir%\inf\minioc.inf
  2008. %systemroot%\sysprep\provider.inf
  2009. In each of these files, we'll look in the [SysprepBeforeExecute] section
  2010. for any entries. The entries must look like:
  2011. dllname,entrypoint
  2012. We'll load the dll and call into the entry point. Errors are ignored.
  2013. Arguments:
  2014. None.
  2015. Return Value:
  2016. TRUE if successful.
  2017. FALSE if any errors encountered
  2018. ===============================================================================
  2019. --*/
  2020. {
  2021. FARPROC MyProc;
  2022. WCHAR InfPath[MAX_PATH];
  2023. WCHAR DllName[MAX_PATH];
  2024. WCHAR EntryPointNameW[MAX_PATH];
  2025. CHAR EntryPointNameA[MAX_PATH];
  2026. HINF AnswerInf;
  2027. HMODULE DllHandle;
  2028. INFCONTEXT InfContext;
  2029. DWORD i;
  2030. PCWSTR SectionName = L"SysprepBeforeExecute";
  2031. BOOL LineExists;
  2032. //
  2033. // =================================
  2034. // Minioc.inf
  2035. // =================================
  2036. //
  2037. //
  2038. // Build the path.
  2039. //
  2040. if (!GetWindowsDirectory( InfPath, MAX_PATH ))
  2041. return;
  2042. StringCchCat( InfPath, AS ( InfPath ), TEXT("\\inf\\minioc.inf") );
  2043. //
  2044. // See if he's got an entry
  2045. // section.
  2046. //
  2047. // NTRAID#NTBUG9-551511-2002/02/26-brucegr: We should make sure that MINIOC.INF is digitally signed before opening!
  2048. // ISSUE-2002/02/26-brucegr: You can OR in both INF style bits!
  2049. //
  2050. AnswerInf = SetupOpenInfFile( InfPath, NULL, INF_STYLE_WIN4, NULL );
  2051. if( AnswerInf == INVALID_HANDLE_VALUE ) {
  2052. //
  2053. // Try an old-style.
  2054. //
  2055. AnswerInf = SetupOpenInfFile( InfPath, NULL, INF_STYLE_OLDNT, NULL );
  2056. }
  2057. if( AnswerInf != INVALID_HANDLE_VALUE ) {
  2058. //
  2059. // Process each line in our section
  2060. //
  2061. LineExists = SetupFindFirstLine( AnswerInf, SectionName, NULL, &InfContext );
  2062. while( LineExists ) {
  2063. //
  2064. // ISSUE-2002/02/26-brucegr: Why not use SetupGetStringFieldA with EntryPointNameA?
  2065. //
  2066. if( SetupGetStringField( &InfContext, 1, DllName, sizeof(DllName)/sizeof(TCHAR), NULL) ) {
  2067. if( SetupGetStringField( &InfContext, 2, EntryPointNameW, sizeof(EntryPointNameW)/sizeof(TCHAR), NULL )) {
  2068. DllHandle = NULL;
  2069. //
  2070. // Load and call the entry point.
  2071. //
  2072. try {
  2073. if( DllHandle = LoadLibrary(DllName) ) {
  2074. //
  2075. // No Unicode version of GetProcAddress(). Convert string to ANSI.
  2076. //
  2077. i = WideCharToMultiByte(CP_ACP,0,EntryPointNameW,-1,EntryPointNameA,MAX_PATH,NULL,NULL);
  2078. if( MyProc = GetProcAddress(DllHandle, EntryPointNameA) ) {
  2079. //
  2080. // Do it, ignoring any return value/errors
  2081. //
  2082. MyProc();
  2083. }
  2084. }
  2085. } except(EXCEPTION_EXECUTE_HANDLER) {
  2086. }
  2087. if( DllHandle ) {
  2088. FreeLibrary( DllHandle );
  2089. }
  2090. }
  2091. }
  2092. LineExists = SetupFindNextLine(&InfContext,&InfContext);
  2093. }
  2094. SetupCloseInfFile( AnswerInf );
  2095. }
  2096. //
  2097. // ISSUE-2002/02/26-brucegr: Why are we duplicating the same INF processing code from above?!!!!
  2098. //
  2099. //
  2100. // =================================
  2101. // Provider.inf
  2102. // =================================
  2103. //
  2104. ProcessUniquenessKey( TRUE );
  2105. }
  2106. BOOL PrepForSidGen
  2107. (
  2108. void
  2109. )
  2110. {
  2111. DWORD l;
  2112. HKEY hKey, hKeyNew;
  2113. DWORD d;
  2114. DWORD Size;
  2115. DWORD Type;
  2116. TCHAR SetupExecuteValue[1024];
  2117. //
  2118. // =================================
  2119. // Set the value of the SetupExecute subkey.
  2120. // =================================
  2121. //
  2122. //
  2123. // Open the Session Manager key.
  2124. //
  2125. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2126. TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager"),
  2127. 0,
  2128. KEY_ALL_ACCESS,
  2129. &hKey );
  2130. if(l != NO_ERROR)
  2131. {
  2132. SetLastError(l);
  2133. return FALSE;
  2134. }
  2135. //
  2136. // Set the Key.
  2137. //
  2138. StringCchCopy ( SetupExecuteValue, AS ( SetupExecuteValue ), TEXT(SYSCLONE_PART2) );
  2139. SetupExecuteValue[lstrlen(SetupExecuteValue) + 1] = L'\0';
  2140. //
  2141. // ISSUE-2002/02/26-brucegr: Are we stomping anything that is already in SetupExecute?
  2142. //
  2143. l = RegSetValueEx(hKey,
  2144. TEXT("SetupExecute"),
  2145. 0,
  2146. REG_MULTI_SZ,
  2147. (CONST BYTE *)SetupExecuteValue,
  2148. (lstrlen( SetupExecuteValue ) + 2) * sizeof(TCHAR));
  2149. RegCloseKey(hKey);
  2150. if(l != NO_ERROR)
  2151. {
  2152. SetLastError(l);
  2153. return FALSE;
  2154. }
  2155. //
  2156. // =================================
  2157. // Let's bump the size of the registry quota a bit so that
  2158. // setupcl.exe can run. He'll pop it back down.
  2159. // =================================
  2160. //
  2161. //
  2162. // Open HKLM\System\CurrentControlSet\Control
  2163. //
  2164. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2165. TEXT("SYSTEM\\CurrentControlSet\\Control"),
  2166. 0,
  2167. KEY_ALL_ACCESS,
  2168. &hKey );
  2169. if(l != NO_ERROR)
  2170. {
  2171. SetLastError(l);
  2172. return FALSE;
  2173. }
  2174. //
  2175. // Query the value of the RegistrySizeLimit Key.
  2176. //
  2177. l = RegQueryValueEx(hKey,
  2178. TEXT("RegistrySizeLimit"),
  2179. NULL,
  2180. &Type,
  2181. (LPBYTE)&d,
  2182. &Size);
  2183. if( l == ERROR_SUCCESS )
  2184. {
  2185. //
  2186. // Got it. Bump the value.
  2187. //
  2188. d += REGISTRY_QUOTA_BUMP; //Increase by some amount to load the repair hives
  2189. //
  2190. // Set the key.
  2191. //
  2192. l = RegSetValueEx(hKey,
  2193. TEXT("RegistrySizeLimit"),
  2194. 0,
  2195. REG_DWORD,
  2196. (CONST BYTE *)&d,
  2197. sizeof(DWORD) );
  2198. if(l != NO_ERROR)
  2199. {
  2200. SetLastError(l);
  2201. //
  2202. // ISSUE-2002/02/26-brucegr: Need to call RegCloseKey!
  2203. //
  2204. return FALSE;
  2205. }
  2206. }
  2207. else
  2208. {
  2209. //
  2210. // Darn! The value probably doesn't exist.
  2211. // Ignore it and expect stuff to work. Only repair hives cannot be fixed
  2212. //
  2213. }
  2214. RegCloseKey(hKey);
  2215. //
  2216. // =================================
  2217. // See if anyone wants to reset uniqueness
  2218. // in their component.
  2219. // =================================
  2220. //
  2221. RunExternalUniqueness();
  2222. return TRUE;
  2223. }
  2224. BOOL SetCloneTag
  2225. (
  2226. void
  2227. )
  2228. {
  2229. HKEY hKey;
  2230. DWORD l;
  2231. TCHAR DateString[1024];
  2232. time_t ltime;
  2233. LPTSTR lpszDate;
  2234. //
  2235. // =================================
  2236. // Put a unique identifier into the registry so we know this machine
  2237. // has been cloned.
  2238. // =================================
  2239. //
  2240. //
  2241. // Open HKLM\System\Setup
  2242. //
  2243. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2244. TEXT("SYSTEM\\Setup"),
  2245. 0,
  2246. KEY_ALL_ACCESS,
  2247. &hKey );
  2248. if(l != NO_ERROR)
  2249. {
  2250. SetLastError(l);
  2251. return FALSE;
  2252. }
  2253. //
  2254. // Set HKLM\System\Setup\CloneTag. We're going to
  2255. // pickup a date string and write it out.
  2256. //
  2257. time( &ltime );
  2258. ZeroMemory(DateString, sizeof(DateString));
  2259. //
  2260. // ISSUE-2002/02/26-brucegr: This function smells horrid!
  2261. //
  2262. lpszDate = _wctime( &ltime );
  2263. if ( lpszDate )
  2264. {
  2265. StringCchCopy( DateString, AS ( DateString ), lpszDate );
  2266. l = RegSetValueEx(hKey,
  2267. TEXT("CloneTag"),
  2268. 0,
  2269. REG_MULTI_SZ,
  2270. (CONST BYTE *)DateString,
  2271. (lstrlen( DateString ) + 2) * sizeof(TCHAR));
  2272. }
  2273. RegCloseKey(hKey);
  2274. if(l != NO_ERROR)
  2275. {
  2276. SetLastError(l);
  2277. return FALSE;
  2278. }
  2279. return (TRUE);
  2280. }
  2281. BOOL SetBigLbaSupport
  2282. (
  2283. LPTSTR lpszSysprepINFPath
  2284. )
  2285. {
  2286. HKEY hKey;
  2287. DWORD dwError, dwValue;
  2288. TCHAR szEnableBigLba[MAX_PATH] = TEXT("\0");
  2289. if ( ( lpszSysprepINFPath ) &&
  2290. ( *lpszSysprepINFPath ) &&
  2291. ( GetPrivateProfileString( TEXT( "Unattended" ), TEXT( "EnableBigLba" ), L"", szEnableBigLba, sizeof(szEnableBigLba)/sizeof(TCHAR), lpszSysprepINFPath ) ) )
  2292. {
  2293. // They would like to enable BigLba support. If the user does not specify "Yes" for this key, we do not
  2294. // touch the key (even if they specify "No"). This is By Design
  2295. //
  2296. if (LSTRCMPI(szEnableBigLba, TEXT("YES")) == 0)
  2297. {
  2298. // Open base key and subkey
  2299. //
  2300. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2301. TEXT("System\\CurrentControlSet\\Services\\Atapi\\Parameters"),
  2302. 0,
  2303. KEY_ALL_ACCESS,
  2304. &hKey );
  2305. // Determine if opening the key was successful
  2306. //
  2307. if(dwError != NO_ERROR)
  2308. {
  2309. SetLastError(dwError);
  2310. return FALSE;
  2311. }
  2312. // Set the value in the registry
  2313. //
  2314. dwValue = 1;
  2315. dwError = RegSetValueEx(hKey,
  2316. TEXT("EnableBigLba"),
  2317. 0,
  2318. REG_DWORD,
  2319. (CONST BYTE *)&dwValue,
  2320. sizeof(DWORD));
  2321. // Close the key
  2322. //
  2323. RegCloseKey(hKey);
  2324. // Return the error if the SetValue failed
  2325. //
  2326. if(dwError != NO_ERROR)
  2327. {
  2328. SetLastError(dwError);
  2329. return FALSE;
  2330. }
  2331. }
  2332. }
  2333. return TRUE;
  2334. }
  2335. BOOL RemoveTapiSettings
  2336. (
  2337. LPTSTR lpszSysprepINFPath
  2338. )
  2339. {
  2340. HKEY hKey;
  2341. DWORD dwError, dwValue;
  2342. TCHAR szTapiConfigured[MAX_PATH] = TEXT("\0"),
  2343. szKeyPath[MAX_PATH] = TEXT("\0");
  2344. if ( ( lpszSysprepINFPath ) &&
  2345. ( *lpszSysprepINFPath ) &&
  2346. ( GetPrivateProfileString( TEXT( "Unattended" ), TEXT( "TapiConfigured" ), TEXT(""), szTapiConfigured, sizeof(szTapiConfigured)/sizeof(TCHAR), lpszSysprepINFPath ) ) )
  2347. {
  2348. // Only if the user specifies No will we remove the registry tapi settings
  2349. //
  2350. if (LSTRCMPI(szTapiConfigured, TEXT("NO")) == 0)
  2351. {
  2352. // Open base key and subkey
  2353. //
  2354. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2355. TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations"),
  2356. 0,
  2357. KEY_ALL_ACCESS,
  2358. &hKey );
  2359. // Determine if opening the key was successful
  2360. //
  2361. if(dwError != NO_ERROR)
  2362. {
  2363. SetLastError(dwError);
  2364. return FALSE;
  2365. }
  2366. // We enumerate the locations keys and delete any subkeys
  2367. //
  2368. while ( RegEnumKey(hKey, 0, szKeyPath, sizeof(szKeyPath)/sizeof(TCHAR)) == ERROR_SUCCESS )
  2369. {
  2370. // Delete the key and all subkeys
  2371. //
  2372. //
  2373. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: If delete fails, should increment RegEnumKey index
  2374. //
  2375. SHDeleteKey(hKey, szKeyPath) ;
  2376. }
  2377. // Close the key
  2378. //
  2379. RegCloseKey(hKey);
  2380. }
  2381. }
  2382. return TRUE;
  2383. }
  2384. //
  2385. // =================================
  2386. // Modify the HKLM\System\Setup\DiskDuplicator Key appropriately.
  2387. // =================================
  2388. //
  2389. BOOL SetOEMDuplicatorString
  2390. (
  2391. LPTSTR lpszSysprepINFPath
  2392. )
  2393. {
  2394. TCHAR szOEMDuplicatorString[256];
  2395. DWORD l;
  2396. HKEY hKey;
  2397. ZeroMemory(szOEMDuplicatorString, sizeof(szOEMDuplicatorString));
  2398. // See if the DiskDuplicator string is present in the
  2399. // unattend file.
  2400. GetPrivateProfileString( TEXT( "GuiUnattended" ),
  2401. TEXT( "OEMDuplicatorString" ),
  2402. L"",
  2403. szOEMDuplicatorString,
  2404. sizeof(szOEMDuplicatorString)/sizeof(TCHAR),
  2405. lpszSysprepINFPath );
  2406. if( szOEMDuplicatorString[0] )
  2407. {
  2408. //
  2409. // ISSUE-2002/02/26-brucegr: This doesn't ensure double termination for REG_MULTI_SZ...
  2410. //
  2411. // Ensure it is not bigger than 255 chars
  2412. szOEMDuplicatorString[255] = TEXT('\0');
  2413. // Open HKLM\System\Setup
  2414. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2415. TEXT("SYSTEM\\Setup"),
  2416. 0,
  2417. KEY_ALL_ACCESS,
  2418. &hKey );
  2419. if(l != NO_ERROR)
  2420. {
  2421. SetLastError(l);
  2422. return FALSE;
  2423. }
  2424. l = RegSetValueEx(hKey,
  2425. TEXT( "OEMDuplicatorString" ),
  2426. 0,
  2427. REG_MULTI_SZ,
  2428. (CONST BYTE *)szOEMDuplicatorString,
  2429. (lstrlen( szOEMDuplicatorString ) + 2) * sizeof(TCHAR));
  2430. RegCloseKey(hKey);
  2431. if(l != NO_ERROR)
  2432. {
  2433. SetLastError(l);
  2434. return FALSE;
  2435. }
  2436. }
  2437. return (TRUE);
  2438. }
  2439. // Reset OOBE settings so it doesn't think it ran already
  2440. //
  2441. void ResetOobeSettings()
  2442. {
  2443. HKEY hkOobe;
  2444. TCHAR szOobeInfoFile[MAX_PATH];
  2445. // Remove HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\OOBE
  2446. //
  2447. if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup"), &hkOobe)) {
  2448. //
  2449. // ISSUE-2002/02/26-brucegr: Why get lError? It's not used
  2450. //
  2451. LONG lError = SHDeleteKey(hkOobe, TEXT("OOBE"));
  2452. RegCloseKey(hkOobe);
  2453. }
  2454. // Build the path to oobeinfo.ini if oobe directory exists for personal
  2455. //
  2456. //
  2457. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: No error checking for GetSystemDirectory
  2458. //
  2459. GetSystemDirectory(szOobeInfoFile, MAX_PATH);
  2460. OPKAddPathN (szOobeInfoFile, TEXT("oobe"), AS ( szOobeInfoFile ) );
  2461. if (PathIsDirectory(szOobeInfoFile)) {
  2462. OPKAddPathN(szOobeInfoFile, TEXT("oobeinfo.ini"), AS ( szOobeInfoFile ) );
  2463. // Remove the RetailOOBE key in oobeinfo.ini
  2464. //
  2465. WritePrivateProfileString(TEXT("StartupOptions"), TEXT("RetailOOBE"), NULL /*Remove it*/, szOobeInfoFile);
  2466. }
  2467. }
  2468. /*++
  2469. ===============================================================================
  2470. Routine Description:
  2471. This routine will setup the first application to run when the machine
  2472. with the image applied to it is run.
  2473. The first run application will either be setup, in MiniSetup mode, or MSOOBE
  2474. The decision for what it will be is based on the product type.
  2475. For Personal/Professional, MSOOBE
  2476. For Professional, default will be MSOOBE, but can be overriden by the OEM to be
  2477. MiniSetup
  2478. For Server, and DTC, MiniSetup
  2479. Arguments:
  2480. None.
  2481. Return Value:
  2482. TRUE if successful.
  2483. FALSE if any errors encountered
  2484. ===============================================================================
  2485. --*/
  2486. BOOL SetupFirstRunApp
  2487. (
  2488. void
  2489. )
  2490. {
  2491. DWORD dwError;
  2492. DWORD dwValue;
  2493. HKEY hKeySetup;
  2494. TCHAR Value[MAX_PATH + 1]; // +1 is for second NULL char at end of REG_MULTI_SZ reg type
  2495. OSVERSIONINFOEX verInfo;
  2496. BOOL bUseMSOOBE = FALSE, bPro = FALSE;
  2497. // Open HKLM\System\Setup
  2498. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2499. TEXT("SYSTEM\\Setup"),
  2500. 0,
  2501. KEY_ALL_ACCESS,
  2502. &hKeySetup );
  2503. if(dwError != NO_ERROR)
  2504. {
  2505. SetLastError(dwError);
  2506. return FALSE;
  2507. }
  2508. // Check the product type, to determine what program we should run
  2509. if (IsPersonalSKU() || (bPro = IsProfessionalSKU())) {
  2510. bUseMSOOBE = TRUE;
  2511. if (bMiniSetup == TRUE && bPro)
  2512. bUseMSOOBE = FALSE;
  2513. }
  2514. else
  2515. bUseMSOOBE = FALSE;
  2516. // Start OOBE on next boot
  2517. //
  2518. if (bUseMSOOBE)
  2519. {
  2520. //
  2521. // ISSUE-2002/02/26-brucegr: If anything fails, machine is screwed. Should restore settings on failure?
  2522. //
  2523. // Set HKLM\System\Setup\SetupType Key to SETUPTYPE_NOREBOOT
  2524. dwValue = SETUPTYPE_NOREBOOT;
  2525. dwError = RegSetValueEx(hKeySetup,
  2526. TEXT("SetupType"),
  2527. 0,
  2528. REG_DWORD,
  2529. (CONST BYTE *)&dwValue,
  2530. sizeof(DWORD));
  2531. if(dwError != NO_ERROR)
  2532. {
  2533. RegCloseKey(hKeySetup);
  2534. SetLastError(dwError);
  2535. return FALSE;
  2536. }
  2537. // Set these keys for OEM
  2538. //
  2539. dwValue = 1;
  2540. dwError = RegSetValueEx(hKeySetup, TEXT("MiniSetupInProgress"), 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue));
  2541. if(dwError != NO_ERROR)
  2542. {
  2543. RegCloseKey(hKeySetup);
  2544. SetLastError(dwError);
  2545. return FALSE;
  2546. }
  2547. dwError = RegSetValueEx(hKeySetup,
  2548. TEXT("SystemSetupInProgress"),
  2549. 0,
  2550. REG_DWORD,
  2551. (CONST BYTE *)&dwValue,
  2552. sizeof(dwValue));
  2553. if(dwError != NO_ERROR)
  2554. {
  2555. RegCloseKey(hKeySetup);
  2556. SetLastError(dwError);
  2557. return FALSE;
  2558. }
  2559. dwError = RegSetValueEx(hKeySetup, TEXT("OobeInProgress"), 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue));
  2560. if(dwError != NO_ERROR)
  2561. {
  2562. RegCloseKey(hKeySetup);
  2563. SetLastError(dwError);
  2564. return FALSE;
  2565. }
  2566. // =================================
  2567. // Modify the HKLM\System\Setup\CmdLine key to run MSBOOBE
  2568. // =================================
  2569. ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\oobe\\msoobe.exe /f"), Value, sizeof(Value)/sizeof(Value[0]));
  2570. Value[lstrlen(Value) + 1] = L'\0';
  2571. dwError = RegSetValueEx(hKeySetup,
  2572. TEXT("CmdLine"),
  2573. 0,
  2574. REG_MULTI_SZ,
  2575. (CONST BYTE *)Value,
  2576. (lstrlen( Value ) + 2) * sizeof(TCHAR));
  2577. if(dwError != NO_ERROR)
  2578. {
  2579. RegCloseKey(hKeySetup);
  2580. SetLastError(dwError);
  2581. return FALSE;
  2582. }
  2583. }
  2584. else
  2585. {
  2586. //
  2587. // ISSUE-2002/02/26-brucegr: We are duplicating some of the above code again!
  2588. //
  2589. // Start MiniSetup on next boot
  2590. //
  2591. //
  2592. // =================================
  2593. // Modify the HKLM\System\Setup\SetupType Key appropriately (set it to 1 so we
  2594. // go into GUI-mode setup as if this were a full install.
  2595. // =================================
  2596. //
  2597. dwValue= SETUPTYPE;
  2598. dwError = RegSetValueEx(hKeySetup,
  2599. TEXT("SetupType"),
  2600. 0,
  2601. REG_DWORD,
  2602. (CONST BYTE *)&dwValue,
  2603. sizeof(dwValue));
  2604. if(dwError != NO_ERROR)
  2605. {
  2606. RegCloseKey(hKeySetup);
  2607. SetLastError(dwError);
  2608. return FALSE;
  2609. }
  2610. //
  2611. // =================================
  2612. // Modify the HKLM\System\Setup\SystemSetupInProgress.
  2613. // =================================
  2614. //
  2615. dwValue = 1;
  2616. dwError = RegSetValueEx(hKeySetup,
  2617. TEXT("SystemSetupInProgress"),
  2618. 0,
  2619. REG_DWORD,
  2620. (CONST BYTE *)&dwValue,
  2621. sizeof(dwValue));
  2622. if(dwError != NO_ERROR)
  2623. {
  2624. RegCloseKey(hKeySetup);
  2625. SetLastError(dwError);
  2626. return FALSE;
  2627. }
  2628. // Setup for PnP
  2629. if( PnP )
  2630. {
  2631. dwValue = 1;
  2632. dwError = RegSetValueEx(hKeySetup,
  2633. TEXT("MiniSetupDoPnP"),
  2634. 0,
  2635. REG_DWORD,
  2636. (CONST BYTE *)&dwValue,
  2637. sizeof(dwValue) );
  2638. if(dwError != NO_ERROR)
  2639. {
  2640. RegCloseKey(hKeySetup);
  2641. SetLastError(dwError);
  2642. return FALSE;
  2643. }
  2644. }
  2645. //
  2646. // =================================
  2647. // Create HKLM\System\Setup\MiniSetupInProgress key and set to 1. This tells LSA to
  2648. // skip generating a new SID. He wants to because he thinks we're
  2649. // setting up a machine for the first time. This also tells
  2650. // a few other people (networking, ...) that we're doing a
  2651. // boot into the mini wizard.
  2652. // =================================
  2653. //
  2654. dwValue = 1;
  2655. dwError = RegSetValueEx( hKeySetup,
  2656. TEXT("MiniSetupInProgress"),
  2657. 0,
  2658. REG_DWORD,
  2659. (CONST BYTE *)&dwValue,
  2660. sizeof(dwValue) );
  2661. if(dwError != NO_ERROR)
  2662. {
  2663. RegCloseKey(hKeySetup);
  2664. SetLastError(dwError);
  2665. return FALSE;
  2666. }
  2667. // =================================
  2668. // Modify the HKLM\System\Setup\CmdLine key appropriately so we do a mini
  2669. // version of gui-mode setup.
  2670. // =================================
  2671. StringCchCopy (Value, AS ( Value ), TEXT("setup.exe -newsetup -mini"));
  2672. Value[lstrlen(Value) + 1] = L'\0';
  2673. dwError = RegSetValueEx(hKeySetup,
  2674. TEXT("CmdLine"),
  2675. 0,
  2676. REG_MULTI_SZ,
  2677. (CONST BYTE *)Value,
  2678. (lstrlen( Value ) + 2) * sizeof(TCHAR));
  2679. if(dwError != NO_ERROR)
  2680. {
  2681. RegCloseKey(hKeySetup);
  2682. SetLastError(dwError);
  2683. return FALSE;
  2684. }
  2685. }
  2686. RegCloseKey(hKeySetup);
  2687. return (TRUE);
  2688. }
  2689. BOOL
  2690. IsSetupClPresent(
  2691. VOID
  2692. )
  2693. /*++
  2694. ===============================================================================
  2695. Routine Description:
  2696. This routine tests to see if the SID generator is present on the system.
  2697. The SID generator will be required to run on reboot, so if it's not here,
  2698. we need to know.
  2699. Arguments:
  2700. None.
  2701. Return Value:
  2702. TRUE - The SID generator is present.
  2703. FALSE - The SID generator is not present.
  2704. ===============================================================================
  2705. --*/
  2706. {
  2707. WCHAR NewFileName[MAX_PATH];
  2708. WCHAR OldFileName[MAX_PATH];
  2709. WIN32_FIND_DATA findData;
  2710. HANDLE FindHandle;
  2711. UINT OldMode;
  2712. DWORD Error;
  2713. WCHAR *wstr_ptr;
  2714. //
  2715. // First, try and copy a setupcl.exe into the system directory.
  2716. // If there's not one in our current directory, forget about it and
  2717. // keep going. The user may already have one installed.
  2718. //
  2719. //
  2720. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: No checking for GetSystemDirectory failure
  2721. //
  2722. GetSystemDirectory( NewFileName, MAX_PATH );
  2723. StringCchCat( NewFileName, AS ( NewFileName ), TEXT( "\\" ) );
  2724. StringCchCat( NewFileName, AS ( NewFileName ), TEXT(SYSCLONE_PART2) );
  2725. //
  2726. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: No checking for GetModuleFileName failure
  2727. //
  2728. GetModuleFileName(NULL,OldFileName,MAX_PATH);
  2729. //
  2730. // ISSUE-2002/02/26-brucegr: Use PathRemoveFileSpec instead of this horrid code
  2731. //
  2732. wstr_ptr = wcsrchr( OldFileName, TEXT( '\\' ) );
  2733. if (wstr_ptr)
  2734. *wstr_ptr = 0;
  2735. StringCchCat( OldFileName, AS ( OldFileName ), TEXT( "\\" ) );
  2736. StringCchCat( OldFileName, AS ( OldFileName ), TEXT(SYSCLONE_PART2) );
  2737. if( !CopyFile( OldFileName, NewFileName, FALSE ) ) {
  2738. Sleep( 500 );
  2739. if( !CopyFile( OldFileName, NewFileName, FALSE ) ) {
  2740. //
  2741. // ISSUE-2002/02/26-brucegr: Why get the error code if we overwrite it below?
  2742. //
  2743. Error = GetLastError();
  2744. }
  2745. }
  2746. //
  2747. // ISSUE-2002/02/26-brucegr: NewFileName should already be constructed... this seems redundant
  2748. //
  2749. //
  2750. // Generate path to the system32 directory, then tack on the
  2751. // name of the SID generator.
  2752. //
  2753. //
  2754. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: No checking for GetSystemDirectory failure
  2755. //
  2756. GetSystemDirectory( NewFileName, MAX_PATH );
  2757. StringCchCat( NewFileName, AS ( NewFileName ), TEXT("\\") );
  2758. StringCchCat( NewFileName, AS ( NewFileName ), TEXT(SYSCLONE_PART2) );
  2759. //
  2760. // Now see if he exists...
  2761. //
  2762. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  2763. //
  2764. // ISSUE-2002/02/26-brucegr: Use GetFileAttributes instead of FindFirstFile
  2765. //
  2766. //
  2767. // See if he's there.
  2768. //
  2769. FindHandle = FindFirstFile( NewFileName, &findData );
  2770. if(FindHandle == INVALID_HANDLE_VALUE) {
  2771. //
  2772. // Nope...
  2773. //
  2774. Error = GetLastError();
  2775. } else {
  2776. //
  2777. // Yep. Close him.
  2778. //
  2779. FindClose(FindHandle);
  2780. Error = NO_ERROR;
  2781. }
  2782. //
  2783. // Restore error mode.
  2784. //
  2785. SetErrorMode(OldMode);
  2786. SetLastError(Error);
  2787. return (Error == NO_ERROR);
  2788. }
  2789. BOOL
  2790. CheckOSVersion(
  2791. VOID
  2792. )
  2793. /*++
  2794. ===============================================================================
  2795. Routine Description:
  2796. This routine returns TRUE if the OS that we're running on
  2797. meets the specified criteria.
  2798. Arguments:
  2799. NONE
  2800. Return Value:
  2801. TRUE - OS meets all criteria.
  2802. FALSE - Failed to meet some criteria.
  2803. ===============================================================================
  2804. --*/
  2805. {
  2806. #include <lmaccess.h>
  2807. #include <lmserver.h>
  2808. OSVERSIONINFO OsVersion;
  2809. NET_API_STATUS RC;
  2810. PSERVER_INFO_101 pSI;
  2811. //
  2812. // ISSUE-2002/02/26-brucegr: Remove unused variables
  2813. //
  2814. DWORD dwLength;
  2815. LPVOID lpData;
  2816. TCHAR pFile[MAX_PATH];
  2817. DWORD dwTemp;
  2818. UINT DataLength;
  2819. PWORD Translation;
  2820. LANGID TargetLocale = 0,
  2821. SourceLocale = 0;
  2822. //
  2823. // Pick a system file that's present on both NT and Win95.
  2824. //
  2825. TCHAR TARGET_TEST_FILE[MAX_PATH] = TEXT("\\kernel32.dll");
  2826. //
  2827. // Get the OS version. We need to make sure we're on NT5.
  2828. //
  2829. OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  2830. //
  2831. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: Check if GetVersionEx fails!
  2832. //
  2833. GetVersionEx(&OsVersion);
  2834. if(OsVersion.dwMajorVersion < 5) {
  2835. return( FALSE );
  2836. }
  2837. //
  2838. // Make sure we're not a Domain Controller (either primary or backup)
  2839. //
  2840. RC = NetServerGetInfo( NULL, // local machine
  2841. 101,
  2842. (LPBYTE *)&pSI );
  2843. //
  2844. // ISSUE-2002/02/26-brucegr: Change this to "NERR_Success == RC"
  2845. //
  2846. if( !RC ) {
  2847. //
  2848. // We got something. See if this might be a DC.
  2849. //
  2850. if( (pSI->sv101_type & SV_TYPE_DOMAIN_CTRL ) ||
  2851. (pSI->sv101_type & SV_TYPE_DOMAIN_BAKCTRL) ) {
  2852. //
  2853. // He's a DC. Fail.
  2854. //
  2855. return( FALSE );
  2856. }
  2857. }
  2858. return( TRUE );
  2859. }
  2860. BOOL FCheckBuildMassStorageSectionFlag(TCHAR* pszSysprepInf)
  2861. {
  2862. TCHAR szValue[MAX_PATH];
  2863. DWORD dwReturn = 0;
  2864. BOOL fReturn = FALSE;
  2865. // If key is missing we default to no, we don't want to build the section
  2866. // but we still process the section if user manually added keys to this
  2867. // section.
  2868. //
  2869. //
  2870. // ISSUE-2002/02/26-brucegr: dwReturn isn't really used
  2871. //
  2872. if (dwReturn = GetPrivateProfileString(SYSPREP_SECTION, SYSPREP_BUILDMASSSTORAGE_KEY,
  2873. TEXT("NO"), szValue, MAX_PATH, pszSysprepInf))
  2874. {
  2875. if (LSTRCMPI(szValue, TEXT("YES")) == 0)
  2876. fReturn = TRUE;
  2877. else if (LSTRCMPI(szValue, TEXT("NO")) == 0)
  2878. fReturn = FALSE;
  2879. }
  2880. return fReturn;
  2881. }
  2882. VOID BuildMassStorageSection(BOOL fForceBuild)
  2883. {
  2884. LPDEVIDLIST lpDeviceIDList = NULL;
  2885. DWORD dwNumDevIDs = 0, dwGuids = 0, dwIdxDevIDs = 0;
  2886. TCHAR szSysPrepInf[MAX_PATH];
  2887. GUID rgGuids[3];
  2888. TCHAR *prgInfs[] = { TEXT("machine.inf"), TEXT("pnpscsi.inf"), TEXT("scsi.inf"), TEXT("mshdc.inf") };
  2889. /* Types of mass storage devices GUIDs */
  2890. rgGuids[0] = GUID_DEVCLASS_SYSTEM; /* machine.inf */
  2891. rgGuids[1] = GUID_DEVCLASS_SCSIADAPTER; /* scsi.inf */
  2892. rgGuids[2] = GUID_DEVCLASS_HDC; /* mshdc.inf */
  2893. /* Only from these inf */
  2894. //
  2895. // NTRAID#NTBUG9-551815-2002/02/26-brucegr: Need to check GetModuleFileName return value
  2896. //
  2897. GetModuleFileName(NULL, szSysPrepInf, MAX_PATH);
  2898. PathRemoveFileSpec(szSysPrepInf);
  2899. OPKAddPathN ( szSysPrepInf, TEXT("sysprep.inf"), AS ( szSysPrepInf ) );
  2900. // Only build if user requested it
  2901. //
  2902. if (!fForceBuild && !FCheckBuildMassStorageSectionFlag(szSysPrepInf))
  2903. return;
  2904. //
  2905. // =================================
  2906. // Remove [sysprepcleanup] which will be added during PopulateDeviceDatabase().
  2907. // =================================
  2908. //
  2909. WritePrivateProfileSection(L"sysprepcleanup", NULL, szSysPrepInf);
  2910. // Loop thru all the mass storage devices
  2911. //
  2912. for (dwGuids = 0; dwGuids < (sizeof(rgGuids) / sizeof(rgGuids[0])); dwGuids++) {
  2913. // Build a list of mass storage devices
  2914. //
  2915. if (BuildDeviceIDList(SYSPREPMASSSTORAGE_SECTION,
  2916. szSysPrepInf,
  2917. (LPGUID)&rgGuids[dwGuids],
  2918. &lpDeviceIDList,
  2919. &dwNumDevIDs,
  2920. TRUE,
  2921. FALSE))
  2922. {
  2923. // Write the mass storage info to sysprep.inf
  2924. //
  2925. for(dwIdxDevIDs = 0; dwIdxDevIDs < dwNumDevIDs; ++dwIdxDevIDs)
  2926. {
  2927. BOOL fInfFound = FALSE;
  2928. // Check if this inf if in our Infs table
  2929. //
  2930. int iCmp = 0;
  2931. for (iCmp = 0; iCmp < (sizeof(prgInfs)/sizeof(prgInfs[0])); iCmp++) {
  2932. //
  2933. // ISSUE-2002/02/26-brucegr: Can we use something better than StrStrI?
  2934. //
  2935. if (StrStrI(lpDeviceIDList[dwIdxDevIDs].szINFFileName, prgInfs[iCmp])) {
  2936. fInfFound = TRUE;
  2937. break;
  2938. }
  2939. }
  2940. if (fInfFound)
  2941. {
  2942. // Check HardwareID first then check the CompatID
  2943. //
  2944. if (lpDeviceIDList[dwIdxDevIDs].szHardwareID[0])
  2945. {
  2946. // Use only the infs we care about
  2947. //
  2948. WritePrivateProfileString(SYSPREPMASSSTORAGE_SECTION,
  2949. lpDeviceIDList[dwIdxDevIDs].szHardwareID,
  2950. lpDeviceIDList[dwIdxDevIDs].szINFFileName,
  2951. szSysPrepInf);
  2952. }
  2953. else if (lpDeviceIDList[dwIdxDevIDs].szCompatibleID[0])
  2954. {
  2955. // Use only the infs we care about
  2956. //
  2957. WritePrivateProfileString(SYSPREPMASSSTORAGE_SECTION,
  2958. lpDeviceIDList[dwIdxDevIDs].szCompatibleID,
  2959. lpDeviceIDList[dwIdxDevIDs].szINFFileName,
  2960. szSysPrepInf);
  2961. }
  2962. }
  2963. }
  2964. // Free the allocated list
  2965. //
  2966. LocalFree(lpDeviceIDList);
  2967. }
  2968. }
  2969. }
  2970. DWORD
  2971. ReArm(
  2972. VOID
  2973. )
  2974. /*++
  2975. ===============================================================================
  2976. Routine Description:
  2977. This routine returns either the error code or success.
  2978. Arguments:
  2979. None.
  2980. Return Value:
  2981. ERROR_SUCCESS - ReArm succeeded and shortcut restored.
  2982. Error code - ReArm failed.
  2983. ===============================================================================
  2984. --*/
  2985. {
  2986. DWORD dwError = ERROR_FILE_NOT_FOUND;
  2987. BYTE bDummy = 1;
  2988. typedef DWORD (WINAPI* MYPROC)(BYTE*);
  2989. // Use Loadlibrary/GetProcAddress because Riprep needs to support Windows 2000
  2990. //
  2991. HINSTANCE hInst = LoadLibrary(L"syssetup.dll");
  2992. if (hInst) {
  2993. MYPROC fnProc;
  2994. if ( fnProc = (MYPROC)GetProcAddress(hInst, "SetupOobeBnk") ) {
  2995. dwError = fnProc(&bDummy);
  2996. }
  2997. FreeLibrary(hInst);
  2998. }
  2999. // Return error code or success
  3000. //
  3001. return dwError;
  3002. }
  3003. BOOL FCommonReseal
  3004. (
  3005. VOID
  3006. )
  3007. /*++
  3008. ===============================================================================
  3009. Routine Description:
  3010. This routine is the common reseal code for both Riprep and Sysprep.
  3011. Arguments:
  3012. None.
  3013. Return Value:
  3014. TRUE - success
  3015. FALSE - failure
  3016. Remarks:
  3017. This routine should only cleanup registry keys as it is being called by
  3018. AdjustRegistry() which is the last step of Riprep after the network is
  3019. removed.
  3020. ===============================================================================
  3021. --*/
  3022. {
  3023. HKEY hKey = NULL;
  3024. SC_HANDLE schService;
  3025. SC_HANDLE schSystem;
  3026. TCHAR szUrllog[MAX_PATH];
  3027. DWORD dwLen;
  3028. //
  3029. // ISSUE-2002/02/26-brucegr: Make sure all intermediate return points are necessary!
  3030. //
  3031. //
  3032. // =================================
  3033. // Clear the MRU list on the machine.
  3034. // =================================
  3035. //
  3036. NukeMruList();
  3037. //
  3038. // =================================
  3039. // Clear recent apps
  3040. // =================================
  3041. //
  3042. ClearRecentApps();
  3043. //
  3044. // =================================
  3045. // Delete User Specific Settings from all user profiles.
  3046. // =================================
  3047. //
  3048. NukeUserSettings();
  3049. //
  3050. // =================================
  3051. // Remove HKLM\System\MountedDevices key.
  3052. // =================================
  3053. //
  3054. if ( NO_ERROR == (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3055. TEXT("System"),
  3056. 0,
  3057. KEY_ALL_ACCESS,
  3058. &hKey)) )
  3059. {
  3060. RegDeleteKey(hKey, TEXT("MountedDevices"));
  3061. RegCloseKey(hKey);
  3062. }
  3063. //
  3064. // =================================
  3065. // Remove Desktop Cleanup wizard registry key to reset cleanup timer
  3066. // =================================
  3067. //
  3068. if ( NO_ERROR == (RegOpenKeyEx(HKEY_CURRENT_USER,
  3069. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Desktop\\CleanupWiz"),
  3070. 0,
  3071. KEY_ALL_ACCESS,
  3072. &hKey)) )
  3073. {
  3074. RegDeleteValue(hKey, TEXT("Last used time"));
  3075. RegCloseKey(hKey);
  3076. }
  3077. //
  3078. // =================================
  3079. // Windows Update Cleanup
  3080. //
  3081. // Do all of the following during SYSPREP -reseal before the system is rebooted:
  3082. //
  3083. // 1) stop the WUAUSERV service
  3084. // 2) delete %ProgramFiles%\WindowsUpdate\urllog.dat (note WindowsUpdate is a hiiden directory. I don't believe this will cause any issues).
  3085. // 3) remove the following registry entries under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate
  3086. // Delete value/data pair: PingID
  3087. // DO NOT DELETE key subtree: Auto Update
  3088. // Delete value/data pair: Auto Update\AUState
  3089. // Delete value/data pair: Auto Update\LastWaitTimeout
  3090. // Delete key subtree: IUControl
  3091. // Delete key subtree: OemInfo
  3092. // =================================
  3093. //
  3094. // 1) stop the WUAUSERV service
  3095. schSystem = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  3096. if (schSystem)
  3097. {
  3098. schService = OpenService( schSystem,
  3099. TEXT("WUAUSERV"),
  3100. SC_MANAGER_ALL_ACCESS);
  3101. if ( schService )
  3102. {
  3103. SERVICE_STATUS ss;
  3104. ControlService( schService, SERVICE_CONTROL_STOP, &ss );
  3105. CloseServiceHandle( schService );
  3106. }
  3107. CloseServiceHandle( schSystem );
  3108. }
  3109. // 2) delete %ProgramFiles%\WindowsUpdate\urllog.dat (note WindowsUpdate is a hiiden directory. I don't believe this will cause any issues).
  3110. dwLen=ExpandEnvironmentStrings(TEXT("%ProgramFiles%\\WindowsUpdate\\urllog.dat"),szUrllog,MAX_PATH);
  3111. if (dwLen && dwLen < MAX_PATH)
  3112. DeleteFile(szUrllog);
  3113. // 3) remove the following registry entries under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate
  3114. // Delete value/data pair: PingID
  3115. // DO NOT DELETE key subtree: Auto Update
  3116. // Delete value/data pair: Auto Update\AUState
  3117. // Delete value/data pair: Auto Update\LastWaitTimeout
  3118. // Delete key subtree: IUControl
  3119. // Delete key subtree: OemInfo
  3120. if ( NO_ERROR == (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3121. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate"),
  3122. 0,
  3123. KEY_ALL_ACCESS,
  3124. &hKey)) )
  3125. {
  3126. RegDeleteValue(hKey, TEXT("PingID"));
  3127. RegDeleteValue(hKey, TEXT("Auto Update\\AUState"));
  3128. RegDeleteValue(hKey, TEXT("Auto Update\\LastWaitTimeout"));
  3129. RegDeleteKey(hKey, TEXT("IUControl"));
  3130. RegDeleteKey(hKey, TEXT("OemInfo"));
  3131. RegCloseKey(hKey);
  3132. }
  3133. //
  3134. // =================================
  3135. // Modify any install paths that may be required
  3136. // for our reboot into gui-mode.
  3137. // =================================
  3138. //
  3139. FixDevicePaths();
  3140. //
  3141. // =================================
  3142. // Clear out winlogon's memory of the last user and domain.
  3143. // =================================
  3144. //
  3145. if( !DeleteWinlogonDefaults() ) {
  3146. return FALSE;
  3147. }
  3148. // Remove Cryptography key so it gets re-generated, only do this if the SIDS have been regenerated
  3149. //
  3150. if ( !NoSidGen )
  3151. {
  3152. if ( NO_ERROR == (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3153. TEXT("SOFTWARE\\Microsoft\\Cryptography"),
  3154. 0,
  3155. KEY_ALL_ACCESS,
  3156. &hKey)) )
  3157. {
  3158. RegDeleteValue(hKey, TEXT("MachineGuid"));
  3159. RegCloseKey(hKey);
  3160. }
  3161. }
  3162. // Set the cloned tag in the registry
  3163. if (!SetCloneTag())
  3164. return FALSE;
  3165. // Sets whether msoobe or mini-setup on first end user boot
  3166. //
  3167. if (!SetupFirstRunApp())
  3168. return FALSE;
  3169. //
  3170. // =================================
  3171. // Clear the LastKnownGood ControlSet.
  3172. // =================================
  3173. //
  3174. if (NO_ERROR != NukeLKGControlSet())
  3175. return FALSE;
  3176. //
  3177. // =================================
  3178. // Clear the eventlogs on the machine.
  3179. // This is the last thing that we should do.
  3180. // =================================
  3181. //
  3182. NukeEventLogs();
  3183. // Common reseal succeeded
  3184. //
  3185. return TRUE;
  3186. }
  3187. BOOL
  3188. AdjustFiles(
  3189. VOID
  3190. )
  3191. /*++
  3192. ===============================================================================
  3193. Routine Description:
  3194. This routine allows cleanup to happen before files are copied to the server
  3195. by Riprep.
  3196. Arguments:
  3197. None.
  3198. Return Value:
  3199. None.
  3200. Remarks:
  3201. This routine should be called before AdjustRegistry() for Riprep. Sysprep
  3202. needs to call this before FCommonReseal().
  3203. ===============================================================================
  3204. --*/
  3205. {
  3206. BOOL bUseMSOOBE = FALSE,
  3207. bPro = FALSE,
  3208. fReturn = TRUE;
  3209. TCHAR szSysprepFolder[MAX_PATH] = TEXT("\0");
  3210. //
  3211. // Make sure we've got the required privileges to update the registry.
  3212. //
  3213. pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
  3214. pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE);
  3215. //
  3216. // Check the product type.
  3217. //
  3218. if (IsPersonalSKU() || (bPro = IsProfessionalSKU())) {
  3219. bUseMSOOBE = TRUE;
  3220. if (bMiniSetup == TRUE && bPro)
  3221. bUseMSOOBE = FALSE;
  3222. }
  3223. else
  3224. bUseMSOOBE = FALSE;
  3225. if (bUseMSOOBE)
  3226. {
  3227. //
  3228. // Prepare for Oobe
  3229. //
  3230. }
  3231. else
  3232. {
  3233. //
  3234. // Prepare for MiniSetup
  3235. //
  3236. }
  3237. //
  3238. // =================================
  3239. // Clear the SMS settings and INI file.
  3240. // =================================
  3241. //
  3242. NukeSmsSettings();
  3243. //
  3244. // =================================
  3245. // Clean up Digital Rights Media information.
  3246. // =================================
  3247. //
  3248. #if !(defined(AMD64) || defined(IA64))
  3249. //
  3250. // This only works on x86. There is no 64-bit library available for us
  3251. // to call into right now.
  3252. //
  3253. if ( GetWindowsDirectory(szSysprepFolder, sizeof(szSysprepFolder)/sizeof(szSysprepFolder[0])) )
  3254. {
  3255. CHAR szLogFileA[MAX_PATH];
  3256. BOOL bLog = TRUE;
  3257. // This will look something like this: "c:\windows". Make the character after the '\' NULL, and
  3258. // append the name of the file to it.
  3259. //
  3260. szSysprepFolder[3] = UNICODE_NULL;
  3261. PathAppend(szSysprepFolder, TEXT("SYSPREP"));
  3262. // Create the folder if it does not exist
  3263. //
  3264. if ( !PathFileExists(szSysprepFolder) )
  3265. {
  3266. bLog = CreateDirectory(szSysprepFolder, NULL);
  3267. }
  3268. PathAppend(szSysprepFolder, CLEANDRM_LOGFILE);
  3269. // Convert UNICODE string to ANSI string.
  3270. //
  3271. if ( WideCharToMultiByte(CP_ACP, 0, szSysprepFolder, -1, szLogFileA, sizeof(szLogFileA), NULL, NULL) )
  3272. {
  3273. CleanDRM( bLog ? szLogFileA : NULL );
  3274. }
  3275. else
  3276. {
  3277. fReturn = FALSE;
  3278. }
  3279. }
  3280. else
  3281. {
  3282. fReturn = FALSE;
  3283. }
  3284. #endif // #if !(defined(AMD64) || defined(IA64))
  3285. //
  3286. // =================================
  3287. // Clear OOBE settings for both minisetup and oobe.
  3288. // =================================
  3289. //
  3290. ResetOobeSettings();
  3291. //
  3292. // =================================
  3293. // Clear the eventlogs on the machine.
  3294. // This is the last thing that we should do.
  3295. // =================================
  3296. //
  3297. NukeEventLogs();
  3298. //
  3299. // =================================
  3300. // Delete temporary files.
  3301. // =================================
  3302. //
  3303. NukeTemporaryFiles();
  3304. return fReturn;
  3305. }
  3306. BOOL
  3307. AdjustRegistry(
  3308. IN BOOL fRemoveNetworkSettings
  3309. )
  3310. /*++
  3311. ===============================================================================
  3312. Routine Description:
  3313. This routine actually adds in the registry entry to enable our second half
  3314. to execute.
  3315. Arguments:
  3316. fRemoveNetworkSettings - indicates if network settings should be removed
  3317. Return Value:
  3318. None
  3319. ===============================================================================
  3320. --*/
  3321. {
  3322. HKEY hKey;
  3323. TCHAR szSysprepINFPath[MAX_PATH];
  3324. BOOL fPopulated = FALSE;
  3325. // Formulate the path to SYSPRE.INF, since we will need it later to look up
  3326. // sysprep options
  3327. if (!GetWindowsDirectory( szSysprepINFPath, MAX_PATH ))
  3328. return FALSE;
  3329. StringCchCopy ( &szSysprepINFPath[3], AS ( szSysprepINFPath ) - 3, TEXT("sysprep\\sysprep.inf") );
  3330. //
  3331. // Make sure we've got the required privileges to update the registry.
  3332. //
  3333. pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
  3334. pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE);
  3335. // Set OEMDuplicatorString
  3336. if (!SetOEMDuplicatorString(szSysprepINFPath))
  3337. return (FALSE);
  3338. // Fill in the [sysprepMassStorage] section for PopulateDeviceDatabase()
  3339. //
  3340. BuildMassStorageSection(FALSE);
  3341. //
  3342. // =================================
  3343. // Fixup boot devices.
  3344. // =================================
  3345. //
  3346. if (!PopulateDeviceDatabase(&fPopulated))
  3347. return FALSE;
  3348. //
  3349. // Perform miscellaneous registry modifications
  3350. //
  3351. // Determine if we should set the BigLba support in registry
  3352. //
  3353. if ( !SetBigLbaSupport(szSysprepINFPath) )
  3354. {
  3355. return FALSE;
  3356. }
  3357. // Determine if we should remove the TAPI settings in registry
  3358. //
  3359. if ( !RemoveTapiSettings(szSysprepINFPath) )
  3360. {
  3361. return FALSE;
  3362. }
  3363. //
  3364. // Remove the LastAliveStamp value so that we don't get erroneous entries into the even log
  3365. // and avoid pop-ups on first boot saying that the machine has been shutdown improperly.
  3366. //
  3367. if ( ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, STR_REG_KEY_RELIABILITY, &hKey) )
  3368. {
  3369. RegDeleteValue(hKey, STR_REG_VALUE_LASTALIVESTAMP);
  3370. RegCloseKey(hKey);
  3371. }
  3372. //
  3373. // =================================
  3374. // Reset network settings last so if any errors happen before we still
  3375. // have network access.
  3376. // =================================
  3377. //
  3378. if (fRemoveNetworkSettings)
  3379. {
  3380. if (!RemoveNetworkSettings(szSysprepINFPath))
  3381. return FALSE;
  3382. }
  3383. //
  3384. // =================================
  3385. // Change our boot timeout to 1.
  3386. // =================================
  3387. //
  3388. ChangeBootTimeout( 1 );
  3389. // Do common reseal code for both Sysprep and Riprep
  3390. //
  3391. if (!FCommonReseal())
  3392. return FALSE;
  3393. return TRUE;
  3394. }
  3395. BOOL
  3396. CreateSysprepTemporaryDevnode(
  3397. HDEVINFO* phDevInfo,
  3398. SP_DEVINFO_DATA* pDeviceInfoData
  3399. )
  3400. /*++
  3401. ===============================================================================
  3402. Routine Description:
  3403. Arguments:
  3404. None.
  3405. Return Value:
  3406. TRUE if everything is OK, FALSE otherwise.
  3407. Assumptions:
  3408. 1. No HardwareID exceeds MAX_PATH characters.
  3409. ===============================================================================
  3410. --*/
  3411. {
  3412. if (phDevInfo) {
  3413. //
  3414. // Create a dummy devnode
  3415. //
  3416. *phDevInfo = SetupDiCreateDeviceInfoList(NULL, NULL);
  3417. if (*phDevInfo == INVALID_HANDLE_VALUE) {
  3418. return FALSE;
  3419. }
  3420. //
  3421. // Initialize the DriverInfoData struct
  3422. //
  3423. pDeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
  3424. //
  3425. // Create the devnode
  3426. //
  3427. if (pDeviceInfoData && !SetupDiCreateDeviceInfo(*phDevInfo,
  3428. L"SYSPREP_TEMPORARY",
  3429. (LPGUID)&GUID_NULL,
  3430. NULL,
  3431. NULL,
  3432. DICD_GENERATE_ID,
  3433. pDeviceInfoData)) {
  3434. //
  3435. // ISSUE-2002/02/26-brucegr: Destroy the info list and set phDevInfo to INVALID_HANDLE_VALUE?
  3436. //
  3437. return FALSE;
  3438. }
  3439. }
  3440. return TRUE;
  3441. }
  3442. BOOL InsertCleanupNode(PPCLEANUP_NODE ppCleanupList, PCLEANUP_NODE pAddNode)
  3443. {
  3444. PPCLEANUP_NODE ppl = ppCleanupList;
  3445. while ( *ppl != NULL && (0 < lstrcmpi(pAddNode->pszService, (*ppl)->pszService))
  3446. )
  3447. {
  3448. ppl = &((*ppl)->pNext);
  3449. }
  3450. if (*ppl && (0 == lstrcmpi(pAddNode->pszService, (*ppl)->pszService)))
  3451. {
  3452. free(pAddNode);
  3453. return FALSE;
  3454. }
  3455. pAddNode->pNext = *ppl;
  3456. *ppl = pAddNode;
  3457. return TRUE;
  3458. }
  3459. PCLEANUP_NODE FindCleanupNode(PPCLEANUP_NODE ppCleanupList, LPTSTR pszServiceName)
  3460. {
  3461. PCLEANUP_NODE pTemp = *ppCleanupList;
  3462. while (pTemp)
  3463. {
  3464. if (0 == lstrcmpi(pTemp->pszService, pszServiceName))
  3465. return pTemp;
  3466. pTemp = pTemp->pNext;
  3467. }
  3468. return NULL;
  3469. }
  3470. void FreeCleanupList(PPCLEANUP_NODE ppCleanupList)
  3471. {
  3472. while (*ppCleanupList)
  3473. {
  3474. PCLEANUP_NODE pTemp = *ppCleanupList;
  3475. *ppCleanupList = (*ppCleanupList)->pNext;
  3476. free(pTemp->pszService);
  3477. free(pTemp);
  3478. }
  3479. *ppCleanupList = NULL;
  3480. }
  3481. BOOL AddCleanupNode(
  3482. LPTSTR pszServiceName
  3483. )
  3484. /*++
  3485. ===============================================================================
  3486. Routine Description:
  3487. When populating the [SysprepCleanup] section we need to check if the service
  3488. or filters already exists in the this section before we enter a duplicate
  3489. entry.
  3490. Arguments:
  3491. LPTSTR pszServiceName - Service/Filter name.
  3492. Return Value:
  3493. TRUE if a duplicate found, FALSE otherwise.
  3494. Assumptions:
  3495. 1. No duplicate entries in [SysprepCleanup] section.
  3496. ===============================================================================
  3497. --*/
  3498. {
  3499. BOOL fAdded = FALSE;
  3500. //
  3501. // Find the Service in our list.
  3502. //
  3503. if (pszServiceName && (NULL == FindCleanupNode(&g_pCleanupListHead, pszServiceName)))
  3504. {
  3505. PCLEANUP_NODE pNode = (PCLEANUP_NODE)malloc(sizeof(CLEANUP_NODE));
  3506. if (pNode)
  3507. {
  3508. int nLen = lstrlen ( pszServiceName ) + 1;
  3509. pNode->pszService = (LPTSTR)malloc( nLen * sizeof ( TCHAR ) );
  3510. if ( pNode->pszService )
  3511. {
  3512. StringCchCopy (pNode->pszService, nLen, pszServiceName);
  3513. }
  3514. pNode->pNext = NULL;
  3515. //
  3516. // We didn't find it so add it to our list.
  3517. // We will not add duplicates to our list.
  3518. //
  3519. fAdded = InsertCleanupNode(&g_pCleanupListHead, pNode);
  3520. }
  3521. }
  3522. return fAdded;
  3523. }
  3524. BOOL
  3525. PopulateDeviceDatabase(
  3526. IN BOOL* pfPopulated
  3527. )
  3528. /*++
  3529. ===============================================================================
  3530. Routine Description:
  3531. Parse the [SysprepMassStorage] section in the sysprep.inf file and
  3532. populate the critical device database with the specified devices to ensure
  3533. that we can boot into the miniwizard when moving the image to a target
  3534. system with different boot storage devices.
  3535. The installed services/upperfilters/lowerfilters will be recorded, so
  3536. that on the next boot into the mini-wizard those without an associated
  3537. device will be disabled (the cleanup stage) in order not to unnecessarily
  3538. degrade Windows start time.
  3539. Arguments:
  3540. None.
  3541. Return Value:
  3542. TRUE if everything is OK, FALSE otherwise.
  3543. Assumptions:
  3544. 1. No HardwareID exceeds MAX_PATH characters.
  3545. 2. No field on a line in the [SysprepMassStorage] section exceeds MAX_PATH
  3546. characters.
  3547. 3. No service's/upperfilter's/lowerfilter's name exceeds MAX_PATH characters.
  3548. 4. DirectoryOnSourceDevice, source DiskDescription, or source DiskTag
  3549. (applying to vendor-supplied drivers) cannot exceed MAX_PATH characters.
  3550. ===============================================================================
  3551. --*/
  3552. {
  3553. BOOL bAllOK = TRUE;
  3554. PCWSTR pszSectionName = L"SysprepMassStorage";
  3555. WCHAR szSysprepInfFile[] = L"?:\\sysprep\\sysprep.inf";
  3556. #ifdef DEBUG_LOGLOG
  3557. WCHAR szLogFile[] = L"?:\\sysprep.log";
  3558. #endif
  3559. WCHAR szBuffer[MAX_PATH], *pszFilter;
  3560. CHAR szOutBufferA[MAX_PATH];
  3561. HANDLE hInfFile = INVALID_HANDLE_VALUE;
  3562. HINF hAnswerInf = INVALID_HANDLE_VALUE;
  3563. BOOL bLineExists;
  3564. INFCONTEXT InfContext;
  3565. HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
  3566. SP_DEVINFO_DATA DeviceInfoData;
  3567. SP_DEVINSTALL_PARAMS DevInstallParams;
  3568. SP_DRVINFO_DATA DriverInfoData;
  3569. HSPFILEQ QueueHandle = INVALID_HANDLE_VALUE;
  3570. DWORD dwSize = 0;
  3571. BOOL bNodeCreated = FALSE;
  3572. WCHAR DirectoryOnSourceDevice[MAX_PATH];
  3573. WCHAR DiskDescription[MAX_PATH];
  3574. WCHAR DiskTag[MAX_PATH];
  3575. PSYSPREP_QUEUE_CONTEXT pSysprepContext;
  3576. if (!GetWindowsDirectory(szBuffer, MAX_PATH))
  3577. return FALSE;
  3578. szSysprepInfFile[0] = szBuffer[0];
  3579. #ifdef DEBUG_LOGLOG
  3580. szLogFile[0] = szBuffer[0];
  3581. DeleteFile(szLogFile);
  3582. LOG_Init(szLogFile);
  3583. LOG_Write(L">>\r\n>> PopulateDeviceDatabase\r\n>>\r\n");
  3584. LOG_Write(L"Sysprep.inf=%s", szSysprepInfFile);
  3585. #endif
  3586. //
  3587. // =================================
  3588. // Open the sysprep.inf file. Since we don't know what the user has in
  3589. // here, so try opening as both styles.
  3590. // =================================
  3591. //
  3592. //
  3593. // ISSUE-2002/02/26-brucegr: You can specify both bits in one call...
  3594. //
  3595. hAnswerInf = SetupOpenInfFile(szSysprepInfFile, NULL, INF_STYLE_WIN4, NULL);
  3596. if (hAnswerInf == INVALID_HANDLE_VALUE) {
  3597. hAnswerInf = SetupOpenInfFile(szSysprepInfFile, NULL, INF_STYLE_OLDNT, NULL);
  3598. if (hAnswerInf == INVALID_HANDLE_VALUE) {
  3599. //
  3600. // User didn't give us a sysprep.inf. Return as if nothing
  3601. // happened.
  3602. //
  3603. return TRUE;
  3604. }
  3605. }
  3606. //
  3607. // open the same inf file to record upper filters, lower filters, and
  3608. // services of the added devices
  3609. //
  3610. hInfFile = CreateFile(szSysprepInfFile,
  3611. GENERIC_WRITE,
  3612. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3613. NULL,
  3614. OPEN_ALWAYS,
  3615. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
  3616. NULL);
  3617. if (hInfFile == INVALID_HANDLE_VALUE) {
  3618. goto PDD_Critical_Error_Handler;
  3619. }
  3620. //
  3621. // =================================
  3622. // Create/clear [sysprepcleanup] which should be at the bottom of the file.
  3623. // =================================
  3624. //
  3625. WritePrivateProfileSection(L"sysprepcleanup", L"", szSysprepInfFile);
  3626. //
  3627. // =================================
  3628. // Create a dummy devnode
  3629. // =================================
  3630. //
  3631. bNodeCreated = CreateSysprepTemporaryDevnode(&hDevInfo, &DeviceInfoData);
  3632. // Initialize the DriverInfoData struct
  3633. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  3634. if (!bNodeCreated)
  3635. goto PDD_Critical_Error_Handler;
  3636. //
  3637. // =================================
  3638. // Process each line in our section. Each line should look like:
  3639. // <hardware-id>=<inf pathname>
  3640. // or in the case of drivers that aren't on the product CD:
  3641. // <hardware-id>=<inf pathname>,<directory on recovery floppy>,<description of recovery floppy>,<disk tag of recovery floppy>
  3642. //
  3643. // If we see an entry like this, we'll know that in the case of system recovery, the
  3644. // file should be retrived from a floppy, and not the Windows CD.
  3645. // =================================
  3646. //
  3647. bLineExists = SetupFindFirstLine(hAnswerInf, pszSectionName, NULL, &InfContext);
  3648. //
  3649. // =================================
  3650. // Let caller know we've go entries to populate.
  3651. // =================================
  3652. //
  3653. if (pfPopulated)
  3654. *pfPopulated = bLineExists;
  3655. while (bLineExists) {
  3656. #ifdef DEBUG_LOGLOG
  3657. LOG_Write(L"");
  3658. #endif
  3659. //
  3660. // =================================
  3661. // Step 1: Set the hardwareID of the devnode.
  3662. // =================================
  3663. //
  3664. //
  3665. // retrieve the hardwareID from the line
  3666. //
  3667. dwSize = MAX_PATH;
  3668. if (!SetupGetStringField(&InfContext, 0, szBuffer, dwSize, &dwSize)) {
  3669. #ifdef DEBUG_LOGLOG
  3670. LOG_WriteLastError();
  3671. #endif
  3672. bAllOK = FALSE;
  3673. goto PDD_Next_Inf_Line;
  3674. }
  3675. #ifdef DEBUG_LOGLOG
  3676. LOG_Write(L"HardwareID=%s", szBuffer);
  3677. #endif
  3678. //
  3679. // and then set it to the devnode,
  3680. //
  3681. if (!SetupDiSetDeviceRegistryProperty(hDevInfo,
  3682. &DeviceInfoData,
  3683. SPDRP_HARDWAREID,
  3684. (PBYTE)szBuffer,
  3685. AS ( szBuffer ) * sizeof(WCHAR))) {
  3686. #ifdef DEBUG_LOGLOG
  3687. LOG_WriteLastError();
  3688. #endif
  3689. // If someone removed the devnode, we need to re-create it and repeat this pnp device
  3690. //
  3691. if (ERROR_NO_SUCH_DEVINST == GetLastError()) {
  3692. // Re-create the SYSPREP_TEMPORARY devnode again
  3693. //
  3694. bAllOK = CreateSysprepTemporaryDevnode(&hDevInfo, &DeviceInfoData);
  3695. bNodeCreated = bAllOK;
  3696. // Set the hardwareID again
  3697. //
  3698. //
  3699. // NTRAID#NTBUG9-551868-2002/02/26-brucegr: Need to increase size parameter by one WCHAR
  3700. //
  3701. if (bNodeCreated && !SetupDiSetDeviceRegistryProperty(hDevInfo,
  3702. &DeviceInfoData,
  3703. SPDRP_HARDWAREID,
  3704. (PBYTE)szBuffer,
  3705. lstrlen(szBuffer) * sizeof(WCHAR))) {
  3706. // We failed again, then quit
  3707. //
  3708. bAllOK = FALSE;
  3709. goto PDD_Critical_Error_Handler;
  3710. }
  3711. }
  3712. else {
  3713. bAllOK = FALSE;
  3714. goto PDD_Next_Inf_Line;
  3715. }
  3716. }
  3717. //
  3718. // make sure that there's no existing compatible list, since we're reusing
  3719. // the dummy devnode
  3720. //
  3721. if (!SetupDiDestroyDriverInfoList(hDevInfo, &DeviceInfoData, SPDIT_COMPATDRIVER)) {
  3722. #ifdef DEBUG_LOGLOG
  3723. LOG_WriteLastError();
  3724. #endif
  3725. bAllOK = FALSE;
  3726. goto PDD_Next_Inf_Line;
  3727. }
  3728. //
  3729. // Build the SP_DEVINSTALL_PARAMS for this node.
  3730. //
  3731. DevInstallParams.cbSize = sizeof(DevInstallParams);
  3732. if (!SetupDiGetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams)) {
  3733. #ifdef DEBUG_LOGLOG
  3734. LOG_WriteLastError();
  3735. #endif
  3736. bAllOK = FALSE;
  3737. goto PDD_Next_Inf_Line;
  3738. }
  3739. //
  3740. // set the Flags field: only search the INF file specified in DriverPath field;
  3741. // don't create a copy queue, use the provided one in FileQueue; don't call the
  3742. // Configuration Manager while populating the CriticalDeviceDatabase.
  3743. //
  3744. DevInstallParams.Flags |= DI_ENUMSINGLEINF;
  3745. DevInstallParams.Flags |= DI_NOVCP;
  3746. DevInstallParams.Flags |= DI_DONOTCALLCONFIGMG;
  3747. //
  3748. // set the file queue field
  3749. //
  3750. QueueHandle = SetupOpenFileQueue();
  3751. if (QueueHandle == INVALID_HANDLE_VALUE) {
  3752. #ifdef DEBUG_LOGLOG
  3753. LOG_WriteLastError();
  3754. #endif
  3755. bAllOK = FALSE;
  3756. goto PDD_Next_Inf_Line;
  3757. }
  3758. DevInstallParams.FileQueue = QueueHandle;
  3759. //
  3760. // set the device's inf pathname
  3761. //
  3762. dwSize = MAX_PATH;
  3763. if (!SetupGetStringField(&InfContext, 1, szBuffer, dwSize, &dwSize)) {
  3764. #ifdef DEBUG_LOGLOG
  3765. LOG_WriteLastError();
  3766. #endif
  3767. bAllOK = FALSE;
  3768. goto PDD_Next_Inf_Line;
  3769. }
  3770. ExpandEnvironmentStrings(szBuffer, DevInstallParams.DriverPath, MAX_PATH);
  3771. #ifdef DEBUG_LOGLOG
  3772. LOG_Write(L"DriverPath=%s", DevInstallParams.DriverPath);
  3773. #endif
  3774. if (!SetupDiSetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams)) {
  3775. #ifdef DEBUG_LOGLOG
  3776. LOG_WriteLastError();
  3777. #endif
  3778. bAllOK = FALSE;
  3779. goto PDD_Next_Inf_Line;
  3780. }
  3781. //
  3782. // Register the newly created device instance with the PnP Manager.
  3783. //
  3784. if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
  3785. hDevInfo,
  3786. &DeviceInfoData)) {
  3787. #ifdef DEBUG_LOGLOG
  3788. LOG_WriteLastError();
  3789. #endif
  3790. bAllOK = FALSE;
  3791. goto PDD_Next_Inf_Line;
  3792. }
  3793. //
  3794. // =================================
  3795. // Step 2: Perform a compatible driver search.
  3796. // =================================
  3797. //
  3798. if (!SetupDiBuildDriverInfoList(hDevInfo, &DeviceInfoData, SPDIT_COMPATDRIVER)) {
  3799. #ifdef DEBUG_LOGLOG
  3800. LOG_WriteLastError();
  3801. #endif
  3802. bAllOK = FALSE;
  3803. goto PDD_Next_Inf_Line;
  3804. }
  3805. // Make sure there is at least 1 compat driver for this device.
  3806. // If there is not, and then we just process the next one in the list
  3807. if (!SetupDiEnumDriverInfo(hDevInfo,
  3808. &DeviceInfoData,
  3809. SPDIT_COMPATDRIVER,
  3810. 0,
  3811. &DriverInfoData))
  3812. {
  3813. // Check to see what the error was. Any error other than ERROR_NO_MORE_ITEMS
  3814. // will be flaged, by setting the bAllOK return value to FALSE
  3815. if (ERROR_NO_MORE_ITEMS != GetLastError())
  3816. {
  3817. #ifdef DEBUG_LOGLOG
  3818. LOG_WriteLastError();
  3819. #endif
  3820. bAllOK = FALSE;
  3821. }
  3822. goto PDD_Next_Inf_Line;
  3823. }
  3824. //
  3825. // =================================
  3826. // Step 3: Select the best compatible driver.
  3827. // =================================
  3828. //
  3829. if (!SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV,
  3830. hDevInfo,
  3831. &DeviceInfoData)) {
  3832. #ifdef DEBUG_LOGLOG
  3833. LOG_WriteLastError();
  3834. #endif
  3835. bAllOK = FALSE;
  3836. goto PDD_Next_Inf_Line;
  3837. }
  3838. //
  3839. // =================================
  3840. // Step 4: Install the driver files.
  3841. // =================================
  3842. //
  3843. if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
  3844. hDevInfo,
  3845. &DeviceInfoData)) {
  3846. #ifdef DEBUG_LOGLOG
  3847. LOG_WriteLastError();
  3848. #endif
  3849. bAllOK = FALSE;
  3850. goto PDD_Next_Inf_Line;
  3851. }
  3852. //
  3853. // Need to commit the file queue here, so the later steps can properly
  3854. // be executed in case the device doesn't use the already existing
  3855. // coinstaller(s).
  3856. //
  3857. pSysprepContext = (PSYSPREP_QUEUE_CONTEXT) InitSysprepQueueCallback();
  3858. //
  3859. // Retrieve DirectoryOnSourceDevice from the inf line, if any
  3860. //
  3861. dwSize = MAX_PATH;
  3862. DirectoryOnSourceDevice[0] = L'\0';
  3863. if (!SetupGetStringField(&InfContext, 2, DirectoryOnSourceDevice, dwSize, &dwSize)) {
  3864. DirectoryOnSourceDevice[0] = L'\0';
  3865. }
  3866. if (DirectoryOnSourceDevice[0] != L'\0') {
  3867. pSysprepContext->DirectoryOnSourceDevice = DirectoryOnSourceDevice;
  3868. }
  3869. //
  3870. // Retrieve DiskDescription from the inf line, if any
  3871. //
  3872. dwSize = MAX_PATH;
  3873. DiskDescription[0] = L'\0';
  3874. if (!SetupGetStringField(&InfContext, 3, DiskDescription, dwSize, &dwSize)) {
  3875. DiskDescription[0] = L'\0';
  3876. }
  3877. if (DiskDescription[0] != L'\0') {
  3878. pSysprepContext->DiskDescription = DiskDescription;
  3879. }
  3880. //
  3881. // Retrieve DiskTag from the inf line, if any
  3882. //
  3883. dwSize = MAX_PATH;
  3884. DiskTag[0] = L'\0';
  3885. if (!SetupGetStringField(&InfContext, 4, DiskTag, dwSize, &dwSize)) {
  3886. DiskTag[0] = L'\0';
  3887. }
  3888. if (DiskTag[0] != L'\0') {
  3889. pSysprepContext->DiskTag = DiskTag;
  3890. }
  3891. //
  3892. // Commit the file queue
  3893. //
  3894. if (!SetupCommitFileQueue(NULL,
  3895. QueueHandle,
  3896. SysprepQueueCallback,
  3897. pSysprepContext)) {
  3898. #ifdef DEBUG_LOGLOG
  3899. LOG_WriteLastError();
  3900. #endif
  3901. }
  3902. FreeSysprepContext(pSysprepContext);
  3903. //
  3904. // =================================
  3905. // Step 4a: Dis-associate file copy queue before we close
  3906. // the queue.
  3907. // =================================
  3908. //
  3909. DevInstallParams.cbSize = sizeof(DevInstallParams);
  3910. if (!SetupDiGetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams)) {
  3911. #ifdef DEBUG_LOGLOG
  3912. LOG_WriteLastError();
  3913. #endif
  3914. bAllOK = FALSE;
  3915. goto PDD_Next_Inf_Line;
  3916. }
  3917. //
  3918. // Remove the DI_NOVCP flag and NULL out the FileQueue.
  3919. //
  3920. DevInstallParams.Flags &= ~DI_NOVCP;
  3921. DevInstallParams.FileQueue = NULL;
  3922. if (!SetupDiSetDeviceInstallParams(hDevInfo, &DeviceInfoData, &DevInstallParams)) {
  3923. #ifdef DEBUG_LOGLOG
  3924. LOG_WriteLastError();
  3925. #endif
  3926. bAllOK = FALSE;
  3927. goto PDD_Next_Inf_Line;
  3928. }
  3929. SetupCloseFileQueue(QueueHandle);
  3930. QueueHandle = INVALID_HANDLE_VALUE;
  3931. //
  3932. // =================================
  3933. // Step 5: Register the device-specific coinstallers.
  3934. // =================================
  3935. //
  3936. if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
  3937. hDevInfo,
  3938. &DeviceInfoData)) {
  3939. #ifdef DEBUG_LOGLOG
  3940. LOG_WriteLastError();
  3941. #endif
  3942. bAllOK = FALSE;
  3943. goto PDD_Next_Inf_Line;
  3944. }
  3945. //
  3946. // =================================
  3947. // Step 6: Install the device.
  3948. // =================================
  3949. //
  3950. if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
  3951. hDevInfo,
  3952. &DeviceInfoData)) {
  3953. #ifdef DEBUG_LOGLOG
  3954. LOG_WriteLastError();
  3955. #endif
  3956. bAllOK = FALSE;
  3957. goto PDD_Next_Inf_Line;
  3958. }
  3959. //
  3960. // =================================
  3961. // Step 7: Retrieve upper filters, lower filters,
  3962. // and controlling service, save them back
  3963. // to the inf file.
  3964. // =================================
  3965. //
  3966. //
  3967. // retrieve device upperfilters (REG_MULTI_SZ)
  3968. //
  3969. if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
  3970. &DeviceInfoData,
  3971. SPDRP_UPPERFILTERS,
  3972. NULL,
  3973. (PBYTE)szBuffer,
  3974. sizeof(szBuffer),
  3975. NULL)) {
  3976. szBuffer[0] = L'\0';
  3977. }
  3978. for (pszFilter = szBuffer; *pszFilter; pszFilter += (lstrlen(pszFilter) + 1)) {
  3979. StringCchPrintfA(szOutBufferA, AS ( szOutBufferA ), "Upperfilter=%S\r\n", pszFilter);
  3980. if (AddCleanupNode(pszFilter)) {
  3981. SetFilePointer(hInfFile, 0L, 0L, FILE_END);
  3982. WriteFile(hInfFile, szOutBufferA, strlen(szOutBufferA), &dwSize, NULL);
  3983. }
  3984. #ifdef DEBUG_LOGLOG
  3985. LOG_Write(L"Upperfilter=%s", pszFilter);
  3986. #endif
  3987. }
  3988. //
  3989. // retrieve device lowerfilters (REG_MULTI_SZ)
  3990. //
  3991. if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
  3992. &DeviceInfoData,
  3993. SPDRP_LOWERFILTERS,
  3994. NULL,
  3995. (PBYTE)szBuffer,
  3996. sizeof(szBuffer),
  3997. NULL)) {
  3998. szBuffer[0] = L'\0';
  3999. }
  4000. for (pszFilter = szBuffer; *pszFilter; pszFilter += (lstrlen(pszFilter) + 1)) {
  4001. StringCchPrintfA(szOutBufferA, AS ( szOutBufferA ), "Lowerfilter=%S\r\n", pszFilter);
  4002. if (AddCleanupNode(pszFilter)) {
  4003. SetFilePointer(hInfFile, 0L, 0L, FILE_END);
  4004. WriteFile(hInfFile, szOutBufferA, strlen(szOutBufferA), &dwSize, NULL);
  4005. }
  4006. #ifdef DEBUG_LOGLOG
  4007. LOG_Write(L"Lowerfilter=%s", pszFilter);
  4008. #endif
  4009. }
  4010. //
  4011. // retrieve device its controlling service (REG_SZ)
  4012. //
  4013. if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
  4014. &DeviceInfoData,
  4015. SPDRP_SERVICE,
  4016. NULL,
  4017. (PBYTE)szBuffer,
  4018. sizeof(szBuffer),
  4019. NULL)) {
  4020. szBuffer[0] = L'\0';
  4021. }
  4022. if (szBuffer[0] != L'\0') {
  4023. StringCchPrintfA(szOutBufferA, AS ( szOutBufferA ), "Service=%S\r\n", szBuffer);
  4024. if (AddCleanupNode(szBuffer)) {
  4025. SetFilePointer(hInfFile, 0L, 0L, FILE_END);
  4026. WriteFile(hInfFile, szOutBufferA, strlen(szOutBufferA), &dwSize, NULL);
  4027. }
  4028. #ifdef DEBUG_LOGLOG
  4029. LOG_Write(L"Service=%s", szBuffer);
  4030. #endif
  4031. }
  4032. PDD_Next_Inf_Line:
  4033. if (QueueHandle != INVALID_HANDLE_VALUE) {
  4034. SetupCloseFileQueue(QueueHandle);
  4035. QueueHandle = INVALID_HANDLE_VALUE;
  4036. }
  4037. //
  4038. // Get the next line from the relevant section in the inf file.
  4039. //
  4040. bLineExists = SetupFindNextLine(&InfContext, &InfContext);
  4041. }
  4042. //
  4043. // =================================
  4044. // Cleanup for a successful run
  4045. // =================================
  4046. //
  4047. //
  4048. // remove the SYSPREP_TEMPORARY node under Root
  4049. //
  4050. SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, &DeviceInfoData);
  4051. SetupDiDestroyDeviceInfoList(hDevInfo);
  4052. CloseHandle(hInfFile);
  4053. SetupCloseInfFile(hAnswerInf);
  4054. //
  4055. // Backup the system hive to the Repair folder
  4056. //
  4057. if (!BackupHives()) {
  4058. #ifdef DEBUG_LOGLOG
  4059. LOG_Write(L"ERROR - Unable to backup the system hive.");
  4060. #endif
  4061. bAllOK = FALSE;
  4062. }
  4063. #ifdef DEBUG_LOGLOG
  4064. LOG_DeInit();
  4065. #endif
  4066. FreeCleanupList(&g_pCleanupListHead);
  4067. return bAllOK;
  4068. //
  4069. // =================================
  4070. PDD_Critical_Error_Handler:
  4071. // =================================
  4072. //
  4073. #ifdef DEBUG_LOGLOG
  4074. LOG_WriteLastError();
  4075. #endif
  4076. if (QueueHandle != INVALID_HANDLE_VALUE) {
  4077. SetupCloseFileQueue(QueueHandle);
  4078. }
  4079. //
  4080. // remove the SYSPREP_TEMPORARY node under Root
  4081. //
  4082. if (bNodeCreated) {
  4083. SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, &DeviceInfoData);
  4084. }
  4085. if (hDevInfo != INVALID_HANDLE_VALUE) {
  4086. SetupDiDestroyDeviceInfoList(hDevInfo);
  4087. }
  4088. if (hInfFile != INVALID_HANDLE_VALUE) {
  4089. CloseHandle(hInfFile);
  4090. }
  4091. if (hAnswerInf != INVALID_HANDLE_VALUE) {
  4092. SetupCloseInfFile(hAnswerInf);
  4093. }
  4094. #ifdef DEBUG_LOGLOG
  4095. LOG_DeInit();
  4096. #endif
  4097. FreeCleanupList(&g_pCleanupListHead);
  4098. return FALSE;
  4099. }
  4100. /*++
  4101. ===============================================================================
  4102. Routine Description:
  4103. Check to see if the service name passed in is in use by a PnP enumerated
  4104. device.
  4105. Arguments:
  4106. lpszServiceName
  4107. Return Value:
  4108. TRUE. The service is in use, or will be in use by a device (as evidenced by
  4109. the presence of the service name as a registry property for an enumerated
  4110. device)
  4111. FALSE. The service is not in use.
  4112. If LastError is set, then a bad thing happed, otherwise the service is
  4113. just not being used
  4114. Assumptions:
  4115. ===============================================================================
  4116. --*/
  4117. BOOL ServiceInUseByDevice
  4118. (
  4119. LPTSTR lpszServiceName
  4120. )
  4121. {
  4122. HDEVINFO DeviceInfoSet;
  4123. HDEVINFO NewDeviceInfoSet;
  4124. DWORD i;
  4125. SP_DEVINFO_DATA DevInfoData;
  4126. TCHAR szServiceName[MAX_PATH];
  4127. TCHAR szDeviceClass[MAX_PATH];
  4128. BOOL bRet = FALSE;
  4129. TCHAR szLegacyClass[MAX_CLASS_NAME_LEN];
  4130. // Clear the last error
  4131. SetLastError(0);
  4132. // Get the Class description for LegacyDriver
  4133. if (!SetupDiClassNameFromGuid(&GUID_DEVCLASS_LEGACYDRIVER,
  4134. szLegacyClass,
  4135. sizeof(szLegacyClass)/sizeof(TCHAR),
  4136. NULL))
  4137. {
  4138. #ifdef DEBUG_LOGLOG
  4139. LOG_Write(L"Unable to get LegacyDriver Class NAME");
  4140. #endif
  4141. // NOTE: LastError will be set to the appropriate error code by
  4142. // SetupDiGetClassDescription
  4143. return FALSE;
  4144. }
  4145. // Create a device information set that will be used to enumerate all
  4146. // present devices
  4147. DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
  4148. if(DeviceInfoSet == INVALID_HANDLE_VALUE)
  4149. {
  4150. #ifdef DEBUG_LOGLOG
  4151. LOG_Write(L"Unable to Create a device info list");
  4152. #endif
  4153. SetLastError(E_FAIL);
  4154. return FALSE;
  4155. }
  4156. // Get a list of all present devices on the system
  4157. NewDeviceInfoSet = SetupDiGetClassDevsEx(NULL,
  4158. NULL,
  4159. NULL,
  4160. DIGCF_PRESENT | DIGCF_ALLCLASSES,
  4161. DeviceInfoSet,
  4162. NULL,
  4163. NULL);
  4164. if(NewDeviceInfoSet == INVALID_HANDLE_VALUE)
  4165. {
  4166. #ifdef DEBUG_LOGLOG
  4167. LOG_Write(L"Unable to enumerate present devices");
  4168. #endif
  4169. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  4170. SetLastError(E_FAIL);
  4171. return FALSE;
  4172. }
  4173. // Enumerate the list of devices, checking to see if the service listed in the
  4174. // registry matches the service we are interested in.
  4175. i = 0;
  4176. DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  4177. while (SetupDiEnumDeviceInfo(NewDeviceInfoSet, i, &DevInfoData))
  4178. {
  4179. // See if this is devnode is using the service we care about.
  4180. // if so, then we will check to see if it is a legacy devnode. If it
  4181. // is NOT a legacy devnode, then we will not mess with it, because
  4182. // the service is in use by a real device.
  4183. if (SetupDiGetDeviceRegistryProperty(NewDeviceInfoSet,
  4184. &DevInfoData,
  4185. SPDRP_SERVICE,
  4186. NULL,
  4187. (PBYTE) szServiceName,
  4188. sizeof(szServiceName),
  4189. NULL))
  4190. {
  4191. // See if this is the service we are looking for
  4192. if (0 == lstrcmpiW(lpszServiceName, szServiceName))
  4193. {
  4194. // Check for a legacy class device
  4195. if (SetupDiGetDeviceRegistryProperty(NewDeviceInfoSet,
  4196. &DevInfoData,
  4197. SPDRP_CLASS,
  4198. NULL,
  4199. (PBYTE) szDeviceClass,
  4200. sizeof(szDeviceClass),
  4201. NULL))
  4202. {
  4203. // We have the class, lets see if it is a legacy device
  4204. if (0 != lstrcmpiW(szLegacyClass, szDeviceClass))
  4205. {
  4206. // it is NOT a legacy device, so this service is in use!
  4207. bRet = TRUE;
  4208. break;
  4209. }
  4210. }
  4211. else
  4212. {
  4213. // We don't know the class, but it is not legacy (otherwise we
  4214. // would have gotten the class returned above, so assume it is
  4215. // is use!
  4216. bRet = TRUE;
  4217. break;
  4218. }
  4219. }
  4220. }
  4221. ++i;
  4222. }
  4223. // Clean up the device info sets that were allocated
  4224. SetupDiDestroyDeviceInfoList(NewDeviceInfoSet);
  4225. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  4226. return bRet;
  4227. }
  4228. BOOL
  4229. CleanDeviceDatabase(
  4230. VOID
  4231. )
  4232. /*++
  4233. ===============================================================================
  4234. Routine Description:
  4235. Parse the [SysprepCleanup] section in the sysprep.inf file, which was
  4236. created during the PopulateDeviceDatabase stage, and disable those
  4237. listed services/upperfilters/lowerfilters which don't have associated
  4238. physical devices.
  4239. The strategy here is that we try to stop each listed service/upperfilter/
  4240. lowerfilter. It will only be stopped if it's not currently running (so
  4241. not controlling a PnP devnode), or is associated with a legacy devnode
  4242. (Root\LEGACY_<SvcName>\0000). Once it can be stopped, we can safely
  4243. disable it.
  4244. Arguments:
  4245. None.
  4246. Return Value:
  4247. TRUE. No errors encountered
  4248. FALSE. Some error occurred. It's not likely that the call will be able
  4249. to do much though.
  4250. Assumptions:
  4251. 1. All listed services/upperfilters/lowerfilters have no dependencies.
  4252. 2. No service's/upperfilter's/lowerfilter's name exceeds MAX_PATH characters.
  4253. ===============================================================================
  4254. --*/
  4255. {
  4256. BOOL bAllOK = TRUE;
  4257. PCWSTR pszSectionName = L"SysprepCleanup";
  4258. WCHAR szSysprepInfFile[] = L"?:\\sysprep\\sysprep.inf";
  4259. #ifdef DEBUG_LOGLOG
  4260. WCHAR szLogFile[] = L"?:\\sysprep.log";
  4261. #endif
  4262. WCHAR szServiceName[MAX_PATH];
  4263. WCHAR szBuffer[MAX_PATH], *pszDevID;
  4264. HINF hAnswerInf = INVALID_HANDLE_VALUE;
  4265. BOOL bLineExists;
  4266. INFCONTEXT InfContext;
  4267. DWORD dwSize;
  4268. CONFIGRET cfgRetVal;
  4269. HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
  4270. SP_DEVINFO_DATA DeviceInfoData;
  4271. SC_HANDLE hSC = NULL;
  4272. SC_HANDLE hSvc = NULL;
  4273. LPQUERY_SERVICE_CONFIG psvcConfig = NULL;
  4274. DWORD Type, l;
  4275. HKEY hKey;
  4276. if (!GetWindowsDirectory(szBuffer, MAX_PATH)) {
  4277. //
  4278. // Unable to get Windows Directory
  4279. //
  4280. return FALSE;
  4281. }
  4282. szSysprepInfFile[0] = szBuffer[0];
  4283. #ifdef DEBUG_LOGLOG
  4284. szLogFile[0] = szBuffer[0];
  4285. LOG_Init(szLogFile);
  4286. LOG_Write(L">>\r\n>> CleanDeviceDatabase\r\n>>\r\n");
  4287. LOG_Write(L"Sysprep.inf=%s", szSysprepInfFile);
  4288. #endif
  4289. //
  4290. // =================================
  4291. // HACK. Winlogon may erroneously append a ',' onto
  4292. // the end of the path to explorer. This would normally
  4293. // get fixed up by ie.inf, but for the sysprep case,
  4294. // this inf isn't run, so we'll continue to have this
  4295. // bad path in the registry. Fix it here.
  4296. // =================================
  4297. //
  4298. //
  4299. // Open HKLM\Software\Microsoft\Windows NT\CurrentVersion\WinLogon
  4300. //
  4301. l = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  4302. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  4303. 0,
  4304. KEY_ALL_ACCESS,
  4305. &hKey );
  4306. if( l == NO_ERROR ) {
  4307. //
  4308. // Query the value of the Shell Key.
  4309. //
  4310. //
  4311. // ISSUE-2002/02/26-brucegr: dwSize = sizeof(szBuffer);
  4312. //
  4313. dwSize = sizeof(szBuffer)/sizeof(szBuffer[0]);
  4314. l = RegQueryValueEx( hKey,
  4315. TEXT("Shell"),
  4316. NULL,
  4317. &Type,
  4318. (LPBYTE)szBuffer,
  4319. &dwSize );
  4320. if( l == NO_ERROR ) {
  4321. pszDevID = wcschr( szBuffer, L',' );
  4322. if( pszDevID ) {
  4323. //
  4324. // We hit, so we should set it back to "Explorer.exe"
  4325. //
  4326. StringCchCopy ( szBuffer, AS ( szBuffer ), L"Explorer.exe" );
  4327. //
  4328. // Now set the key with our new value.
  4329. //
  4330. l = RegSetValueEx( hKey,
  4331. TEXT("Shell"),
  4332. 0,
  4333. REG_SZ,
  4334. (CONST BYTE *)szBuffer,
  4335. (lstrlen( szBuffer ) + 1) * sizeof(WCHAR));
  4336. }
  4337. }
  4338. RegCloseKey(hKey);
  4339. }
  4340. //
  4341. // =================================
  4342. // Open the sysprep.inf file. Since we don't know what the user has in
  4343. // here, so try opening as both styles.
  4344. // =================================
  4345. //
  4346. //
  4347. // ISSUE-2002/02/26-brucegr: You can pass in both bits in one call to SetupOpenInfFile
  4348. //
  4349. hAnswerInf = SetupOpenInfFile(szSysprepInfFile, NULL, INF_STYLE_WIN4, NULL);
  4350. if (hAnswerInf == INVALID_HANDLE_VALUE) {
  4351. hAnswerInf = SetupOpenInfFile(szSysprepInfFile, NULL, INF_STYLE_OLDNT, NULL);
  4352. if (hAnswerInf == INVALID_HANDLE_VALUE) {
  4353. //
  4354. // User didn't give us a sysprep.inf. Return as if nothing
  4355. // happened.
  4356. //
  4357. return TRUE;
  4358. }
  4359. }
  4360. //
  4361. // =================================
  4362. // Remove the buildmassstoragesection=yes if it exists.
  4363. // =================================
  4364. //
  4365. WritePrivateProfileString(SYSPREP_SECTION, SYSPREP_BUILDMASSSTORAGE_KEY, NULL, szSysprepInfFile);
  4366. //
  4367. // =================================
  4368. // Establish a connection to the service control manager on the local
  4369. // computer to retrieve status and reconfig services.
  4370. // =================================
  4371. //
  4372. hSC = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  4373. if (hSC == NULL) {
  4374. goto CDD_Critical_Error_Handler;
  4375. }
  4376. //
  4377. // =================================
  4378. // Process each line in our section
  4379. // =================================
  4380. //
  4381. bLineExists = SetupFindFirstLine(hAnswerInf, pszSectionName, NULL, &InfContext);
  4382. while (bLineExists) {
  4383. #ifdef DEBUG_LOGLOG
  4384. LOG_Write(L"");
  4385. #endif
  4386. //
  4387. // We've got a line, and it should look like:
  4388. // <key>=<service name>
  4389. //
  4390. //
  4391. // =================================
  4392. // Retrieve the service name from the line
  4393. // =================================
  4394. //
  4395. dwSize = MAX_PATH;
  4396. if (!SetupGetStringField(&InfContext, 1, szServiceName, dwSize, &dwSize)) {
  4397. #ifdef DEBUG_LOGLOG
  4398. LOG_WriteLastError();
  4399. #endif
  4400. bAllOK = FALSE;
  4401. goto CDD_Next_Inf_Line;
  4402. }
  4403. #ifdef DEBUG_LOGLOG
  4404. LOG_Write(L"Service=%s", szServiceName);
  4405. #endif
  4406. //
  4407. // ISSUE-2002/02/26-brucegr: EXPENSIVE!!! Should build the in-use service list once and then loop through INF.
  4408. // Code is currently enumerating all devices for every INF entry. Bad times.
  4409. //
  4410. // Check to see if the service is in use by a currently present, enumerated
  4411. // device. If it is, then skip it, otherwise try to stop it, etc
  4412. if (ServiceInUseByDevice(szServiceName))
  4413. {
  4414. #ifdef DEBUG_LOGLOG
  4415. LOG_Write(L"Service is in use by a device. Skipping...");
  4416. #endif
  4417. goto CDD_Next_Inf_Line;
  4418. }
  4419. else
  4420. {
  4421. if (E_FAIL == GetLastError())
  4422. {
  4423. #ifdef DEBUG_LOGLOG
  4424. LOG_WriteLastError();
  4425. #endif
  4426. bAllOK = FALSE;
  4427. goto CDD_Next_Inf_Line;
  4428. }
  4429. #ifdef DEBUG_LOGLOG
  4430. LOG_Write(L"Service is not in use by a device. Attempting to disable...");
  4431. #endif
  4432. }
  4433. //
  4434. // =================================
  4435. // Open the service to query its status, start type, and disable
  4436. // it if it is not running and not yet disabled.
  4437. // =================================
  4438. //
  4439. hSvc = OpenService(
  4440. hSC,
  4441. szServiceName,
  4442. SERVICE_STOP | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
  4443. );
  4444. if (hSvc == NULL) {
  4445. #ifdef DEBUG_LOGLOG
  4446. LOG_WriteLastError();
  4447. #endif
  4448. bAllOK = FALSE;
  4449. goto CDD_Next_Inf_Line;
  4450. }
  4451. //
  4452. // =================================
  4453. // If PnP driver then don't disable the service and continue.
  4454. // =================================
  4455. //
  4456. if (IsPnPDriver(szServiceName)) {
  4457. #ifdef DEBUG_LOGLOG
  4458. LOG_Write(L"IsPnPDriver() returned TRUE. Continue to next entry.");
  4459. #endif
  4460. bAllOK = FALSE;
  4461. goto CDD_Next_Inf_Line;
  4462. }
  4463. //
  4464. // =================================
  4465. // Query the service start type.
  4466. // =================================
  4467. //
  4468. psvcConfig = (LPQUERY_SERVICE_CONFIG) malloc(sizeof(QUERY_SERVICE_CONFIG));
  4469. if (psvcConfig == NULL) {
  4470. #ifdef DEBUG_LOGLOG
  4471. LOG_Write(L"ERROR@malloc - Not enough memory.");
  4472. #endif
  4473. bAllOK = FALSE;
  4474. goto CDD_Next_Inf_Line;
  4475. }
  4476. if (!QueryServiceConfig(hSvc, psvcConfig, sizeof(QUERY_SERVICE_CONFIG), &dwSize)) {
  4477. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  4478. #ifdef DEBUG_LOGLOG
  4479. LOG_WriteLastError();
  4480. #endif
  4481. bAllOK = FALSE;
  4482. goto CDD_Next_Inf_Line;
  4483. }
  4484. else {
  4485. //
  4486. // Need to expand the service configuration buffer and call the API again.
  4487. //
  4488. void *pTemp = realloc(psvcConfig, dwSize);
  4489. if (pTemp == NULL) {
  4490. #ifdef DEBUG_LOGLOG
  4491. LOG_Write(L"ERROR@realloc - Not enough memory.");
  4492. #endif
  4493. bAllOK = FALSE;
  4494. goto CDD_Next_Inf_Line;
  4495. }
  4496. psvcConfig = (LPQUERY_SERVICE_CONFIG) pTemp;
  4497. if (!QueryServiceConfig(hSvc, psvcConfig, dwSize, &dwSize)) {
  4498. #ifdef DEBUG_LOGLOG
  4499. LOG_WriteLastError();
  4500. #endif
  4501. bAllOK = FALSE;
  4502. goto CDD_Next_Inf_Line;
  4503. }
  4504. }
  4505. }
  4506. #ifdef DEBUG_LOGLOG
  4507. switch(psvcConfig->dwStartType) {
  4508. case SERVICE_BOOT_START:
  4509. LOG_Write(L"StartType=SERVICE_BOOT_START");
  4510. break;
  4511. case SERVICE_SYSTEM_START:
  4512. LOG_Write(L"StartType=SERVICE_SYSTEM_START");
  4513. break;
  4514. case SERVICE_AUTO_START:
  4515. LOG_Write(L"StartType=SERVICE_AUTO_START");
  4516. break;
  4517. case SERVICE_DEMAND_START:
  4518. LOG_Write(L"StartType=SERVICE_DEMAND_START");
  4519. break;
  4520. case SERVICE_DISABLED:
  4521. LOG_Write(L"StartType=SERVICE_DISABLED");
  4522. break;
  4523. }
  4524. #endif
  4525. //
  4526. // =================================
  4527. // Retrieve device IDs for the device instances controlled by the service.
  4528. // ISSUE-2002/02/26-brucegr: Need to call CM_Get_Device_ID_List_Size to get
  4529. // the required buffer size. But we're OK here, since by reaching this point,
  4530. // we know we have a single device instance.
  4531. // =================================
  4532. //
  4533. cfgRetVal = CM_Get_Device_ID_List(
  4534. szServiceName,
  4535. szBuffer,
  4536. sizeof(szBuffer)/sizeof(WCHAR),
  4537. CM_GETIDLIST_FILTER_SERVICE | CM_GETIDLIST_DONOTGENERATE
  4538. );
  4539. if (cfgRetVal != CR_SUCCESS) {
  4540. #ifdef DEBUG_LOGLOG
  4541. LOG_Write(L"ERROR@CM_Get_Device_ID_List - (%08X)", cfgRetVal);
  4542. #endif
  4543. bAllOK = FALSE;
  4544. goto CDD_Next_Inf_Line;
  4545. }
  4546. //
  4547. // =================================
  4548. // Remove all "bogus" devnodes.
  4549. // =================================
  4550. //
  4551. //
  4552. // Create an empty device information set.
  4553. //
  4554. hDevInfo = SetupDiCreateDeviceInfoList(NULL, NULL);
  4555. if (hDevInfo == INVALID_HANDLE_VALUE) {
  4556. #ifdef DEBUG_LOGLOG
  4557. LOG_WriteLastError();
  4558. #endif
  4559. bAllOK = FALSE;
  4560. goto CDD_Next_Inf_Line;
  4561. }
  4562. for (pszDevID = szBuffer; *pszDevID; pszDevID += (lstrlen(pszDevID) + 1)) {
  4563. #ifdef DEBUG_LOGLOG
  4564. LOG_Write(L"--> removing %s...", pszDevID);
  4565. #endif
  4566. //
  4567. // Open a device instance into the hDevInfo set
  4568. //
  4569. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  4570. if (!SetupDiOpenDeviceInfo(
  4571. hDevInfo,
  4572. pszDevID,
  4573. NULL,
  4574. 0,
  4575. &DeviceInfoData)
  4576. ) {
  4577. #ifdef DEBUG_LOGLOG
  4578. LOG_WriteLastError();
  4579. #endif
  4580. bAllOK = FALSE;
  4581. continue;
  4582. }
  4583. if (!SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, &DeviceInfoData)) {
  4584. #ifdef DEBUG_LOGLOG
  4585. LOG_WriteLastError();
  4586. #endif
  4587. bAllOK = FALSE;
  4588. }
  4589. #ifdef DEBUG_LOGLOG
  4590. LOG_Write(L"--> successfully done!");
  4591. #endif
  4592. }
  4593. SetupDiDestroyDeviceInfoList(hDevInfo);
  4594. hDevInfo = INVALID_HANDLE_VALUE;
  4595. //
  4596. // =================================
  4597. // Disable stopped and not-yet-disabled services
  4598. // =================================
  4599. //
  4600. #ifdef DEBUG_LOGLOG
  4601. LOG_Write(L"--> changing StartType to SERVICE_DISABLED...");
  4602. #endif
  4603. if (!ChangeServiceConfig(
  4604. hSvc,
  4605. SERVICE_NO_CHANGE,
  4606. SERVICE_DISABLED,
  4607. SERVICE_NO_CHANGE,
  4608. NULL,
  4609. NULL,
  4610. NULL,
  4611. NULL,
  4612. NULL,
  4613. NULL,
  4614. psvcConfig->lpDisplayName)) {
  4615. #ifdef DEBUG_LOGLOG
  4616. LOG_WriteLastError();
  4617. #endif
  4618. bAllOK = FALSE;
  4619. goto CDD_Next_Inf_Line;
  4620. }
  4621. #ifdef DEBUG_LOGLOG
  4622. LOG_Write(L"--> successfully done!");
  4623. #endif
  4624. CDD_Next_Inf_Line:
  4625. if (psvcConfig != NULL) {
  4626. free(psvcConfig);
  4627. psvcConfig = NULL;
  4628. }
  4629. if (hSvc != NULL) {
  4630. CloseServiceHandle(hSvc);
  4631. hSvc = NULL;
  4632. }
  4633. //
  4634. // Get the next line from the relevant section in the inf file
  4635. //
  4636. bLineExists = SetupFindNextLine(&InfContext, &InfContext);
  4637. }
  4638. //
  4639. // =================================
  4640. // Cleanup for a successful run
  4641. // =================================
  4642. //
  4643. CloseServiceHandle(hSC);
  4644. SetupCloseInfFile(hAnswerInf);
  4645. #ifdef DEBUG_LOGLOG
  4646. LOG_DeInit();
  4647. #endif
  4648. return bAllOK;
  4649. //
  4650. // =================================
  4651. CDD_Critical_Error_Handler:
  4652. //
  4653. // =================================
  4654. #ifdef DEBUG_LOGLOG
  4655. LOG_WriteLastError();
  4656. #endif
  4657. if (hSvc != NULL) {
  4658. CloseServiceHandle(hSvc);
  4659. }
  4660. if (hSC != NULL) {
  4661. CloseServiceHandle(hSC);
  4662. }
  4663. if (hAnswerInf != INVALID_HANDLE_VALUE) {
  4664. SetupCloseInfFile(hAnswerInf);
  4665. }
  4666. #ifdef DEBUG_LOGLOG
  4667. LOG_DeInit();
  4668. #endif
  4669. return FALSE;
  4670. }
  4671. BOOL
  4672. IsPnPDriver(
  4673. IN LPTSTR ServiceName
  4674. )
  4675. /*++
  4676. Routine Description:
  4677. This function checks whether a specified driver is a PnP driver
  4678. Arguments:
  4679. ServiceName - Specifies the driver of interest.
  4680. Return Value:
  4681. TRUE - if the driver is a PnP driver or if this cannot be determined.
  4682. FALSE - if the service is not a PnP driver.
  4683. --*/
  4684. {
  4685. CONFIGRET Status;
  4686. BOOL fRetStatus = TRUE;
  4687. WCHAR * pBuffer;
  4688. ULONG cchLen, ulRegDataType;
  4689. WCHAR szClassGuid[MAX_GUID_STRING_LEN];
  4690. DEVNODE DevNode;
  4691. //
  4692. // Allocate a buffer for the list of device instances associated with
  4693. // this service
  4694. //
  4695. Status = CM_Get_Device_ID_List_Size(
  4696. &cchLen, // list length in wchars
  4697. ServiceName, // pszFilter
  4698. CM_GETIDLIST_FILTER_SERVICE); // filter is a service name
  4699. if (Status != CR_SUCCESS)
  4700. {
  4701. #ifdef DEBUG_LOGLOG
  4702. LOG_Write(L"CM_Get_Device_ID_List_Size failed %#lx for service %ws\n",
  4703. Status, ServiceName);
  4704. #endif
  4705. return TRUE;
  4706. }
  4707. //
  4708. // If there are no devnodes, this is not a PnP driver
  4709. //
  4710. if (cchLen == 0)
  4711. {
  4712. #ifdef DEBUG_LOGLOG
  4713. LOG_Write(L"IsPnPDriver: %ws is not a PnP driver (no devnodes)\n",
  4714. ServiceName);
  4715. #endif
  4716. return FALSE;
  4717. }
  4718. pBuffer = (WCHAR *) LocalAlloc(0, cchLen * sizeof(WCHAR));
  4719. if (pBuffer == NULL)
  4720. {
  4721. #ifdef DEBUG_LOGLOG
  4722. LOG_Write(L"Couldn't allocate buffer for device list, error %lu\n",
  4723. GetLastError());
  4724. #endif
  4725. return TRUE;
  4726. }
  4727. //
  4728. // Initialize parameters for CM_Get_Device_ID_List, the same way as is
  4729. // normally done in the client side of the API
  4730. //
  4731. pBuffer[0] = L'\0';
  4732. //
  4733. // Get the list of device instances that are associated with this service
  4734. //
  4735. // (For legacy and PNP-aware services, we could get an empty device list.)
  4736. //
  4737. Status = CM_Get_Device_ID_List(
  4738. ServiceName, // pszFilter
  4739. pBuffer, // buffer for device list
  4740. cchLen, // buffer length in wchars
  4741. CM_GETIDLIST_FILTER_SERVICE | // filter is a service name
  4742. CM_GETIDLIST_DONOTGENERATE // do not generate an instance if none exists
  4743. );
  4744. if (Status != CR_SUCCESS)
  4745. {
  4746. #ifdef DEBUG_LOGLOG
  4747. LOG_Write(L"CM_Get_Device_ID_List failed %#lx for service %ws\n",
  4748. Status, ServiceName);
  4749. #endif
  4750. LocalFree(pBuffer);
  4751. return TRUE;
  4752. }
  4753. //
  4754. // If there's more than one devnode, this is a PnP driver
  4755. //
  4756. if (*(pBuffer + wcslen(pBuffer) + 1) != L'\0')
  4757. {
  4758. #ifdef DEBUG_LOGLOG
  4759. LOG_Write(L"IsPnPDriver: %ws is a PnP driver (more than 1 devnode)\n",
  4760. ServiceName);
  4761. #endif
  4762. LocalFree(pBuffer);
  4763. return TRUE;
  4764. }
  4765. //
  4766. // This has only one DevNode so lets check if it's legacy.
  4767. // Use CM_LOCATE_DEVNODE_PHANTOM because the DevNode may not be considered alive but
  4768. // exists in the registry.
  4769. //
  4770. if ( CR_SUCCESS == CM_Locate_DevNode(&DevNode, pBuffer, CM_LOCATE_DEVNODE_PHANTOM) )
  4771. {
  4772. //
  4773. // Get the class GUID of this driver
  4774. //
  4775. cchLen = sizeof(szClassGuid);
  4776. Status = CM_Get_DevNode_Registry_Property(
  4777. DevNode, // devnode
  4778. CM_DRP_CLASSGUID, // property to get
  4779. &ulRegDataType, // pointer to REG_* type
  4780. szClassGuid, // return buffer
  4781. &cchLen, // buffer length in bytes
  4782. 0 // flags
  4783. );
  4784. if (Status != CR_SUCCESS)
  4785. {
  4786. #ifdef DEBUG_LOGLOG
  4787. LOG_Write(L"CM_Get_DevNode_Registry_Property failed %#lx for service %ws\n",
  4788. Status, ServiceName);
  4789. #endif
  4790. LocalFree(pBuffer);
  4791. return TRUE;
  4792. }
  4793. //
  4794. // If the single devnode's class is LegacyDriver,
  4795. // this is not a PnP driver
  4796. //
  4797. fRetStatus = (_wcsicmp(szClassGuid, LEGACYDRIVER_STRING) != 0);
  4798. #ifdef DEBUG_LOGLOG
  4799. LOG_Write(L"IsPnPDriver: %ws %ws a PnP driver\n",
  4800. ServiceName, fRetStatus ? L"is" : L"is not");
  4801. #endif
  4802. }
  4803. LocalFree(pBuffer);
  4804. return fRetStatus;
  4805. }
  4806. BOOL
  4807. BackupHives(
  4808. VOID
  4809. )
  4810. /*++
  4811. ===============================================================================
  4812. Routine Description:
  4813. Copy the system hive over into the repair directory. This is required
  4814. if the user has asked us to fixup the critical device database (which
  4815. will change the contents of the system hive).
  4816. Arguments:
  4817. None.
  4818. Return Value:
  4819. TRUE if the operation succeeds, FALSE otherwise.
  4820. ===============================================================================
  4821. --*/
  4822. {
  4823. WCHAR szRepairSystemHive[MAX_PATH];
  4824. WCHAR szRepairSystemHiveBackup[MAX_PATH];
  4825. HKEY hkey;
  4826. LONG lStatus;
  4827. //
  4828. // Get the full pathname of the "system" file in the repair directory.
  4829. //
  4830. if (!GetWindowsDirectory(szRepairSystemHive, MAX_PATH))
  4831. return FALSE;
  4832. StringCchCat (szRepairSystemHive, AS ( szRepairSystemHive ), L"\\repair\\system");
  4833. //
  4834. // Generate the full pathname of the backup copy of the current "system" file.
  4835. StringCchCopy (szRepairSystemHiveBackup, AS ( szRepairSystemHiveBackup ), szRepairSystemHive);
  4836. //
  4837. // ISSUE-2002/02/26-brucegr: This should be szRepairSystemHiveBackup!!!!
  4838. //
  4839. StringCchCat(szRepairSystemHive, AS ( szRepairSystemHive ), L".bak");
  4840. //
  4841. // Open the root of the system hive.
  4842. //
  4843. lStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  4844. L"system",
  4845. REG_OPTION_RESERVED,
  4846. READ_CONTROL,
  4847. &hkey);
  4848. if (lStatus == ERROR_SUCCESS) {
  4849. //
  4850. // First, rename the current "system" file to "system.bak", so that
  4851. // we can restore it if RegSaveKey fails.
  4852. //
  4853. SetFileAttributes(szRepairSystemHiveBackup, FILE_ATTRIBUTE_NORMAL);
  4854. DeleteFile(szRepairSystemHiveBackup);
  4855. SetFileAttributes(szRepairSystemHive, FILE_ATTRIBUTE_NORMAL);
  4856. MoveFile(szRepairSystemHive, szRepairSystemHiveBackup);
  4857. //
  4858. // Save the registry system hive into the "system" file.
  4859. //
  4860. //
  4861. // ISSUE-2002/02/26-brucegr: We need to make sure we have SE_BACKUP_NAME privilege
  4862. //
  4863. lStatus = RegSaveKey(hkey, szRepairSystemHive, NULL);
  4864. if (lStatus == ERROR_SUCCESS) {
  4865. //
  4866. // Now we can safely delete the backup copy.
  4867. //
  4868. DeleteFile(szRepairSystemHiveBackup);
  4869. }
  4870. else {
  4871. //
  4872. // Otherwise we need to restore the system file from the backup.
  4873. //
  4874. MoveFile(szRepairSystemHiveBackup, szRepairSystemHive);
  4875. }
  4876. RegCloseKey(hkey);
  4877. }
  4878. return (lStatus == ERROR_SUCCESS);
  4879. }
  4880. VOID
  4881. FreeSysprepContext(
  4882. IN PVOID SysprepContext
  4883. )
  4884. {
  4885. PSYSPREP_QUEUE_CONTEXT Context = SysprepContext;
  4886. try {
  4887. if(Context->DefaultContext) {
  4888. SetupTermDefaultQueueCallback(Context->DefaultContext);
  4889. }
  4890. free(Context);
  4891. } except(EXCEPTION_EXECUTE_HANDLER) {
  4892. ;
  4893. }
  4894. }
  4895. PVOID
  4896. InitSysprepQueueCallback(
  4897. VOID
  4898. )
  4899. /*++
  4900. ===============================================================================
  4901. Routine Description:
  4902. Initialize the data structure used for the callback that fires when
  4903. we commit the file copy queue.
  4904. Arguments:
  4905. Return Value:
  4906. ===============================================================================
  4907. --*/
  4908. {
  4909. PSYSPREP_QUEUE_CONTEXT SysprepContext;
  4910. SysprepContext = malloc(sizeof(SYSPREP_QUEUE_CONTEXT));
  4911. if(SysprepContext) {
  4912. SysprepContext->DirectoryOnSourceDevice = NULL;
  4913. SysprepContext->DiskDescription = NULL;
  4914. SysprepContext->DiskTag = NULL;
  4915. SysprepContext->DefaultContext = SetupInitDefaultQueueCallbackEx( NULL,
  4916. INVALID_HANDLE_VALUE,
  4917. 0,
  4918. 0,
  4919. NULL );
  4920. }
  4921. return SysprepContext;
  4922. }
  4923. UINT
  4924. SysprepQueueCallback(
  4925. IN PVOID Context,
  4926. IN UINT Notification,
  4927. IN UINT_PTR Param1,
  4928. IN UINT_PTR Param2
  4929. )
  4930. /*++
  4931. ===============================================================================
  4932. Routine Description:
  4933. Callback function for setupapi to use as he's copying files.
  4934. We'll use this to ensure that the files we copy get appended to
  4935. setup.log, which in turn may get used when/if the user ever tries to
  4936. use Windows repair capabilities.
  4937. Arguments:
  4938. Return Value:
  4939. ===============================================================================
  4940. --*/
  4941. {
  4942. UINT Status;
  4943. PSYSPREP_QUEUE_CONTEXT SysprepContext = Context;
  4944. PFILEPATHS FilePaths = (PFILEPATHS)Param1;
  4945. //
  4946. // Make sure that if we get these notification to check Param1.
  4947. //
  4948. switch (Notification) {
  4949. case SPFILENOTIFY_COPYERROR:
  4950. {
  4951. //
  4952. // Copy error happened log and skip this file.
  4953. //
  4954. #ifdef DEBUG_LOGLOG
  4955. LOG_Write(L"SysprepQueueCallback: SPFILENOTIFY_COPYERROR - %s, %s, %s, %s, %s",
  4956. (PWSTR) FilePaths->Source,
  4957. (PWSTR) FilePaths->Target,
  4958. SysprepContext->DirectoryOnSourceDevice,
  4959. SysprepContext->DiskDescription,
  4960. SysprepContext->DiskTag);
  4961. #endif
  4962. return FILEOP_SKIP;
  4963. }
  4964. break;
  4965. case SPFILENOTIFY_NEEDMEDIA:
  4966. {
  4967. //
  4968. // If user specified an OEM driver file and path break and let
  4969. // the DefaultQueueCallback handle it.
  4970. //
  4971. PSOURCE_MEDIA pSourceMedia = (PSOURCE_MEDIA)Param1;
  4972. if (pSourceMedia) {
  4973. #ifdef DEBUG_LOGLOG
  4974. LOG_Write(L"SysprepQueueCallback: SPFILENOTIFY_NEEDMEDIA - %s, %s, %s, %s, %s",
  4975. (PWSTR) pSourceMedia->SourcePath,
  4976. (PWSTR) pSourceMedia->SourceFile,
  4977. (PWSTR) pSourceMedia->Tagfile,
  4978. (PWSTR) pSourceMedia->Description);
  4979. #endif
  4980. if (pSourceMedia->SourcePath && lstrlen(pSourceMedia->SourcePath) &&
  4981. pSourceMedia->SourceFile && lstrlen(pSourceMedia->SourceFile))
  4982. break; // continue if SourcePath and SourceFile is specified
  4983. else
  4984. return FILEOP_SKIP;
  4985. }
  4986. }
  4987. break;
  4988. default:
  4989. break;
  4990. }
  4991. //
  4992. // Use default processing, then check for errors.
  4993. //
  4994. Status = SetupDefaultQueueCallback( SysprepContext->DefaultContext,
  4995. Notification,
  4996. Param1,
  4997. Param2 );
  4998. switch(Notification) {
  4999. case SPFILENOTIFY_ENDCOPY:
  5000. //
  5001. // The copy just finished. Let's log the
  5002. // file.
  5003. //
  5004. LogRepairInfo( (PWSTR) FilePaths->Source,
  5005. (PWSTR) FilePaths->Target,
  5006. SysprepContext->DirectoryOnSourceDevice,
  5007. SysprepContext->DiskDescription,
  5008. SysprepContext->DiskTag );
  5009. break;
  5010. default:
  5011. break;
  5012. }
  5013. return Status;
  5014. }
  5015. BOOL
  5016. ValidateAndChecksumFile(
  5017. IN PCWSTR Filename,
  5018. OUT PBOOLEAN IsNtImage,
  5019. OUT PULONG Checksum,
  5020. OUT PBOOLEAN Valid
  5021. )
  5022. /*++
  5023. ===============================================================================
  5024. Routine Description:
  5025. Calculate a checksum value for a file using the standard
  5026. nt image checksum method. If the file is an nt image, validate
  5027. the image using the partial checksum in the image header. If the
  5028. file is not an nt image, it is simply defined as valid.
  5029. If we encounter an i/o error while checksumming, then the file
  5030. is declared invalid.
  5031. Arguments:
  5032. Filename - supplies full NT path of file to check.
  5033. IsNtImage - Receives flag indicating whether the file is an
  5034. NT image file.
  5035. Checksum - receives 32-bit checksum value.
  5036. Valid - receives flag indicating whether the file is a valid
  5037. image (for nt images) and that we can read the image.
  5038. Return Value:
  5039. BOOL - Returns TRUE if the flie was validated, and in this case,
  5040. IsNtImage, Checksum, and Valid will contain the result of
  5041. the validation.
  5042. This function will return FALSE, if the file could not be
  5043. validated, and in this case, the caller should call GetLastError()
  5044. to find out why this function failed.
  5045. ===============================================================================
  5046. --*/
  5047. {
  5048. DWORD Error;
  5049. PVOID BaseAddress;
  5050. ULONG FileSize;
  5051. HANDLE hFile;
  5052. HANDLE hSection;
  5053. PIMAGE_NT_HEADERS NtHeaders;
  5054. ULONG HeaderSum;
  5055. //
  5056. // Assume not an image and failure.
  5057. //
  5058. *IsNtImage = FALSE;
  5059. *Checksum = 0;
  5060. *Valid = FALSE;
  5061. //
  5062. // Open and map the file for read access.
  5063. //
  5064. Error = pSetupOpenAndMapFileForRead( Filename,
  5065. &FileSize,
  5066. &hFile,
  5067. &hSection,
  5068. &BaseAddress );
  5069. if( Error != ERROR_SUCCESS ) {
  5070. SetLastError( Error );
  5071. return(FALSE);
  5072. }
  5073. if( FileSize == 0 ) {
  5074. *IsNtImage = FALSE;
  5075. *Checksum = 0;
  5076. *Valid = TRUE;
  5077. CloseHandle( hFile );
  5078. return(TRUE);
  5079. }
  5080. try {
  5081. NtHeaders = CheckSumMappedFile(BaseAddress,FileSize,&HeaderSum,Checksum);
  5082. } except(EXCEPTION_EXECUTE_HANDLER) {
  5083. *Checksum = 0;
  5084. NtHeaders = NULL;
  5085. }
  5086. //
  5087. // If the file is not an image and we got this far (as opposed to encountering
  5088. // an i/o error) then the checksum is declared valid. If the file is an image,
  5089. // then its checksum may or may not be valid.
  5090. //
  5091. if(NtHeaders) {
  5092. *IsNtImage = TRUE;
  5093. *Valid = HeaderSum ? (*Checksum == HeaderSum) : TRUE;
  5094. } else {
  5095. *Valid = TRUE;
  5096. }
  5097. pSetupUnmapAndCloseFile( hFile, hSection, BaseAddress );
  5098. return( TRUE );
  5099. }
  5100. VOID
  5101. LogRepairInfo(
  5102. IN PWSTR Source,
  5103. IN PWSTR Target,
  5104. IN PWSTR DirectoryOnSourceDevice,
  5105. IN PWSTR DiskDescription,
  5106. IN PWSTR DiskTag
  5107. )
  5108. /*++
  5109. ===============================================================================
  5110. Routine Description:
  5111. This function will log the fact that a file was installed into the
  5112. machine. This will enable Windows repair functionality to be alerted
  5113. that in case of a repair, this file will need to be restored.
  5114. Arguments:
  5115. Return Value:
  5116. ===============================================================================
  5117. --*/
  5118. {
  5119. WCHAR RepairLog[MAX_PATH];
  5120. BOOLEAN IsNtImage;
  5121. ULONG Checksum;
  5122. BOOLEAN Valid;
  5123. WCHAR Filename[MAX_PATH];
  5124. WCHAR SourceName[MAX_PATH];
  5125. DWORD LastSourceChar, LastTargetChar;
  5126. DWORD LastSourcePeriod, LastTargetPeriod;
  5127. WCHAR Line[MAX_PATH];
  5128. WCHAR tmp[MAX_PATH];
  5129. if (!GetWindowsDirectory( RepairLog, MAX_PATH ))
  5130. return;
  5131. StringCchCat( RepairLog, AS ( RepairLog ), L"\\repair\\setup.log" );
  5132. if( ValidateAndChecksumFile( Target, &IsNtImage, &Checksum, &Valid )) {
  5133. //
  5134. // Strip off drive letter.
  5135. //
  5136. StringCchPrintf(
  5137. Filename,
  5138. AS ( Filename ),
  5139. L"\"%s\"",
  5140. Target+2
  5141. );
  5142. //
  5143. // Convert source name to uncompressed form.
  5144. //
  5145. StringCchCopy ( SourceName, AS ( SourceName ), wcsrchr( Source, (WCHAR)'\\' ) + 1 );
  5146. if(!SourceName [ 0 ] ) {
  5147. return;
  5148. }
  5149. LastSourceChar = wcslen (SourceName) - 1;
  5150. if(SourceName[LastSourceChar] == L'_') {
  5151. LastSourcePeriod = (DWORD)(wcsrchr( SourceName, (WCHAR)'.' ) - SourceName);
  5152. if(LastSourceChar - LastSourcePeriod == 1) {
  5153. //
  5154. // No extension - just truncate the "._"
  5155. //
  5156. SourceName[LastSourceChar-1] = L'\0';
  5157. } else {
  5158. //
  5159. // Make sure the extensions on source and target match.
  5160. // If this fails, we can't log the file copy
  5161. //
  5162. LastTargetChar = wcslen (Target) - 1;
  5163. LastTargetPeriod = (ULONG)(wcsrchr( Target, (WCHAR)'.' ) - Target);
  5164. if( _wcsnicmp(
  5165. SourceName + LastSourcePeriod,
  5166. Target + LastTargetPeriod,
  5167. LastSourceChar - LastSourcePeriod - 1 )) {
  5168. return;
  5169. }
  5170. if(LastTargetChar - LastTargetPeriod < 3) {
  5171. //
  5172. // Short extension - just truncate the "_"
  5173. //
  5174. SourceName[LastSourceChar] = L'\0';
  5175. } else {
  5176. //
  5177. // Need to replace "_" with last character from target
  5178. //
  5179. SourceName[LastSourceChar] = Target[LastTargetChar];
  5180. }
  5181. }
  5182. }
  5183. //
  5184. // Write the line.
  5185. //
  5186. if( (DirectoryOnSourceDevice) &&
  5187. (DiskDescription) &&
  5188. (DiskTag) ) {
  5189. //
  5190. // Treat this as an OEM file.
  5191. //
  5192. StringCchPrintf( Line,
  5193. AS ( Line ),
  5194. L"\"%s\",\"%x\",\"%s\",\"%s\",\"%s\"",
  5195. SourceName,
  5196. Checksum,
  5197. DirectoryOnSourceDevice,
  5198. DiskDescription,
  5199. DiskTag );
  5200. } else {
  5201. //
  5202. // Treat this as an "in the box" file.
  5203. //
  5204. StringCchPrintf( Line,
  5205. AS ( Line ),
  5206. L"\"%s\",\"%x\"",
  5207. SourceName,
  5208. Checksum );
  5209. }
  5210. if (GetPrivateProfileString(L"Files.WinNt",Filename,L"",tmp,sizeof(tmp)/sizeof(tmp[0]),RepairLog)) {
  5211. //
  5212. // there is already an entry for this file present (presumably
  5213. // from textmode phase of setup.) Favor this entry over what we
  5214. // are about to add
  5215. //
  5216. } else {
  5217. WritePrivateProfileString(
  5218. L"Files.WinNt",
  5219. Filename,
  5220. Line,
  5221. RepairLog);
  5222. }
  5223. }
  5224. }
  5225. #ifdef _X86_
  5226. BOOL
  5227. ChangeBootTimeout(
  5228. IN UINT Timeout
  5229. )
  5230. /*++
  5231. ===============================================================================
  5232. Routine Description:
  5233. Changes the boot countdown value in boot.ini.
  5234. Arguments:
  5235. Timeout - supplies new timeout value, in seconds.
  5236. Return Value:
  5237. None.
  5238. ===============================================================================
  5239. --*/
  5240. {
  5241. HFILE hfile;
  5242. ULONG FileSize;
  5243. PUCHAR buf = NULL,p1,p2;
  5244. BOOL b;
  5245. CHAR TimeoutLine[256];
  5246. CHAR szBootIni[] = "?:\\BOOT.INI";
  5247. UINT OldMode;
  5248. WIN32_FIND_DATA findData;
  5249. HANDLE FindHandle;
  5250. WCHAR DriveLetter;
  5251. WCHAR tmpBuffer[MAX_PATH];
  5252. //
  5253. // Generate path to the boot.ini file. This is actually more
  5254. // complicated than one might think. It will almost always
  5255. // be located on c:, but the user may have remapped his drive
  5256. // letters.
  5257. //
  5258. // We'll use a brute-force method here and look for the first
  5259. // instance of boot.ini. We've got two factors going for us
  5260. // here:
  5261. // 1. boot.ini be on the lower-drive letters, so look there
  5262. // first
  5263. // 2. I can't think of a scenario where he would have multiple
  5264. // boot.ini files, so the first one we find is going to be
  5265. // the right one.
  5266. //
  5267. OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  5268. StringCchCopy ( tmpBuffer, AS ( tmpBuffer ), TEXT( "?:\\BOOT.INI" ) );
  5269. for( DriveLetter = 'c'; DriveLetter <= 'z'; DriveLetter++ ) {
  5270. tmpBuffer[0] = DriveLetter;
  5271. //
  5272. // See if he's there.
  5273. //
  5274. //
  5275. // ISSUE-2002/02/26-brucegr: Use GetFileAttributes/GetFileAttributesEx instead of FindFirstFile!
  5276. //
  5277. FindHandle = FindFirstFile( tmpBuffer, &findData );
  5278. if(FindHandle != INVALID_HANDLE_VALUE) {
  5279. //
  5280. // Yep. Close him and break the for-loop.
  5281. //
  5282. FindClose(FindHandle);
  5283. break;
  5284. }
  5285. }
  5286. SetErrorMode(OldMode);
  5287. if( DriveLetter > 'z' ) {
  5288. return FALSE;
  5289. }
  5290. szBootIni[0] = (CHAR)DriveLetter;
  5291. StringCchPrintfA (TimeoutLine,AS ( TimeoutLine ), "timeout=%u\r\n",Timeout);
  5292. //
  5293. // Open and read boot.ini.
  5294. //
  5295. //
  5296. // ISSUE-2002/02/26-brucegr: Why can't we use PrivateProfile APIs?
  5297. //
  5298. b = FALSE;
  5299. hfile = _lopen(szBootIni,OF_READ);
  5300. if(hfile != HFILE_ERROR) {
  5301. FileSize = _llseek(hfile,0,2);
  5302. if(FileSize != (ULONG)(-1)) {
  5303. if((_llseek(hfile,0,0) != -1)
  5304. && (buf = malloc(FileSize+1))
  5305. && (_lread(hfile,buf,FileSize) != (UINT)(-1)))
  5306. {
  5307. buf[FileSize] = 0;
  5308. b = TRUE;
  5309. }
  5310. }
  5311. _lclose(hfile);
  5312. }
  5313. if(!b) {
  5314. if(buf) {
  5315. free(buf);
  5316. }
  5317. return(FALSE);
  5318. }
  5319. if(!(p1 = strstr(buf,"timeout"))) {
  5320. free(buf);
  5321. return(FALSE);
  5322. }
  5323. if(p2 = strchr(p1,'\n')) {
  5324. p2++; // skip NL.
  5325. } else {
  5326. p2 = buf + FileSize;
  5327. }
  5328. SetFileAttributesA(szBootIni,FILE_ATTRIBUTE_NORMAL);
  5329. hfile = _lcreat(szBootIni,0);
  5330. if(hfile == HFILE_ERROR) {
  5331. free(buf);
  5332. return(FALSE);
  5333. }
  5334. //
  5335. // Write:
  5336. //
  5337. // 1) the first part, start=buf, len=p1-buf
  5338. // 2) the timeout line
  5339. // 3) the last part, start=p2, len=buf+FileSize-p2
  5340. //
  5341. b = ((_lwrite(hfile,buf ,p1-buf ) != (UINT)(-1))
  5342. && (_lwrite(hfile,TimeoutLine,strlen(TimeoutLine)) != (UINT)(-1))
  5343. && (_lwrite(hfile,p2 ,buf+FileSize-p2 ) != (UINT)(-1)));
  5344. _lclose(hfile);
  5345. free(buf);
  5346. //
  5347. // Make boot.ini archive, read only, and system.
  5348. //
  5349. SetFileAttributesA(
  5350. szBootIni,
  5351. FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN
  5352. );
  5353. return(b);
  5354. }
  5355. #else
  5356. BOOL
  5357. ChangeBootTimeout(
  5358. IN UINT Timeout
  5359. )
  5360. /*++
  5361. ===============================================================================
  5362. Routine Description:
  5363. Changes the boot timeout value in NVRAM.
  5364. Arguments:
  5365. Timeout - supplies new timeout value, in seconds.
  5366. Return Value:
  5367. None.
  5368. ===============================================================================
  5369. --*/
  5370. {
  5371. NTSTATUS Status;
  5372. BOOT_OPTIONS BootOptions;
  5373. BootOptions.Version = BOOT_OPTIONS_VERSION;
  5374. BootOptions.Length = sizeof(BootOptions);
  5375. BootOptions.Timeout = Timeout;
  5376. pSetupEnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE);
  5377. Status = NtSetBootOptions(&BootOptions, BOOT_OPTIONS_FIELD_TIMEOUT);
  5378. return (NT_SUCCESS(Status));
  5379. }
  5380. #endif
  5381. // Disable System Restore
  5382. //
  5383. void DisableSR()
  5384. {
  5385. HINSTANCE hInst = LoadLibrary(FILE_SRCLIENT_DLL);
  5386. if (hInst) {
  5387. FARPROC fnProc;
  5388. if ( fnProc = GetProcAddress(hInst, "DisableSR") ) {
  5389. fnProc(NULL);
  5390. }
  5391. FreeLibrary(hInst);
  5392. }
  5393. }
  5394. // Enable System Restore
  5395. //
  5396. void EnableSR()
  5397. {
  5398. HINSTANCE hInst = LoadLibrary(FILE_SRCLIENT_DLL);
  5399. if (hInst) {
  5400. FARPROC fnProc;
  5401. if ( fnProc = GetProcAddress(hInst, "EnableSR") ) {
  5402. fnProc(NULL);
  5403. }
  5404. FreeLibrary(hInst);
  5405. }
  5406. }
  5407. LPTSTR OPKAddPathN(LPTSTR lpPath, LPCTSTR lpName, DWORD cbPath)
  5408. {
  5409. LPTSTR lpTemp = lpPath;
  5410. // Validate the parameters passed in.
  5411. //
  5412. if ( ( lpPath == NULL ) ||
  5413. ( lpName == NULL ) )
  5414. {
  5415. return NULL;
  5416. }
  5417. // Find the end of the path.
  5418. //
  5419. while ( *lpTemp )
  5420. {
  5421. lpTemp = CharNext(lpTemp);
  5422. if ( cbPath )
  5423. {
  5424. cbPath--;
  5425. }
  5426. }
  5427. // If no trailing backslash on the path then add one.
  5428. //
  5429. if ( ( lpTemp > lpPath ) &&
  5430. ( *CharPrev(lpPath, lpTemp) != CHR_BACKSLASH ) )
  5431. {
  5432. // Make sure there is room in the path buffer to
  5433. // add the backslash and the null terminator.
  5434. //
  5435. if ( cbPath < 2 )
  5436. {
  5437. return NULL;
  5438. }
  5439. *lpTemp = CHR_BACKSLASH;
  5440. lpTemp = CharNext(lpTemp);
  5441. cbPath--;
  5442. }
  5443. else
  5444. {
  5445. // Make sure there is at least room for the null
  5446. // terminator.
  5447. //
  5448. if ( cbPath < 1 )
  5449. {
  5450. return NULL;
  5451. }
  5452. }
  5453. // Make sure there is no preceeding spaces or backslashes
  5454. // on the name to add.
  5455. //
  5456. while ( ( *lpName == CHR_SPACE ) ||
  5457. ( *lpName == CHR_BACKSLASH ) )
  5458. {
  5459. lpName = CharNext(lpName);
  5460. }
  5461. // Add the new name to existing path.
  5462. //
  5463. lstrcpyn(lpTemp, lpName, cbPath);
  5464. // Trim trailing spaces from result.
  5465. //
  5466. while ( ( lpTemp > lpPath ) &&
  5467. ( *(lpTemp = CharPrev(lpPath, lpTemp)) == CHR_SPACE ) )
  5468. {
  5469. *lpTemp = NULLCHR;
  5470. }
  5471. return lpPath;
  5472. }