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.

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