Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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