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.

955 lines
24 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. log.c
  5. Abstract:
  6. Routines for logging actions performed during setup.
  7. Author:
  8. Ted Miller (tedm) 4-Apr-1995
  9. Revision History:
  10. --*/
  11. #include "setupp.h"
  12. #pragma hdrstop
  13. #include <wtypes.h> // to define HRESULT for richedit.h
  14. #include <richedit.h>
  15. #include "setuplog.h"
  16. //
  17. // Severity descriptions. Initialized in InitializeSetupActionLog.
  18. //
  19. PCSTR SeverityDescriptions[LogSevMaximum];
  20. //
  21. // Constant strings used for logging in various places.
  22. //
  23. PCWSTR szWaitForSingleObject = L"WaitForSingleObject";
  24. PCWSTR szFALSE = L"FALSE";
  25. PCWSTR szSetGroupOfValues = L"SetGroupOfValues";
  26. PCWSTR szSetArrayToMultiSzValue = L"pSetupSetArrayToMultiSzValue";
  27. PCWSTR szCreateProcess = L"CreateProcess";
  28. PCWSTR szRegOpenKeyEx = L"RegOpenKeyEx";
  29. PCWSTR szRegQueryValueEx = L"RegQueryValueEx";
  30. PCWSTR szRegSetValueEx = L"RegSetValueEx";
  31. PCWSTR szDeleteFile = L"DeleteFile";
  32. PCWSTR szRemoveDirectory = L"RemoveDirectory";
  33. PCWSTR szSetupInstallFromInfSection = L"SetupInstallFromInfSection";
  34. //
  35. // This structure is passed as the parameter to DialogBoxParam to provide
  36. // initialization data.
  37. //
  38. typedef struct _LOGVIEW_DIALOG_DATA {
  39. PCWSTR LogFileName; // actual file used
  40. PCWSTR WindowHeading; // actual title of main window
  41. } LOGVIEW_DIALOG_DATA, *PLOGVIEW_DIALOG_DATA;
  42. PVOID
  43. pOpenFileCallback(
  44. IN PCTSTR Filename,
  45. IN BOOL WipeLogFile
  46. )
  47. {
  48. WCHAR CompleteFilename[MAX_PATH];
  49. HANDLE hFile;
  50. DWORD Result;
  51. //
  52. // Form the pathname of the logfile.
  53. //
  54. Result = GetWindowsDirectory(CompleteFilename,MAX_PATH);
  55. if( Result == 0) {
  56. MYASSERT(FALSE);
  57. return NULL;
  58. }
  59. pSetupConcatenatePaths(CompleteFilename,Filename,MAX_PATH,NULL);
  60. //
  61. // If we're wiping the logfile clean, attempt to delete
  62. // what's there.
  63. //
  64. if(WipeLogFile) {
  65. SetFileAttributes(CompleteFilename,FILE_ATTRIBUTE_NORMAL);
  66. DeleteFile(CompleteFilename);
  67. }
  68. //
  69. // Open existing file or create a new one.
  70. //
  71. hFile = CreateFile(
  72. CompleteFilename,
  73. GENERIC_READ | GENERIC_WRITE,
  74. FILE_SHARE_READ | FILE_SHARE_WRITE,
  75. NULL,
  76. OPEN_ALWAYS,
  77. FILE_ATTRIBUTE_NORMAL,
  78. NULL
  79. );
  80. return (PVOID)hFile;
  81. }
  82. BOOL
  83. pWriteFile (
  84. IN PVOID LogFile,
  85. IN LPCTSTR Buffer
  86. )
  87. {
  88. PCSTR AnsiBuffer;
  89. BOOL Status;
  90. DWORD BytesWritten;
  91. // Write message to log file
  92. //
  93. if(AnsiBuffer = pSetupUnicodeToAnsi (Buffer)) {
  94. SetFilePointer (LogFile, 0, NULL, FILE_END);
  95. Status = WriteFile (
  96. LogFile,
  97. AnsiBuffer,
  98. lstrlenA(AnsiBuffer),
  99. &BytesWritten,
  100. NULL
  101. );
  102. MyFree (AnsiBuffer);
  103. } else {
  104. Status = FALSE;
  105. }
  106. // Write log message to debugging log
  107. //
  108. SetupDebugPrint((LPWSTR)Buffer);
  109. return Status;
  110. }
  111. BOOL
  112. pAcquireMutex (
  113. IN PVOID Mutex
  114. )
  115. /*++
  116. Routine Description:
  117. Waits on the log mutex for a max of 1 second, and returns TRUE if the mutex
  118. was claimed, or FALSE if the claim timed out.
  119. Arguments:
  120. Mutex - specifies which mutex to acquire.
  121. Return Value:
  122. TRUE if the mutex was claimed, or FALSE if the claim timed out.
  123. --*/
  124. {
  125. DWORD rc;
  126. if (!Mutex) {
  127. SetLastError (ERROR_INVALID_HANDLE);
  128. return FALSE;
  129. }
  130. // Wait a max of 1 second for the mutex
  131. rc = WaitForSingleObject (Mutex, 1000);
  132. if (rc != WAIT_OBJECT_0) {
  133. SetLastError (ERROR_EXCL_SEM_ALREADY_OWNED);
  134. return FALSE;
  135. }
  136. return TRUE;
  137. }
  138. VOID
  139. InitializeSetupLog(
  140. IN PSETUPLOG_CONTEXT Context
  141. )
  142. /*++
  143. Routine Description:
  144. Initialize the setup action log. This file is a textual description
  145. of actions performed during setup.
  146. The log file is called setuplog.txt and it exists in the windows dir.
  147. Arguments:
  148. Context - context structrure used by Setuplog.
  149. Return Value:
  150. Boolean value indicating whether initialization was sucessful.
  151. --*/
  152. {
  153. UINT i;
  154. PWSTR p;
  155. Context->OpenFile = pOpenFileCallback;
  156. Context->CloseFile = CloseHandle;
  157. Context->AllocMem = MyMalloc;
  158. Context->FreeMem = MyFree;
  159. Context->Format = RetrieveAndFormatMessageV;
  160. Context->Write = pWriteFile;
  161. Context->Lock = pAcquireMutex;
  162. Context->Unlock = ReleaseMutex;
  163. Context->Mutex = CreateMutex(NULL,FALSE,L"SetuplogMutex");
  164. //
  165. // Initialize the log severity descriptions.
  166. //
  167. for(i=0; i<LogSevMaximum; i++) {
  168. Context->SeverityDescriptions[i] = MyLoadString(IDS_LOGSEVINFO+i);
  169. }
  170. SetuplogInitialize (Context, FALSE);
  171. SetuplogError(
  172. LogSevInformation,
  173. SETUPLOG_USE_MESSAGEID,
  174. MSG_LOG_GUI_START,
  175. NULL,NULL);
  176. }
  177. VOID
  178. TerminateSetupLog(
  179. IN PSETUPLOG_CONTEXT Context
  180. )
  181. /*++
  182. Routine Description:
  183. Close the Setup log and free resources.
  184. Arguments:
  185. Context - context structrure used by Setuplog.
  186. Return Value:
  187. None.
  188. --*/
  189. {
  190. UINT i;
  191. if(Context->Mutex) {
  192. CloseHandle(Context->Mutex);
  193. Context->Mutex = NULL;
  194. }
  195. for (i=0; i<LogSevMaximum; i++) {
  196. if (Context->SeverityDescriptions[i]) {
  197. MyFree (Context->SeverityDescriptions[i]);
  198. }
  199. }
  200. SetuplogTerminate();
  201. }
  202. DWORD CALLBACK
  203. EditStreamCallback (
  204. IN HANDLE hLogFile,
  205. IN LPBYTE Buffer,
  206. IN LONG cb,
  207. IN PLONG pcb
  208. )
  209. /*++
  210. Routine Description:
  211. Callback routine used by the rich edit control to read in the log file.
  212. Arguments:
  213. hLogFile - handle of file to read. This module provides the value through
  214. the EDITSTREAM structure.
  215. Buffer - address of buffer that receives the data
  216. cb - number of bytes to read
  217. pcb - address of number of bytes actually read
  218. Return Value:
  219. 0 to continue the stream operation, or nonzero to abort it.
  220. --*/
  221. {
  222. DWORD error;
  223. if (!ReadFile (hLogFile, Buffer, cb, pcb, NULL)) {
  224. error = GetLastError();
  225. return error;
  226. }
  227. return 0;
  228. }
  229. BOOL
  230. FormatText (
  231. IN HWND hWndRichEdit
  232. )
  233. /*++
  234. Routine Description:
  235. Modify the contents of the rich edit control to make the log file look
  236. prettier. The modifications are driven by the array FormatStrings. It
  237. contains a list of strings to search for, and modifications to make when
  238. a target string is found.
  239. Arguments:
  240. hWndRichEdit - handle to the Rich Edit control.
  241. Return Value:
  242. Boolean indicating whether routine was successful.
  243. --*/
  244. {
  245. //
  246. // separate items in the log with a horizontal line
  247. //
  248. PCWSTR NewTerm = L"----------------------------------------"
  249. L"----------------------------------------\r\n\r\n";
  250. FINDTEXT FindText; // target text to change
  251. INT Position; // start of where target was found
  252. INT LineIndex; // index of line containing target
  253. CHARRANGE SelectRange; // range where target was found
  254. CHARFORMAT NewFormat; // structure to hold our format changes
  255. INT i; // loop counter
  256. PWSTR pw; // temporary pointer
  257. BOOL Status; // return status
  258. //
  259. // An array of changes we're going to make
  260. //
  261. struct tagFormatStrings {
  262. PCWSTR Find; // target string
  263. PCWSTR Replace; // change the target to this
  264. COLORREF Color; // make target text this color
  265. DWORD Effects; // modifications to target's font
  266. }
  267. FormatStrings[] = {
  268. {NULL, NULL, RGB(0,150,0), CFE_UNDERLINE},
  269. {NULL, NULL, RGB(150,150,0), CFE_UNDERLINE},
  270. {NULL, NULL, RGB(255,0,0), CFE_UNDERLINE},
  271. {NULL, NULL, RGB(255,0,0), CFE_UNDERLINE|CFE_ITALIC},
  272. {NULL, NULL, RGB(0,0,255), 0}
  273. };
  274. //
  275. // Number of elements in FormatStrings array
  276. //
  277. #define FORMATSTRINGSCOUNT \
  278. (sizeof(FormatStrings) / sizeof(struct tagFormatStrings))
  279. MYASSERT(FORMATSTRINGSCOUNT == LogSevMaximum + 1);
  280. //
  281. // Initialize those parts of our data structures that won't change
  282. //
  283. Status = TRUE;
  284. NewFormat.cbSize = sizeof(NewFormat);
  285. FindText.chrg.cpMax = -1; // search to the end
  286. for (i=0; i<LogSevMaximum; i++) { // load severity strings
  287. if (!(pw = MyLoadString (IDS_LOGSEVINFO+i))) {
  288. Status = FALSE;
  289. goto cleanup;
  290. }
  291. FormatStrings[i].Find = MyMalloc((lstrlen(pw)+4)*sizeof(WCHAR));
  292. if(!FormatStrings[i].Find) {
  293. MyFree(pw);
  294. Status = FALSE;
  295. goto cleanup;
  296. }
  297. lstrcpy ((PWSTR)FormatStrings[i].Find, pw);
  298. lstrcat ((PWSTR)FormatStrings[i].Find, L":\r\n");
  299. MyFree(pw);
  300. if(pw = MyMalloc((lstrlen(FormatStrings[i].Find)+3)*sizeof(WCHAR))) {
  301. lstrcpy(pw,FormatStrings[i].Find);
  302. lstrcat(pw,L"\r\n");
  303. FormatStrings[i].Replace = pw;
  304. } else {
  305. Status = FALSE;
  306. goto cleanup;
  307. }
  308. }
  309. FormatStrings[LogSevMaximum].Find =
  310. pSetupDuplicateString(SETUPLOG_ITEM_TERMINATOR);
  311. if (!FormatStrings[LogSevMaximum].Find) {
  312. Status = FALSE;
  313. goto cleanup;
  314. }
  315. FormatStrings[LogSevMaximum].Replace = pSetupDuplicateString (NewTerm);
  316. if (!FormatStrings[LogSevMaximum].Replace) {
  317. Status = FALSE;
  318. goto cleanup;
  319. }
  320. //
  321. // Change 1 string at a time in the rich edit control
  322. //
  323. for (i=0; i<FORMATSTRINGSCOUNT; i++) {
  324. FindText.chrg.cpMin = 0; // start search at beginning
  325. FindText.lpstrText = (PWSTR) FormatStrings[i].Find;
  326. //
  327. // Search for current target until we've found each instance
  328. //
  329. while ((Position = (INT)SendMessage
  330. (hWndRichEdit, EM_FINDTEXT, FR_MATCHCASE, (LPARAM) &FindText))
  331. != -1) {
  332. //
  333. // Verify that the target is at the beginning of the line
  334. //
  335. LineIndex = (INT)SendMessage (hWndRichEdit, EM_LINEFROMCHAR,
  336. Position, 0);
  337. if (SendMessage (hWndRichEdit, EM_LINEINDEX, LineIndex, 0) !=
  338. Position) {
  339. FindText.chrg.cpMin = Position + lstrlen (FindText.lpstrText);
  340. continue;
  341. }
  342. //
  343. // Select the target text and get its format
  344. //
  345. SelectRange.cpMin = Position;
  346. SelectRange.cpMax = Position + lstrlen (FindText.lpstrText);
  347. SendMessage (hWndRichEdit, EM_EXSETSEL, 0, (LPARAM) &SelectRange);
  348. SendMessage (hWndRichEdit, EM_GETCHARFORMAT, TRUE,
  349. (LPARAM) &NewFormat);
  350. //
  351. // Modify the target's format
  352. //
  353. NewFormat.dwMask = CFM_COLOR | CFM_UNDERLINE | CFM_ITALIC;
  354. NewFormat.dwEffects &= ~CFE_AUTOCOLOR;
  355. NewFormat.crTextColor = FormatStrings[i].Color;
  356. NewFormat.dwEffects |= FormatStrings[i].Effects;
  357. SendMessage (hWndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION,
  358. (LPARAM) &NewFormat);
  359. //
  360. // Replace the target with new text. Set the starting point for
  361. // the next search at the end of the current string
  362. //
  363. if (FormatStrings[i].Replace != NULL) {
  364. SendMessage (hWndRichEdit, EM_REPLACESEL, FALSE,
  365. (LPARAM) FormatStrings[i].Replace);
  366. FindText.chrg.cpMin = Position +
  367. lstrlen (FormatStrings[i].Replace);
  368. } else {
  369. FindText.chrg.cpMin = Position + lstrlen (FindText.lpstrText);
  370. }
  371. }
  372. }
  373. cleanup:
  374. for (i=0; i<=LogSevMaximum; i++) {
  375. if (FormatStrings[i].Find) {
  376. MyFree (FormatStrings[i].Find);
  377. }
  378. if (FormatStrings[i].Replace) {
  379. MyFree (FormatStrings[i].Replace);
  380. }
  381. }
  382. return Status;
  383. }
  384. BOOL
  385. ReadLogFile (
  386. PCWSTR LogFileName,
  387. HWND hWndRichEdit
  388. )
  389. /*++
  390. Routine Description:
  391. This routine reads the log file and initializes the contents of the Rich
  392. Edit control.
  393. Arguments:
  394. LogFileName - path to the file we're going to read.
  395. hWndRichEdit - handle to the Rich Edit control.
  396. Return Value:
  397. Boolean indicating whether routine was successful.
  398. --*/
  399. {
  400. HANDLE hLogFile; // handle to log file
  401. EDITSTREAM eStream; // structure used by EM_STREAMIN message
  402. hLogFile = CreateFile(
  403. LogFileName,
  404. GENERIC_READ,
  405. FILE_SHARE_READ | FILE_SHARE_WRITE,
  406. NULL,
  407. OPEN_EXISTING,
  408. FILE_ATTRIBUTE_NORMAL,
  409. NULL
  410. );
  411. if (hLogFile == INVALID_HANDLE_VALUE) {
  412. hLogFile = NULL;
  413. return FALSE;
  414. }
  415. //
  416. // Read the file into the Rich Edit control.
  417. //
  418. eStream.dwCookie = (DWORD_PTR) hLogFile;
  419. eStream.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
  420. eStream.dwError = 0;
  421. SendMessage (hWndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM) &eStream);
  422. CloseHandle (hLogFile);
  423. if (!FormatText (hWndRichEdit)) {
  424. return FALSE;
  425. }
  426. SendMessage (hWndRichEdit, EM_SETMODIFY, TRUE, 0);
  427. return TRUE;
  428. }
  429. INT_PTR
  430. DialogProc (
  431. IN HWND hDialog,
  432. IN UINT message,
  433. IN WPARAM wParam,
  434. IN LPARAM lParam
  435. )
  436. /*++
  437. Routine Description:
  438. This is the window proc for the dialog box.
  439. Arguments:
  440. Standard window proc arguments.
  441. Return Value:
  442. Bool that indicates whether we handled the message.
  443. --*/
  444. {
  445. HWND hWndRichEdit; // handle to rich edit window
  446. switch (message) {
  447. case WM_INITDIALOG:
  448. SetWindowText (hDialog,
  449. ((LOGVIEW_DIALOG_DATA *)lParam)->WindowHeading);
  450. hWndRichEdit = GetDlgItem (hDialog, IDT_RICHEDIT1);
  451. if (!ReadLogFile (((LOGVIEW_DIALOG_DATA *)lParam)->LogFileName,
  452. hWndRichEdit)) {
  453. MessageBoxFromMessage (hDialog, MSG_UNABLE_TO_SHOW_LOG, NULL,
  454. IDS_ERROR, MB_OK|MB_ICONSTOP);
  455. EndDialog (hDialog, FALSE);
  456. }
  457. // if we have the BB window, do the positioning on that. MainWindowHandle point to that window
  458. if (GetBBhwnd())
  459. CenterWindowRelativeToWindow(hDialog, MainWindowHandle, FALSE);
  460. else
  461. pSetupCenterWindowRelativeToParent(hDialog);
  462. PostMessage(hDialog,WM_APP,0,0);
  463. break;
  464. case WM_APP:
  465. hWndRichEdit = GetDlgItem (hDialog, IDT_RICHEDIT1);
  466. SendMessage(hWndRichEdit,EM_SETSEL,0,0);
  467. SendMessage(hWndRichEdit,EM_SCROLLCARET,0,0);
  468. break;
  469. case WM_COMMAND:
  470. switch (wParam) {
  471. case IDOK:
  472. EndDialog (hDialog, TRUE);
  473. default:
  474. return FALSE;
  475. }
  476. break;
  477. default:
  478. return FALSE;
  479. }
  480. return TRUE;
  481. }
  482. BOOL
  483. ViewSetupActionLog (
  484. IN HWND hOwnerWindow,
  485. IN PCWSTR OptionalFileName OPTIONAL,
  486. IN PCWSTR OptionalHeading OPTIONAL
  487. )
  488. /*++
  489. Routine Description:
  490. Formats the setup action log and displays it in a window.
  491. The log file is called setuplog.txt and it exists in the windows dir.
  492. Arguments:
  493. hOwnerWindow - handle to window that owns the dialog box
  494. OptionalFileName - full path of the file to be displayed.
  495. OptionalHeading - text to be shown at the top of the window.
  496. Return Value:
  497. Boolean value indicating whether the routine was sucessful.
  498. --*/
  499. {
  500. LOGVIEW_DIALOG_DATA Global; // initialization data for dialog box
  501. WCHAR TmpFileName[MAX_PATH]; // used to create the log file name
  502. PCWSTR TmpHeading; // used to create the heading
  503. HANDLE hRichedDLL; // DLL used for rich edit
  504. INT Status; // what we're going to return
  505. DWORD Result;
  506. //
  507. // Form the pathname of the logfile.
  508. //
  509. if (!ARGUMENT_PRESENT(OptionalFileName)) {
  510. Result = GetWindowsDirectory (TmpFileName,MAX_PATH);
  511. if( Result == 0) {
  512. MYASSERT(FALSE);
  513. return FALSE;
  514. }
  515. pSetupConcatenatePaths (TmpFileName,SETUPLOG_ERROR_FILENAME,MAX_PATH,NULL);
  516. Global.LogFileName = pSetupDuplicateString (TmpFileName);
  517. } else {
  518. if (wcslen(OptionalFileName) > MAX_PATH) {
  519. Status = 0;
  520. goto err0;
  521. }
  522. Global.LogFileName = pSetupDuplicateString (OptionalFileName);
  523. }
  524. if (!Global.LogFileName) {
  525. Status = FALSE;
  526. goto err0;
  527. }
  528. //
  529. // Form the heading for the dialog box.
  530. //
  531. if (!ARGUMENT_PRESENT(OptionalHeading)) {
  532. TmpHeading = MyLoadString (IDS_LOG_DEFAULT_HEADING);
  533. } else {
  534. TmpHeading = pSetupDuplicateString (OptionalHeading);
  535. }
  536. if (!TmpHeading) {
  537. Status = FALSE;
  538. goto err1;
  539. }
  540. Global.WindowHeading = FormatStringMessage (IDS_LOG_WINDOW_HEADING,
  541. TmpHeading, Global.LogFileName);
  542. if (!Global.WindowHeading) {
  543. Status = FALSE;
  544. goto err2;
  545. }
  546. //
  547. // Create the dialog box.
  548. //
  549. if (!(hRichedDLL = LoadLibrary (L"RICHED20.DLL"))) {
  550. Status = FALSE;
  551. goto err3;
  552. }
  553. Status = (BOOL)DialogBoxParam (MyModuleHandle, MAKEINTRESOURCE(IDD_VIEWLOG),
  554. hOwnerWindow, DialogProc, (LPARAM) &Global);
  555. //
  556. // Clean up and return.
  557. //
  558. FreeLibrary (hRichedDLL);
  559. err3:
  560. MyFree (Global.WindowHeading);
  561. err2:
  562. MyFree (TmpHeading);
  563. err1:
  564. MyFree (Global.LogFileName);
  565. err0:
  566. return Status;
  567. }
  568. VOID
  569. LogRepairInfo(
  570. IN PCWSTR Source,
  571. IN PCWSTR Target
  572. )
  573. {
  574. static WCHAR RepairLog[MAX_PATH];
  575. static DWORD WinDirLength;
  576. static DWORD SourcePathLength;
  577. PWSTR SourceName;
  578. DWORD LastSourceChar, LastTargetChar;
  579. DWORD LastSourcePeriod, LastTargetPeriod;
  580. WCHAR Filename[MAX_PATH];
  581. WCHAR Line[MAX_PATH];
  582. WCHAR tmp[MAX_PATH];
  583. BOOLEAN IsNtImage;
  584. ULONG Checksum;
  585. BOOLEAN Valid;
  586. DWORD Result;
  587. if(!RepairLog[0]) {
  588. //
  589. // We haven't calculated the path to setup.log yet
  590. //
  591. Result = GetWindowsDirectory( RepairLog, MAX_PATH );
  592. if( Result == 0) {
  593. MYASSERT(FALSE);
  594. return;
  595. }
  596. WinDirLength = lstrlen( RepairLog );
  597. pSetupConcatenatePaths( RepairLog, L"repair\\setup.log", MAX_PATH, NULL );
  598. SourcePathLength = lstrlen( SourcePath );
  599. }
  600. //
  601. // Only log the file if it's inside the Windows directory.
  602. //
  603. if( !wcsncmp( Target, RepairLog, WinDirLength )) {
  604. //
  605. // If we're installing an OEM driver, we shouldn't log it because we can't
  606. // repair it. Make sure the file comes from either the local source or
  607. // the windows directory (for files from driver.cab).
  608. //
  609. if (wcsncmp( Source, SourcePath, SourcePathLength ) &&
  610. wcsncmp( Source, RepairLog, WinDirLength )
  611. ) {
  612. SetupDebugPrint2(L"SETUP: oem driver not logged: %ws -> %ws.",
  613. Source, Target);
  614. return;
  615. }
  616. if( ValidateAndChecksumFile( Target, &IsNtImage, &Checksum, &Valid )) {
  617. //
  618. // Strip off drive letter.
  619. //
  620. swprintf(
  621. Filename,
  622. L"\"%s\"",
  623. Target+2
  624. );
  625. //
  626. // Convert source name to uncompressed form.
  627. //
  628. SourceName = pSetupDuplicateString( wcsrchr( Source, (WCHAR)'\\' ) + 1 );
  629. if(!SourceName) {
  630. SetupDebugPrint( L"SETUP: pSetupDuplicateString failed in LogRepairInfo." );
  631. return;
  632. }
  633. LastSourceChar = wcslen (SourceName) - 1;
  634. if(SourceName[LastSourceChar] == L'_') {
  635. LastSourcePeriod = (DWORD)(wcsrchr( SourceName, (WCHAR)'.' ) - SourceName);
  636. MYASSERT(LastSourceChar - LastSourcePeriod < 4);
  637. if(LastSourceChar - LastSourcePeriod == 1) {
  638. //
  639. // No extension - just truncate the "._"
  640. //
  641. SourceName[LastSourceChar-1] = L'\0';
  642. } else {
  643. //
  644. // Make sure the extensions on source and target match.
  645. // If this fails, we can't log the file copy
  646. //
  647. LastTargetChar = wcslen (Target) - 1;
  648. LastTargetPeriod = (ULONG)(wcsrchr( Target, (WCHAR)'.' ) - Target);
  649. if( _wcsnicmp(
  650. SourceName + LastSourcePeriod,
  651. Target + LastTargetPeriod,
  652. LastSourceChar - LastSourcePeriod - 1 )) {
  653. SetupDebugPrint2(L"SETUP: unable to log the following file copy: %ws -> %ws.",
  654. Source, Target);
  655. MyFree (SourceName);
  656. return;
  657. }
  658. if(LastTargetChar - LastTargetPeriod < 3) {
  659. //
  660. // Short extension - just truncate the "_"
  661. //
  662. SourceName[LastSourceChar] = L'\0';
  663. } else {
  664. //
  665. // Need to replace "_" with last character from target
  666. //
  667. MYASSERT(LastTargetChar - LastTargetPeriod == 3);
  668. SourceName[LastSourceChar] = Target[LastTargetChar];
  669. }
  670. }
  671. }
  672. swprintf(
  673. Line,
  674. L"\"%s\",\"%x\"",
  675. SourceName,
  676. Checksum);
  677. if (GetPrivateProfileString(L"Files.WinNt",Filename,L"",tmp,sizeof(tmp)/sizeof(tmp[0]),RepairLog)) {
  678. //
  679. // there is already an entry for this file present (presumably
  680. // from textmode phase of setup.) Favor this entry over what we
  681. // are about to add
  682. //
  683. SetupDebugPrint1(L"SETUP: skipping log of %ws since it's already present in setup.log.", Target);
  684. } else {
  685. WritePrivateProfileString(
  686. L"Files.WinNt",
  687. Filename,
  688. Line,
  689. RepairLog);
  690. }
  691. MyFree (SourceName);
  692. } else {
  693. SetupDebugPrint1(L"SETUP: unable to compute checksum for %ws.", Target);
  694. }
  695. }
  696. }
  697. BOOL
  698. WINAPI
  699. pSetuplogSfcError(
  700. IN PCWSTR String,
  701. IN DWORD Index
  702. )
  703. /*++
  704. Routine Description:
  705. This function is used by sfc.dll to log any file signature problems when sfc
  706. is run during setup. if you change this you MUST change the caller in
  707. \nt\private\sm\sfc\dll\eventlog.c
  708. Arguments:
  709. String - pointer to a filename string for the problem file.
  710. Index - this identifies what message should be logged onto the system.
  711. Return Value:
  712. TRUE for success (the message was added to the errorlog), FALSE for failure.
  713. --*/
  714. {
  715. DWORD MessageId;
  716. DWORD Severity;
  717. #if PRERELEASE
  718. SfcErrorOccurred = TRUE;
  719. #endif
  720. switch (Index) {
  721. case 0:
  722. MessageId= MSG_DLL_CHANGE;
  723. Severity = LogSevInformation;
  724. break;
  725. case 1:
  726. MessageId= MSG_DLL_CHANGE_FAILURE;
  727. Severity = LogSevError;
  728. break;
  729. case 2:
  730. MessageId= MSG_DLL_CACHE_COPY_ERROR;
  731. Severity = LogSevInformation;
  732. break;
  733. default:
  734. MYASSERT(FALSE && "Unknown message id pSetuplogSfcError");
  735. return(FALSE);
  736. }
  737. return SetuplogError(
  738. SETUPLOG_SINGLE_MESSAGE | Severity,
  739. SETUPLOG_USE_MESSAGEID,
  740. MessageId,
  741. String, NULL, NULL
  742. );
  743. }