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.

1742 lines
45 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. buildinf.c
  5. Abstract:
  6. The functions in this file are a set of APIs to build an INF,
  7. merge existing INFs, and write the INF to disk.
  8. Author:
  9. Jim Schmidt (jimschm) 20-Sept-1996
  10. Revision History:
  11. marcw 30-Jun-1999 pWriteDelAndMoveFiles is now done in a seperate function so that
  12. it can be on the progress bar and not cause an uncomfortable delay
  13. after the user passes the progress bar.
  14. marcw 09-Feb-1999 Filter out quotes in values -- Not supported by inf parser
  15. in NTLDR.
  16. ovidiut 03-Feb-1999 Ensure that directory collision moves are done first
  17. in mov/del files.
  18. jimschm 23-Sep-1998 Updated for new fileops
  19. marcw 23-Jul-1998 Removed lots of intermediary conversions (MB->W->MB->W)
  20. in writing the win9xmov and win9xdel txt files.
  21. (Fixes problems with certain characters that were messed
  22. up in the translations.)
  23. marcw 10-Jun-1998 Added support for multiple keys with the same name.
  24. marcw 08-Apr-1998 Fixed problems associated with some conversions.
  25. marcw 08-Apr-1998 All values are automatically quoted now..Matches winnt32 behavior.
  26. calinn 25-Mar-1998 fixed unicode header writing error
  27. marcw 16-Dec-1997 pWriteDelAndMoveFiles now writes to a unicode file instead of winnt.sif.
  28. mikeco 11-Nov-1997 DBCS issues.
  29. jimschm 21-Jul-1997 pWriteDelAndMoveFiles (see fileops.c in memdb)
  30. mikeco 10-Jun-1997 DBCS issues.
  31. marcw 09-Apr-1997 Performance tuned memdb usage.
  32. marcw 01-Apr-1997 Re-engineered this code..Now memdb based. shorter.
  33. also added smart-merge for migration DLLs.
  34. jimschm 03-Jan-1997 Re-enabled memory-based INF building code
  35. --*/
  36. #include "pch.h"
  37. #define MAX_LINE_LENGTH 512
  38. #define MIN_SPLIT_COL 490
  39. #define MAX_OEM_PATH (MAX_PATH*2)
  40. #define WACKREPLACECHAR 2
  41. #define REPLACE_WACKS TRUE
  42. #define RESTORE_WACKS FALSE
  43. #define SECTION_NAME_SIZE 8192
  44. //
  45. // These levels refer to the MEMDB_ENUM structure component nCurPos.
  46. // They were determined empirically in test to be the correct values
  47. //
  48. #define SECTIONLEVEL 3
  49. #define KEYLEVEL 4
  50. #define VALUELEVEL 5
  51. #define BUILDINF_UNIQUEKEY_PREFIX TEXT("~BUILDINF~UNIQUE~")
  52. #define BUILDINF_NULLKEY_PREFIX TEXT("~BUILDINF~NULLKEY~")
  53. #define BUILDINF_UNIQUEKEY_PREFIX_SIZE 17
  54. TCHAR g_DblQuote[] = TEXT("\"");
  55. TCHAR g_Comments[] = TEXT(";\r\n; Setup-generated migration INF file\r\n;\r\n\r\n");
  56. extern HINF g_Win95UpgInf;
  57. BOOL g_AnswerFileAlreadyWritten;
  58. BOOL pWriteStrToFile (HANDLE File, PCTSTR String, BOOL ConvertToUnicode);
  59. BOOL pWriteSectionString (HANDLE File, PCTSTR szString, BOOL ConvertToUnicode, BOOL Quote);
  60. BOOL pWriteSections (HANDLE File,BOOL ConvertToUnicode);
  61. BOOL pWriteDelAndMoveFiles (VOID);
  62. BOOL
  63. WINAPI
  64. BuildInf_Entry(IN HINSTANCE hinstDLL,
  65. IN DWORD dwReason,
  66. IN LPVOID lpv)
  67. /*++
  68. Routine Description:
  69. DllMain is called after the C runtime is initialized, and its purpose
  70. is to initialize the globals for this process. For this DLL, it
  71. initializes g_hInst and g_hHeap.
  72. Arguments:
  73. hinstDLL - (OS-supplied) Instance handle for the DLL
  74. dwReason - (OS-supplied) Type of initialization or termination
  75. lpv - (OS-supplied) Unused
  76. Return Value:
  77. TRUE because DLL always initializes properly.
  78. --*/
  79. {
  80. switch (dwReason) {
  81. case DLL_PROCESS_ATTACH:
  82. g_AnswerFileAlreadyWritten = FALSE;
  83. break;
  84. case DLL_PROCESS_DETACH:
  85. break;
  86. }
  87. UNREFERENCED_PARAMETER(hinstDLL);
  88. UNREFERENCED_PARAMETER(lpv);
  89. return TRUE;
  90. }
  91. VOID
  92. pHandleWacks(
  93. IN OUT PSTR String,
  94. IN BOOL Operation
  95. )
  96. /*++
  97. Routine Description:
  98. pHandleWack is a simple helper function who's purpose is to remove/restore
  99. the wacks in a string. This is necessary because there are cases where we
  100. want to have wacks in keys and values without invoking the memdb
  101. functionality of using those wacks to indicate new tree nodes.
  102. Arguments:
  103. String - The string to perform the replacement on.
  104. Operation - set to REPLACE_WACKS if the function should replace the wacks
  105. in a string and RESTORE_WACKS if they should restore them.
  106. Return Value:
  107. None.
  108. --*/
  109. {
  110. TCHAR findChar;
  111. TCHAR replaceChar;
  112. PSTR curChar;
  113. //
  114. // Set find and replace chars based on operation.
  115. //
  116. if (Operation == REPLACE_WACKS) {
  117. findChar = TEXT('\\');
  118. replaceChar = TEXT(WACKREPLACECHAR);
  119. }
  120. else {
  121. findChar = TEXT(WACKREPLACECHAR);
  122. replaceChar = TEXT('\\');
  123. }
  124. if ((curChar = _tcschr(String,findChar)) != NULL) {
  125. do {
  126. *curChar = replaceChar;
  127. } while ((curChar = _tcschr(curChar,findChar)) != NULL);
  128. }
  129. }
  130. BOOL
  131. WriteInfToDisk (
  132. IN PCTSTR OutputFile
  133. )
  134. /*++
  135. Routine Description:
  136. WriteInfToDisk outputs the memory based INF file that has been built to disk.
  137. Arguments:
  138. OutputFile - A full path to the output INF file.
  139. Return Value:
  140. Returns TRUE if the INF file was successfully written, or FALSE
  141. if an I/O error was encountered. Call GetLastError upon failure
  142. for a Win32 error code.
  143. --*/
  144. {
  145. HANDLE hFile;
  146. MEMDB_ENUM e;
  147. BOOL b = FALSE;
  148. TCHAR category[MEMDB_MAX];
  149. if (g_AnswerFileAlreadyWritten) {
  150. LOG ((LOG_ERROR,"Answer file has already been written to disk."));
  151. return FALSE;
  152. }
  153. //
  154. // set flag indicating that answerfile is closed for business.
  155. //
  156. g_AnswerFileAlreadyWritten = TRUE;
  157. //
  158. // Write an INF
  159. //
  160. hFile = CreateFile (OutputFile,
  161. GENERIC_WRITE,
  162. 0,
  163. NULL,
  164. CREATE_ALWAYS,
  165. FILE_ATTRIBUTE_NORMAL,
  166. NULL);
  167. if (hFile != INVALID_HANDLE_VALUE) {
  168. __try {
  169. #ifdef UNICODE
  170. //
  171. // Write UNICODE signature
  172. //
  173. if (!pWriteStrToFile (hFile, (LPCTSTR) "\xff\xfe\0",FALSE))
  174. __leave;
  175. #endif
  176. //
  177. // Write comments
  178. //
  179. if (!pWriteStrToFile (hFile, g_Comments,FALSE))
  180. __leave;
  181. //
  182. // Write out sections.
  183. //
  184. if (!pWriteSections (hFile,FALSE))
  185. __leave;
  186. //
  187. // Delete Answer file parts of memdb.
  188. //
  189. MemDbDeleteTree(MEMDB_CATEGORY_UNATTENDRESTRICTRIONS);
  190. MemDbDeleteTree(MEMDB_CATEGORY_AF_VALUES);
  191. if (MemDbEnumItems(&e,MEMDB_CATEGORY_AF_SECTIONS)) {
  192. do {
  193. wsprintf(category,S_ANSWERFILE_SECTIONMASK,e.szName);
  194. MemDbDeleteTree(category);
  195. } while (MemDbEnumNextValue(&e));
  196. }
  197. MemDbDeleteTree(MEMDB_CATEGORY_AF_SECTIONS);
  198. b = TRUE; // indicate success
  199. }
  200. __finally {
  201. CloseHandle (hFile);
  202. }
  203. }
  204. return b;
  205. }
  206. BOOL
  207. pWriteStrToFile (
  208. IN HANDLE File,
  209. IN LPCTSTR String,
  210. IN BOOL ConvertToUnicode
  211. )
  212. /*++
  213. Routine Description:
  214. pWriteStrToFile takes a zero-terminated string and writes it
  215. to the open file indicated by File. Optionally, the function can
  216. convert its string argument into a unicode string.
  217. Arguments:
  218. File - A handle to an open file with write permission.
  219. String - A pointer to the string to write. The string is written
  220. to the file, but the zero terminator is not written to
  221. the file.
  222. ConvertToUnicode - True if you wish to convert to unicode, false otherwise.
  223. Return Value:
  224. TRUE if the write succeeds, or FALSE if an I/O error occurred.
  225. GetLastError() can be used to get more error information.
  226. --*/
  227. {
  228. DWORD bytesWritten = 0;
  229. DWORD size = 0;
  230. BOOL rFlag = TRUE;
  231. PBYTE data = NULL;
  232. WCHAR unicodeString[MEMDB_MAX];
  233. if (ConvertToUnicode) {
  234. size = _mbslen(String);
  235. MultiByteToWideChar (
  236. CP_OEMCP,
  237. 0,
  238. String,
  239. -1,
  240. unicodeString,
  241. MEMDB_MAX
  242. );
  243. data = (PBYTE) unicodeString;
  244. size = ByteCountW(unicodeString);
  245. }
  246. else {
  247. data = (PBYTE) String;
  248. size = ByteCount(String);
  249. }
  250. rFlag = WriteFile (File, data, size, &bytesWritten, NULL);
  251. if (bytesWritten != size) {
  252. rFlag = FALSE;
  253. }
  254. return rFlag;
  255. }
  256. BOOL
  257. pIsDoubledChar (
  258. CHARTYPE Char
  259. )
  260. /*++
  261. Routine Description:
  262. This simple function simply returns true if the character in question is a
  263. double quote or a percent sign.
  264. Arguments:
  265. Char - The character in question.
  266. Return Value:
  267. --*/
  268. {
  269. return Char == TEXT('\"') || Char == TEXT('%');
  270. }
  271. BOOL
  272. pWriteSectionString (
  273. HANDLE File,
  274. LPCTSTR String,
  275. BOOL ConvertToUnicode,
  276. BOOL Quote
  277. )
  278. /*++
  279. Routine Description:
  280. pWriteSectionString writes a line in a section to disk.
  281. If the string has a character that requires quote mode, the
  282. string is surrounded by quotes, and all literal quotes and
  283. percent signs are doubled-up.
  284. Arguments:
  285. File - A handle to an open file with write permission.
  286. String - A pointer to the string holding the section line.
  287. ConvertToUnicode - TRUE if the line in question should be
  288. converted to UNICODE, FALSE otherwise.
  289. Quote - Automatically Quotes the string if set to TRUE.
  290. Return Value:
  291. TRUE if the write succeeds, or FALSE if an I/O error occurred.
  292. GetLastError() can be used to get more error information.
  293. --*/
  294. {
  295. TCHAR buffer[256];
  296. int i;
  297. int doubles;
  298. PTSTR dest;
  299. CHARTYPE tc;
  300. //
  301. // Initialize variables first time through the loop.
  302. //
  303. doubles = 0;
  304. dest = buffer;
  305. while (*String) {
  306. //
  307. // Add initial quote
  308. //
  309. if (Quote) {
  310. StringCopy (dest, g_DblQuote);
  311. dest = _tcsinc (dest);
  312. }
  313. //
  314. // Copy as many characters as can fit on the line
  315. //
  316. tc = 0;
  317. for (i = 0 ; *String && i + doubles < MAX_LINE_LENGTH ; i++) {
  318. if (i + doubles > MIN_SPLIT_COL)
  319. tc = _tcsnextc (String);
  320. //
  321. // Double up certain characters when in quote mode
  322. //
  323. if (Quote && pIsDoubledChar (_tcsnextc (String))) {
  324. doubles++;
  325. //
  326. // Copy a UNICODE/MBCS char, leaving src in place
  327. //
  328. _tcsncpy (dest, String, (_tcsinc(String)-String));
  329. dest = _tcsinc (dest);
  330. }
  331. //
  332. // Copy a UNICODE/MBCS char, advancing src/tgt
  333. //
  334. _tcsncpy (dest, String, (_tcsinc(String)-String));
  335. dest = _tcsinc (dest);
  336. String = _tcsinc (String);
  337. //
  338. // Test split condition (NOTE: tc == 0 when i + doubles <= MIN_SPLIT_COL)
  339. //
  340. if (tc && (tc == TEXT(',') || tc == TEXT(' ') || tc == TEXT('\\'))) {
  341. break;
  342. }
  343. }
  344. //
  345. // Add a trailing quote
  346. //
  347. if (Quote) {
  348. StringCopy (dest, g_DblQuote);
  349. dest = _tcsinc (dest);
  350. }
  351. //
  352. // Add trailing wack if line is split
  353. //
  354. if (*String) {
  355. *dest = TEXT('\\');
  356. dest = _tcsinc (dest);
  357. }
  358. *dest = 0;
  359. //
  360. // Write line to disk
  361. //
  362. if (!pWriteStrToFile (File, buffer,ConvertToUnicode)) {
  363. DEBUGMSG ((DBG_ERROR, "pWriteSectionString: pWriteStrToFile failed"));
  364. return FALSE;
  365. }
  366. //
  367. // Reset for next time through the while loop
  368. //
  369. doubles = 0;
  370. dest = buffer;
  371. }
  372. return TRUE;
  373. }
  374. BOOL
  375. pWriteSections (
  376. IN HANDLE File,
  377. IN BOOL ConvertToUnicode
  378. )
  379. /*++
  380. Routine Description:
  381. pWriteSections enumerates all of the sections stored by previous calls to
  382. WriteInfKey and WriteInfKeyEx, and writes these sections to the open file
  383. specified by File.
  384. Arguments:
  385. File - An open file.
  386. ConvertToUnicode - Set to TRUE if the lines should be converted to UNICODE, FALSE otherwise.
  387. Return Value:
  388. TRUE if the write succeeds, or FALSE if an I/O error occurred.
  389. GetLastError() can be used to get more error information.
  390. --*/
  391. {
  392. MEMDB_ENUM e;
  393. TCHAR buffer[MAX_TCHAR_PATH];
  394. PTSTR sectionName;
  395. PTSTR p;
  396. BOOL firstTime = FALSE;
  397. BOOL nullKey = FALSE;
  398. //
  399. // enumerate the memdb sections and keys section.
  400. //
  401. if (MemDbEnumFirstValue (&e, MEMDB_CATEGORY_AF_SECTIONS, MEMDB_ALL_SUBLEVELS, MEMDB_ALL_BUT_PROXY)) {
  402. do {
  403. switch(e.PosCount) {
  404. case SECTIONLEVEL:
  405. //
  406. // Write the Section Name to the File, surrounded by Brackets.
  407. //
  408. if (!pWriteStrToFile (File, TEXT("\r\n\r\n"),ConvertToUnicode)) {
  409. DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed"));
  410. return FALSE;
  411. }
  412. if (!pWriteStrToFile (File, TEXT("["),ConvertToUnicode)) {
  413. DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed"));
  414. return FALSE;
  415. }
  416. if ((sectionName = _tcschr(e.szName,TEXT('\\'))) == NULL || (sectionName = _tcsinc(sectionName)) == NULL) {
  417. LOG ((LOG_ERROR,"Invalid section name for answer file."));
  418. return FALSE;
  419. }
  420. //
  421. // put wacks back in if necessary.
  422. //
  423. pHandleWacks(sectionName,RESTORE_WACKS);
  424. if (!pWriteStrToFile (File, sectionName,ConvertToUnicode)) {
  425. DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed"));
  426. return FALSE;
  427. }
  428. if (!pWriteStrToFile (File, TEXT("]"),ConvertToUnicode)) {
  429. DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed"));
  430. return FALSE;
  431. }
  432. break;
  433. case KEYLEVEL:
  434. //
  435. // Key name.
  436. //
  437. if (!pWriteStrToFile (File, TEXT("\r\n"),ConvertToUnicode)) {
  438. DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed"));
  439. return FALSE;
  440. }
  441. if (!MemDbBuildKeyFromOffset(e.dwValue,buffer,1,NULL)) {
  442. DEBUGMSG((DBG_ERROR,"pWriteSections: MemDb failure!"));
  443. return FALSE;
  444. }
  445. //
  446. // Skip NULL keys
  447. //
  448. if (!StringMatch (buffer, BUILDINF_NULLKEY_PREFIX)) {
  449. //
  450. // Strip out "uniqueified" prefix, if present.
  451. //
  452. if (!StringCompareTcharCount(BUILDINF_UNIQUEKEY_PREFIX,buffer,BUILDINF_UNIQUEKEY_PREFIX_SIZE)) {
  453. p = buffer + BUILDINF_UNIQUEKEY_PREFIX_SIZE + 4;
  454. }
  455. else {
  456. p = buffer;
  457. }
  458. if (!pWriteSectionString(File,p,ConvertToUnicode,FALSE)) {
  459. DEBUGMSG((DBG_ERROR,"WriteSections: pWriteStrToFile failed"));
  460. return FALSE;
  461. }
  462. nullKey = FALSE;
  463. } else {
  464. nullKey = TRUE;
  465. }
  466. firstTime = TRUE;
  467. break;
  468. case VALUELEVEL:
  469. //
  470. // Value.
  471. //
  472. if (MemDbBuildKeyFromOffset(e.dwValue,buffer,1,NULL)) {
  473. if (!nullKey) {
  474. if (!pWriteStrToFile(File, (firstTime ? TEXT("=") : TEXT(",")),ConvertToUnicode)) {
  475. DEBUGMSG((DBG_ERROR,"pWriteSections: pWriteStrToFile failed"));
  476. return FALSE;
  477. }
  478. }
  479. firstTime = FALSE;
  480. if (!pWriteSectionString(File, buffer,ConvertToUnicode,TRUE)) {
  481. DEBUGMSG((DBG_ERROR,"pWriteSections: pWriteSectionString failed."));
  482. return FALSE;
  483. }
  484. }
  485. break;
  486. default:
  487. //
  488. // Not a bug if it gets to this case, we just don't care.
  489. //
  490. break;
  491. }
  492. } while (MemDbEnumNextValue(&e));
  493. }
  494. pWriteStrToFile (File, TEXT("\r\n\r\n"),ConvertToUnicode);
  495. return TRUE;
  496. }
  497. BOOL
  498. pRestrictedKey (
  499. IN PCTSTR Section,
  500. IN PCTSTR Key
  501. )
  502. /*++
  503. Routine Description:
  504. pRestrictedKey returns TRUE if a key is restricted in win95upg.inf and FALSE
  505. otherwise. Certain keys are restricted to prevent migration dlls from
  506. overwriting important upgrade information.
  507. Arguments:
  508. Section - A string containing the section to be written.
  509. Key - A String containing the Key to be written.
  510. Return Value:
  511. TRUE if the key is restricted, and FALSE otherwise.
  512. --*/
  513. {
  514. TCHAR memDbKey[MEMDB_MAX+1];
  515. MemDbBuildKey(memDbKey,MEMDB_CATEGORY_UNATTENDRESTRICTRIONS,Section,Key,NULL);
  516. return MemDbGetPatternValue(memDbKey,NULL);
  517. }
  518. BOOL
  519. pDoMerge (
  520. IN PCTSTR InputFile,
  521. IN BOOL Restricted
  522. )
  523. {
  524. /*++
  525. Routine Description:
  526. pDoMerge is responsible for merging in information from a file into the
  527. currently maintained answer file in memdb. The data being added from file
  528. will take precidence over what has already been written. Therefore, if a
  529. section/key pair is added that has previously been added, the new
  530. section/key will take precedence. The one caveat is that if Restricted is
  531. set to true, only keys that are not restricted in win95upg.inf may be
  532. merged in.
  533. Arguments:
  534. InputFile - The name of the file containing answerfile data to be merged
  535. into memdb.
  536. Restricted - TRUE if Answer File Restrictions should be applied to the
  537. file, FALSE otherwise.
  538. Return Value:
  539. TRUE if the merge was successful, FALSE otherwise.
  540. --*/
  541. HINF hInf = INVALID_HANDLE_VALUE;
  542. PTSTR sectionNames;
  543. PTSTR stringKey;
  544. PTSTR currentField;
  545. PTSTR workBuffer;
  546. POOLHANDLE pool = NULL;
  547. PTSTR firstValue;
  548. DWORD fieldCount;
  549. DWORD valueSectionIndex = 0;
  550. DWORD index;
  551. PTSTR currentSection;
  552. INFCONTEXT ic;
  553. BOOL b = FALSE;
  554. BOOL result = FALSE;
  555. __try {
  556. //
  557. // Make sure the answer file has not already been written to the disk.
  558. //
  559. if (g_AnswerFileAlreadyWritten) {
  560. LOG ((LOG_ERROR,"Answer file has already been written to disk."));
  561. SetLastError (ERROR_SUCCESS);
  562. __leave;
  563. }
  564. pool = PoolMemInitNamedPool ("SIF Merge");
  565. if (!pool) {
  566. __leave;
  567. }
  568. sectionNames = (PTSTR) PoolMemGetMemory (pool, SECTION_NAME_SIZE * sizeof (TCHAR));
  569. stringKey = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR));
  570. currentField = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR));
  571. workBuffer = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR));
  572. if (!sectionNames || !stringKey || !currentField || !workBuffer) {
  573. __leave;
  574. }
  575. GetPrivateProfileSectionNames (sectionNames, SECTION_NAME_SIZE, InputFile);
  576. hInf = InfOpenInfFile (InputFile);
  577. if (hInf == INVALID_HANDLE_VALUE) {
  578. LOG ((LOG_ERROR, "Can't open %s for merge", InputFile));
  579. __leave;
  580. }
  581. //
  582. // Loop through each section name, get each line and add it
  583. //
  584. for (currentSection = sectionNames ; *currentSection ; currentSection = GetEndOfString (currentSection) + 1) {
  585. //
  586. // Get each line in the section and add it to memory
  587. //
  588. if (SetupFindFirstLine (hInf, currentSection, NULL, &ic)) {
  589. do {
  590. fieldCount = SetupGetFieldCount(&ic);
  591. //
  592. // Get the StringKey
  593. //
  594. SetupGetStringField(&ic,0,stringKey,MEMDB_MAX,NULL);
  595. b = SetupGetStringField(&ic,1,currentField,MEMDB_MAX,NULL);
  596. firstValue = currentField;
  597. //
  598. // If key and value are the same, we might not have an equals.
  599. // Due to setupapi limitations, we have to use another INF
  600. // parser to determine this case.
  601. //
  602. if (StringMatch (stringKey, firstValue)) {
  603. if (!GetPrivateProfileString (
  604. currentSection,
  605. firstValue,
  606. TEXT(""),
  607. workBuffer,
  608. MEMDB_MAX,
  609. InputFile
  610. )) {
  611. valueSectionIndex = WriteInfKey (currentSection, NULL, currentField);
  612. DEBUGMSG_IF ((!valueSectionIndex, DBG_ERROR, "AnswerFile: WriteInfKey returned 0"));
  613. continue;
  614. }
  615. }
  616. if (!Restricted || !pRestrictedKey(currentSection,stringKey)) {
  617. //
  618. // Write the first line first, remember the valuesectionid from it.
  619. //
  620. if (b) {
  621. valueSectionIndex = WriteInfKey(currentSection,stringKey,firstValue);
  622. DEBUGMSG_IF((!valueSectionIndex,DBG_ERROR,"AnswerFile: WriteInfKey returned 0..."));
  623. }
  624. for (index = 2;index <= fieldCount;index++) {
  625. b = SetupGetStringField(&ic,index,currentField,MEMDB_MAX,NULL);
  626. if (b) {
  627. WriteInfKeyEx(currentSection,stringKey,currentField,valueSectionIndex,FALSE);
  628. }
  629. }
  630. //
  631. // Kludge to make sure that we respect the JoinWorkgroup/JoinDomain specified in the unattend
  632. // file over what we may have already written.
  633. //
  634. if (StringIMatch (currentSection, S_PAGE_IDENTIFICATION) && StringIMatch (stringKey, S_JOINDOMAIN)) {
  635. WriteInfKey (currentSection, S_JOINWORKGROUP, TEXT(""));
  636. }
  637. else if (StringIMatch (currentSection, S_PAGE_IDENTIFICATION) && StringIMatch (stringKey, S_JOINWORKGROUP)) {
  638. WriteInfKey (currentSection, S_JOINDOMAIN, TEXT(""));
  639. }
  640. }
  641. ELSE_DEBUGMSG((DBG_VERBOSE,"AnswerFile: Not merging restricted key %s",stringKey));
  642. } while (b && SetupFindNextLine (&ic, &ic));
  643. if (!b) {
  644. LOG ((
  645. LOG_ERROR,
  646. "An error occured merging the section [%s] from %s. "
  647. "Some settings from this section may have been lost.",
  648. currentSection,
  649. InputFile
  650. ));
  651. __leave;
  652. }
  653. }
  654. }
  655. result = TRUE;
  656. }
  657. __finally {
  658. PushError();
  659. if (hInf != INVALID_HANDLE_VALUE) {
  660. InfCloseInfFile (hInf);
  661. }
  662. if (pool) {
  663. PoolMemDestroyPool (pool);
  664. }
  665. PopError();
  666. }
  667. return result;
  668. }
  669. BOOL
  670. MergeMigrationDllInf (
  671. IN PCTSTR InputFile
  672. )
  673. /*++
  674. Routine Description:
  675. MergeMigrationDllInf is responsible for merging information from a
  676. migration dll supplied answer file into the memdb based answer file being
  677. maintained. This is done by first ensuring that the answer file
  678. restrictions have been initialized and then merging in the data using those
  679. restrictions.
  680. Arguments:
  681. InputFile - A String containing the name of the file to merge into the
  682. answer file being maintained.
  683. Return Value:
  684. TRUE if the merge was successful, FALSE otherwise.
  685. --*/
  686. {
  687. static BOOL initialized = FALSE;
  688. if (!initialized) {
  689. INFCONTEXT context;
  690. TCHAR section[MAX_TCHAR_PATH];
  691. TCHAR key[MAX_TCHAR_PATH];
  692. //
  693. // Add the contents of the unattended constraints section to
  694. // MemDb.
  695. //
  696. //
  697. // Each line is of the form <section>=<pattern> where <section> need
  698. // not be unique and <pattern> is a section key pattern that may contain
  699. // wildcard characters.
  700. //
  701. if (SetupFindFirstLine (g_Win95UpgInf, MEMDB_CATEGORY_UNATTENDRESTRICTRIONS, NULL, &context)) {
  702. do {
  703. if (SetupGetStringField (&context, 0, section, MAX_TCHAR_PATH, NULL) &&
  704. SetupGetStringField (&context, 1, key, MAX_TCHAR_PATH, NULL)) {
  705. //
  706. // Add to Memdb.
  707. //
  708. MemDbSetValueEx(
  709. MEMDB_CATEGORY_UNATTENDRESTRICTRIONS,
  710. section,
  711. key,
  712. NULL,
  713. 0,
  714. NULL
  715. );
  716. }
  717. ELSE_DEBUGMSG((DBG_WARNING,"BuildInf: SetupGetStringField failed."));
  718. } while (SetupFindNextLine (&context, &context));
  719. }
  720. ELSE_DEBUGMSG((DBG_WARNING,"BuildInf: No %s section in w95upg.inf.",MEMDB_CATEGORY_UNATTENDRESTRICTRIONS));
  721. initialized = TRUE;
  722. }
  723. //
  724. // At this point, all restrictions are accounted for. Merge the file with
  725. // the memory based structure.
  726. //
  727. return pDoMerge(InputFile,TRUE);
  728. }
  729. BOOL
  730. MergeInf (
  731. IN PCTSTR InputFile
  732. )
  733. /*++
  734. Routine Description:
  735. MergeInf opens an INF file using the Setup APIs, enumerates all the
  736. sections and merges the strings in the INF file with what is in
  737. memory
  738. .
  739. Arguments:
  740. InputFile - The path to the INF file. It is treated as an old-style
  741. INF.
  742. Return Value:
  743. TRUE if the file was read and merged successfully, or FALSE if an error
  744. occurred. Call GetLastError to get a failure error code.
  745. --*/
  746. {
  747. return pDoMerge(InputFile,FALSE);
  748. }
  749. DWORD
  750. pWriteInfKey (
  751. LPCTSTR Section,
  752. LPCTSTR Key, OPTIONAL
  753. LPCTSTR Value, OPTIONAL
  754. DWORD ValueSectionId,
  755. BOOL EnsureKeyIsUnique
  756. )
  757. /*++
  758. Routine Description:
  759. pWriteInfKey is responsible for adding an inf key to the memdb data being
  760. maintained for the answer file.
  761. Arguments:
  762. Section - A string containing the section to add the information
  763. to.
  764. Key - A string containing the key to add information under.
  765. If not specified, then Value represents the complete
  766. line text.
  767. Value - A String containing the value to add under the
  768. section/key. If not specified, Key will be removed.
  769. ValueSectionId - A DWORD offset that is used to add multiple values to
  770. the same key. 0 indicates no offset, and causes the old
  771. key to be overwritten, if it exists or a new key to be
  772. created, if it does not. pWriteInfKey returns this
  773. offset when successful.
  774. EnsureKeyIsUnique - TRUE if the key should be unique in memory, FALSE
  775. otherwise. This is used to create multiple keys in the
  776. answer file under the same section with the same name.
  777. Return Value:
  778. A valid offset if the call was successful, FALSE otherwise.
  779. ++*/
  780. {
  781. BOOL b;
  782. TCHAR aKey[MEMDB_MAX];
  783. TCHAR keySection[MEMDB_MAX];
  784. TCHAR massagedSection[MEMDB_MAX];
  785. DWORD testValue;
  786. TCHAR valueId[20];
  787. TCHAR sequence[20];
  788. static DWORD idSeed = 1;
  789. static DWORD seqSeed = 1;
  790. DWORD rSeed = 1;
  791. DWORD sequenceValue;
  792. BOOL keyFound;
  793. DWORD valueOffset;
  794. DWORD keyOffset;
  795. static DWORD uniqueNumber = 1;
  796. TCHAR uniqueKey[MEMDB_MAX];
  797. PTSTR keyPtr = NULL;
  798. //
  799. // Guard against rouge parameters.
  800. //
  801. if (!Section || !*Section) {
  802. DEBUGMSG ((DBG_WHOOPS, "Missing Section or Key for SIF"));
  803. return 0;
  804. }
  805. if (!Key && !Value) {
  806. DEBUGMSG ((DBG_WHOOPS, "Missing Value or Key for SIF"));
  807. return 0;
  808. }
  809. if (Key && !*Key) {
  810. DEBUGMSG ((DBG_WHOOPS, "Empty key specified for SIF"));
  811. return 0;
  812. }
  813. //
  814. // ensure key/value do not have quotes.
  815. //
  816. if (Value && _tcschr (Value,TEXT('\"'))) {
  817. DEBUGMSG ((DBG_WHOOPS, "Quotes found in SIF value %s", Value));
  818. return 0;
  819. }
  820. if (Key && _tcschr (Key, TEXT('\"'))) {
  821. DEBUGMSG ((DBG_WHOOPS, "Quotes found in SIF key %s", Key));
  822. return 0;
  823. }
  824. if (g_AnswerFileAlreadyWritten) {
  825. DEBUGMSG ((DBG_WHOOPS, "Answer file has already been written to disk."));
  826. rSeed = 0;
  827. } else {
  828. //
  829. // Make sure Key is not NULL
  830. //
  831. if (!Key) {
  832. Key = BUILDINF_NULLKEY_PREFIX;
  833. }
  834. //
  835. // Massage the section in case it has any wacks in it.
  836. //
  837. StringCopy (massagedSection,Section);
  838. pHandleWacks(massagedSection,REPLACE_WACKS);
  839. //
  840. // Ensure the key is unique, if requested.
  841. //
  842. if (EnsureKeyIsUnique) {
  843. //
  844. // Add the prefix on..
  845. //
  846. wsprintf(uniqueKey,TEXT("%s%04X%s"),BUILDINF_UNIQUEKEY_PREFIX,ValueSectionId ? uniqueNumber - 1 : uniqueNumber,Key);
  847. if (!ValueSectionId) {
  848. uniqueNumber++;
  849. }
  850. keyPtr = uniqueKey;
  851. } else {
  852. keyPtr = (PTSTR) Key;
  853. }
  854. //
  855. // See if the key exists already.
  856. //
  857. wsprintf(keySection,S_ANSWERFILE_SECTIONMASK,massagedSection);
  858. MemDbBuildKey(aKey,keySection,keyPtr,NULL,NULL);
  859. keyFound = MemDbGetValue(aKey,&testValue);
  860. //
  861. // Prepare Id and Sequence strings, compute return Seed.
  862. //
  863. idSeed++;
  864. wsprintf(valueId,TEXT("%04x"),idSeed);
  865. if (keyFound) {
  866. sequenceValue = testValue;
  867. }
  868. else {
  869. sequenceValue = seqSeed++;
  870. }
  871. wsprintf(sequence,TEXT("%04x"),sequenceValue);
  872. //
  873. // Delete case
  874. //
  875. if (!Value) {
  876. MemDbBuildKey (aKey, MEMDB_CATEGORY_AF_SECTIONS, massagedSection, sequence, NULL);
  877. MemDbDeleteTree (aKey);
  878. return 0;
  879. }
  880. if (ValueSectionId && !keyFound) {
  881. LOG ((LOG_ERROR,"%u is not associated with %s.",testValue,aKey));
  882. return 0;
  883. }
  884. if (!ValueSectionId) {
  885. //
  886. // This is not a continuation. Need to save the section and key into memdb.
  887. //
  888. if (keyFound) {
  889. //
  890. // Need to replace the key that already exists.
  891. //
  892. MemDbBuildKey(aKey,MEMDB_CATEGORY_AF_SECTIONS,massagedSection,sequence,NULL);
  893. MemDbDeleteTree(aKey);
  894. }
  895. //
  896. // Save away key.
  897. //
  898. b = MemDbSetValueEx(
  899. keySection,
  900. keyPtr,
  901. NULL,
  902. NULL,
  903. sequenceValue,
  904. &keyOffset
  905. );
  906. if (!b) {
  907. DEBUGMSG((DBG_ERROR,"BuildInf: Unable to save key into MemDb."));
  908. rSeed = 0;
  909. }
  910. else {
  911. //
  912. // Save away section
  913. //
  914. b = MemDbSetValueEx(
  915. MEMDB_CATEGORY_AF_SECTIONS,
  916. massagedSection,
  917. sequence,
  918. NULL,
  919. keyOffset,
  920. NULL
  921. );
  922. if (!b) {
  923. DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value."));
  924. rSeed = 0;
  925. }
  926. }
  927. }
  928. //
  929. // Add the value into the database.
  930. //
  931. b = MemDbSetValueEx(
  932. MEMDB_CATEGORY_AF_VALUES,
  933. Value,
  934. NULL,
  935. NULL,
  936. 0,
  937. &valueOffset
  938. );
  939. if (!b) {
  940. DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value."));
  941. rSeed = 0;
  942. }
  943. else {
  944. b = MemDbSetValueEx(
  945. MEMDB_CATEGORY_AF_SECTIONS,
  946. massagedSection,
  947. sequence,
  948. valueId,
  949. valueOffset,
  950. NULL
  951. );
  952. if (!b) {
  953. DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value."));
  954. rSeed = 0;
  955. }
  956. }
  957. }
  958. return rSeed;
  959. }
  960. /*++
  961. Routine Description:
  962. WriteInfKeyEx and WriteInfKey are the external wrapper apis for
  963. pWriteInfKey. Each is used to add information to the memory based answer
  964. file being constructed. WriteInfKeyEx provides greateer control over how
  965. thiings are written to this file.
  966. Arguments:
  967. Section - A string containing the section to add the information
  968. to.
  969. Key - A string containing the key to add information under.
  970. Value - A String containing the value to add under the
  971. section/key. If not specified, Key will be removed.
  972. ValueSectionId - A DWORD offset that is used to add multiple values to
  973. the same key. 0 indicates no offset, and causes the old
  974. key to be overwritten, if it exists or a new key to be
  975. created, if it does not. pWriteInfKey returns this
  976. offset when successful.
  977. EnsureKeyIsUnique - TRUE if the key should be unique in memory, FALSE
  978. otherwise. This is used to create multiple keys in the
  979. answer file under the same section with the same name.
  980. Return Value:
  981. A valid offset, if the call was successful, 0 otherwise.
  982. --*/
  983. DWORD
  984. WriteInfKeyEx (
  985. PCTSTR Section,
  986. PCTSTR Key, OPTIONAL
  987. PCTSTR Value, OPTIONAL
  988. DWORD ValueSectionId,
  989. BOOL EnsureKeyIsUnique
  990. )
  991. {
  992. return pWriteInfKey(Section,Key,Value,ValueSectionId,EnsureKeyIsUnique);
  993. }
  994. DWORD
  995. WriteInfKey (
  996. IN PCTSTR Section,
  997. IN PCTSTR Key, OPTIONAL
  998. IN PCTSTR Value OPTIONAL
  999. )
  1000. {
  1001. return pWriteInfKey(Section,Key,Value,0,FALSE);
  1002. }
  1003. #define S_WIN9XDEL_FILE TEXT("WIN9XDEL.TXT")
  1004. #define S_WIN9XMOV_FILE TEXT("WIN9XMOV.TXT")
  1005. BOOL
  1006. pWriteDelAndMoveFiles (
  1007. VOID
  1008. )
  1009. /*++
  1010. Routine Description:
  1011. pWriteDelAndMoveFiles actually creates two files unrelated to the
  1012. winnt.sif file. These files contain information on the files to be deleted
  1013. during textmode and moved during textmode respectively. Because of the size
  1014. of these sections, and due to certain answer file restrictions, these
  1015. sections are no longer written into the winnt.sif file. They are processed
  1016. seperately during textmode.
  1017. Arguments:
  1018. none.
  1019. Return Value:
  1020. TRUE if the files were created successfully, FALSE otherwise.
  1021. --*/
  1022. {
  1023. MEMDB_ENUMW e;
  1024. WCHAR SrcFile[MEMDB_MAX];
  1025. WCHAR buffer[MEMDB_MAX];
  1026. HANDLE file = INVALID_HANDLE_VALUE;
  1027. PTSTR fileString = NULL;
  1028. PWSTR unicodeString = NULL;
  1029. DWORD bytesWritten;
  1030. ALL_FILEOPS_ENUMW OpEnum;
  1031. UINT unused;
  1032. DWORD Count;
  1033. HASHTABLE fileTable = HtAllocW();
  1034. BOOL result = FALSE;
  1035. MOVELISTW moveList = NULL;
  1036. POOLHANDLE moveListPool = NULL;
  1037. __try {
  1038. //
  1039. // Special code for netcfg.exe tool. Of course, in the real project, g_TempDir will never be null..
  1040. // (We would've exited long ago!!)
  1041. //
  1042. #ifdef DEBUG
  1043. if (!g_TempDir) {
  1044. return TRUE;
  1045. }
  1046. #endif
  1047. moveListPool = PoolMemInitNamedPool ("Move List");
  1048. if (!moveListPool) {
  1049. __leave;
  1050. }
  1051. moveList = AllocateMoveListW (moveListPool);
  1052. if (!moveList) {
  1053. __leave;
  1054. }
  1055. fileString = JoinPaths(g_TempDir,WINNT32_D_WIN9XDEL_FILE);
  1056. file = CreateFile (
  1057. fileString,
  1058. GENERIC_WRITE,
  1059. 0,
  1060. NULL,
  1061. CREATE_ALWAYS,
  1062. FILE_ATTRIBUTE_NORMAL,
  1063. NULL
  1064. );
  1065. DeclareTemporaryFile(fileString);
  1066. if (file == INVALID_HANDLE_VALUE) {
  1067. LOG ((LOG_ERROR,"Error creating file %s.", fileString));
  1068. __leave;
  1069. }
  1070. //
  1071. // Enumerate all TEXTMODE FileDel entries and add them to winnt.sif
  1072. //
  1073. if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_DELETE, NULL)) {
  1074. do {
  1075. //
  1076. // For each file that should be deleted during textmode,
  1077. // write it to the win9xdel.txt file.
  1078. //
  1079. HtAddStringW (fileTable, OpEnum.Path);
  1080. Count++;
  1081. if (!(Count % 128)) {
  1082. TickProgressBar();
  1083. DEBUGLOGTIME (("pWriteDelAndMoveFiles: FILE_DELETE enum 128 files"));
  1084. }
  1085. if (CANCELLED()) {
  1086. return TRUE;
  1087. }
  1088. //WriteFile (file, OpEnum.Path, ByteCountW (OpEnum.Path), &bytesWritten, NULL);
  1089. //pWriteStrToFile(file, TEXT("\r\n"),TRUE);
  1090. } while (EnumNextFileOpW (&OpEnum));
  1091. }
  1092. if (!HtWriteToFile (fileTable, file, WRITE_UNICODE_HEADER)) {
  1093. LOG ((LOG_ERROR,"Unable to write to win9xdel.txt."));
  1094. __leave;
  1095. }
  1096. //
  1097. // Clean up resources.
  1098. //
  1099. CloseHandle(file);
  1100. FreePathString(fileString);
  1101. HtFree (fileTable);
  1102. fileTable = NULL;
  1103. //
  1104. // Create WIN9XMOV.TXT file.
  1105. //
  1106. fileString = JoinPaths(g_TempDir,WINNT32_D_WIN9XMOV_FILE);
  1107. file = CreateFile (
  1108. fileString,
  1109. GENERIC_WRITE,
  1110. 0,
  1111. NULL,
  1112. CREATE_ALWAYS,
  1113. FILE_ATTRIBUTE_NORMAL,
  1114. NULL
  1115. );
  1116. DeclareTemporaryFile(fileString);
  1117. if (file == INVALID_HANDLE_VALUE) {
  1118. LOG ((LOG_ERROR,"Error creating file %s.",fileString));
  1119. __leave;
  1120. }
  1121. //
  1122. // Add all the files to be moved
  1123. //
  1124. if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_MOVE|OPERATION_TEMP_PATH, NULL)) {
  1125. do {
  1126. //
  1127. // only take into account the first destination of a file
  1128. // (when OpEnum.PropertyNum == 0)
  1129. // all other destinations are not relevant for textmode move
  1130. //
  1131. if (OpEnum.PropertyValid && OpEnum.PropertyNum == 0) {
  1132. InsertMoveIntoListW (
  1133. moveList,
  1134. OpEnum.Path,
  1135. OpEnum.Property
  1136. );
  1137. Count++;
  1138. if (!(Count % 256)) {
  1139. TickProgressBar();
  1140. DEBUGLOGTIME (("pWriteDelAndMoveFiles: FILE_MOVE|TEMP_PATH enum 256 files"));
  1141. }
  1142. if (CANCELLED()) {
  1143. return TRUE;
  1144. }
  1145. }
  1146. } while (EnumNextFileOpW (&OpEnum));
  1147. }
  1148. //
  1149. // Enumerate all the SfTemp values and add them to the list of things to move.
  1150. //
  1151. if (MemDbGetValueExW (&e, MEMDB_CATEGORY_SF_TEMPW, NULL, NULL)) {
  1152. do {
  1153. if (MemDbBuildKeyFromOffsetW (e.dwValue, SrcFile, 1, NULL)) {
  1154. InsertMoveIntoListW (
  1155. moveList,
  1156. SrcFile,
  1157. e.szName
  1158. );
  1159. Count++;
  1160. if (!(Count % 128)) {
  1161. TickProgressBar();
  1162. DEBUGLOGTIME (("pWriteDelAndMoveFiles: MEMDB_CATEGORY_SF_TEMPW enum 128 files"));
  1163. }
  1164. if (CANCELLED()) {
  1165. return TRUE;
  1166. }
  1167. }
  1168. ELSE_DEBUGMSGW ((
  1169. DBG_WHOOPS,
  1170. "MemDbBuildKeyFromOffset: Cannot create key from offset %u of %s (2)",
  1171. e.dwValue,
  1172. e.szName
  1173. ));
  1174. } while (MemDbEnumNextValueW (&e));
  1175. }
  1176. //
  1177. // Enumerate all DirsCollision values and add them to the list of things to move.
  1178. //
  1179. if (MemDbGetValueExW (&e, MEMDB_CATEGORY_DIRS_COLLISIONW, NULL, NULL)) {
  1180. do {
  1181. if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_MOVE, e.szName)) {
  1182. InsertMoveIntoListW (
  1183. moveList,
  1184. e.szName,
  1185. OpEnum.Property
  1186. );
  1187. if (CANCELLED()) {
  1188. return TRUE;
  1189. }
  1190. }
  1191. //ELSE_DEBUGMSGW ((
  1192. // DBG_WHOOPS,
  1193. // "EnumFirstFileOpW: failed for FileSpec=%s",
  1194. // e.szName
  1195. // ));
  1196. } while (MemDbEnumNextValueW (&e));
  1197. }
  1198. moveList = RemoveMoveListOverlapW (moveList);
  1199. if (!OutputMoveListW (file, moveList, FALSE)) {
  1200. LOG ((LOG_ERROR,"Unable to write to win9xmov.txt."));
  1201. __leave;
  1202. }
  1203. CloseHandle(file);
  1204. FreePathString(fileString);
  1205. //
  1206. // Finally, we need to write any 'absolutely make sure everything is deleted in this dir' dirs.
  1207. // Textmode will blast away everything in the dir it finds. This *shouldn't* be needed, but
  1208. // beta2 had a problem where there were some INFs left in %windir%\inf even after we had
  1209. // supposedly enumerated all the files there and added them to the delete file.
  1210. // As luck would have it, this was on a reviewer's machine...
  1211. //
  1212. //
  1213. // Create W9XDDIR.TXT file.
  1214. //
  1215. fileString = JoinPaths(g_TempDir,WINNT32_D_W9XDDIR_FILE);
  1216. file = CreateFile (
  1217. fileString,
  1218. GENERIC_WRITE,
  1219. 0,
  1220. NULL,
  1221. CREATE_ALWAYS,
  1222. FILE_ATTRIBUTE_NORMAL,
  1223. NULL
  1224. );
  1225. DeclareTemporaryFile(fileString);
  1226. if (file == INVALID_HANDLE_VALUE) {
  1227. LOG ((LOG_ERROR,"Error creating file %s.",fileString));
  1228. __leave;
  1229. }
  1230. WriteFile (file, "\xff\xfe", 2, &bytesWritten, NULL);
  1231. //
  1232. // Add any enumeration we want to do later. Right now, we only have the one dir to try.
  1233. //
  1234. FreePathString (fileString);
  1235. fileString = JoinPaths (g_WinDir, TEXT("inf"));
  1236. pWriteStrToFile (file, fileString, TRUE); // TRUE == convert to unicode
  1237. pWriteStrToFile (file, "\r\n", TRUE);
  1238. if (MemDbGetValueExW (&e, MEMDB_CATEGORY_FULL_DIR_DELETESW, NULL, NULL)) {
  1239. do {
  1240. if (!WriteFile (file, e.szName, ByteCountW (e.szName), &unused, NULL)) {
  1241. LOG ((LOG_ERROR,"Unable to write to w9xddir.txt."));
  1242. __leave;
  1243. }
  1244. if (!WriteFile (file, L"\r\n", 4, &unused, NULL)) {
  1245. LOG ((LOG_ERROR,"Unable to write to w9xddir.txt."));
  1246. __leave;
  1247. }
  1248. if (CANCELLED()) {
  1249. return TRUE;
  1250. }
  1251. } while (MemDbEnumNextValueW (&e));
  1252. }
  1253. result = TRUE;
  1254. }
  1255. __finally {
  1256. //
  1257. // Free resources.
  1258. //
  1259. if (file != INVALID_HANDLE_VALUE) {
  1260. CloseHandle(file);
  1261. }
  1262. FreePathString(fileString);
  1263. HtFree (fileTable);
  1264. PoolMemDestroyPool (moveListPool);
  1265. }
  1266. return result;
  1267. }
  1268. DWORD
  1269. CreateFileLists (
  1270. IN DWORD Request
  1271. )
  1272. {
  1273. switch (Request) {
  1274. case REQUEST_QUERYTICKS:
  1275. if (REPORTONLY ()) {
  1276. return 0;
  1277. }
  1278. else {
  1279. return TICKS_CREATE_FILE_LISTS;
  1280. }
  1281. case REQUEST_RUN:
  1282. ProgressBar_SetComponentById (MSG_PROCESSING_SYSTEM_FILES);
  1283. pWriteDelAndMoveFiles ();
  1284. ProgressBar_SetComponent (NULL);
  1285. break;
  1286. }
  1287. return ERROR_SUCCESS;
  1288. }