|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
log.c
Abstract:
Routines for logging actions performed during setup.
Author:
Ted Miller (tedm) 4-Apr-1995
Revision History:
--*/
#include "setupp.h"
#pragma hdrstop
#include <wtypes.h> // to define HRESULT for richedit.h
#include <richedit.h>
#include "setuplog.h"
//
// Severity descriptions. Initialized in InitializeSetupActionLog.
//
PCSTR SeverityDescriptions[LogSevMaximum];
//
// Constant strings used for logging in various places.
//
PCWSTR szWaitForSingleObject = L"WaitForSingleObject"; PCWSTR szFALSE = L"FALSE"; PCWSTR szSetGroupOfValues = L"SetGroupOfValues"; PCWSTR szSetArrayToMultiSzValue = L"pSetupSetArrayToMultiSzValue"; PCWSTR szCreateProcess = L"CreateProcess"; PCWSTR szRegOpenKeyEx = L"RegOpenKeyEx"; PCWSTR szRegQueryValueEx = L"RegQueryValueEx"; PCWSTR szRegSetValueEx = L"RegSetValueEx"; PCWSTR szDeleteFile = L"DeleteFile"; PCWSTR szRemoveDirectory = L"RemoveDirectory"; PCWSTR szSetupInstallFromInfSection = L"SetupInstallFromInfSection";
//
// This structure is passed as the parameter to DialogBoxParam to provide
// initialization data.
//
typedef struct _LOGVIEW_DIALOG_DATA { PCWSTR LogFileName; // actual file used
PCWSTR WindowHeading; // actual title of main window
} LOGVIEW_DIALOG_DATA, *PLOGVIEW_DIALOG_DATA;
PVOID pOpenFileCallback( IN PCTSTR Filename, IN BOOL WipeLogFile ) { WCHAR CompleteFilename[MAX_PATH]; HANDLE hFile; DWORD Result;
//
// Form the pathname of the logfile.
//
Result = GetWindowsDirectory(CompleteFilename,MAX_PATH); if( Result == 0) { MYASSERT(FALSE); return NULL; } pSetupConcatenatePaths(CompleteFilename,Filename,MAX_PATH,NULL);
//
// If we're wiping the logfile clean, attempt to delete
// what's there.
//
if(WipeLogFile) { SetFileAttributes(CompleteFilename,FILE_ATTRIBUTE_NORMAL); DeleteFile(CompleteFilename); }
//
// Open existing file or create a new one.
//
hFile = CreateFile( CompleteFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
return (PVOID)hFile; }
BOOL pWriteFile ( IN PVOID LogFile, IN LPCTSTR Buffer ) { PCSTR AnsiBuffer; BOOL Status; DWORD BytesWritten;
// Write message to log file
//
if(AnsiBuffer = pSetupUnicodeToAnsi (Buffer)) { SetFilePointer (LogFile, 0, NULL, FILE_END);
Status = WriteFile ( LogFile, AnsiBuffer, lstrlenA(AnsiBuffer), &BytesWritten, NULL ); MyFree (AnsiBuffer); } else { Status = FALSE; }
// Write log message to debugging log
//
SetupDebugPrint((LPWSTR)Buffer);
return Status;
}
BOOL pAcquireMutex ( IN PVOID Mutex )
/*++
Routine Description:
Waits on the log mutex for a max of 1 second, and returns TRUE if the mutex was claimed, or FALSE if the claim timed out.
Arguments:
Mutex - specifies which mutex to acquire.
Return Value:
TRUE if the mutex was claimed, or FALSE if the claim timed out.
--*/
{ DWORD rc;
if (!Mutex) { SetLastError (ERROR_INVALID_HANDLE); return FALSE; }
// Wait a max of 1 second for the mutex
rc = WaitForSingleObject (Mutex, 1000); if (rc != WAIT_OBJECT_0) { SetLastError (ERROR_EXCL_SEM_ALREADY_OWNED); return FALSE; }
return TRUE; }
VOID InitializeSetupLog( IN PSETUPLOG_CONTEXT Context )
/*++
Routine Description:
Initialize the setup action log. This file is a textual description of actions performed during setup.
The log file is called setuplog.txt and it exists in the windows dir.
Arguments:
Context - context structrure used by Setuplog.
Return Value:
Boolean value indicating whether initialization was sucessful.
--*/
{ UINT i; PWSTR p;
Context->OpenFile = pOpenFileCallback; Context->CloseFile = CloseHandle; Context->AllocMem = MyMalloc; Context->FreeMem = MyFree; Context->Format = RetrieveAndFormatMessageV; Context->Write = pWriteFile; Context->Lock = pAcquireMutex; Context->Unlock = ReleaseMutex;
Context->Mutex = CreateMutex(NULL,FALSE,L"SetuplogMutex");
//
// Initialize the log severity descriptions.
//
for(i=0; i<LogSevMaximum; i++) { Context->SeverityDescriptions[i] = MyLoadString(IDS_LOGSEVINFO+i); }
SetuplogInitialize (Context, FALSE);
SetuplogError( LogSevInformation, SETUPLOG_USE_MESSAGEID, MSG_LOG_GUI_START, NULL,NULL);
}
VOID TerminateSetupLog( IN PSETUPLOG_CONTEXT Context )
/*++
Routine Description:
Close the Setup log and free resources.
Arguments:
Context - context structrure used by Setuplog.
Return Value:
None.
--*/
{ UINT i;
if(Context->Mutex) { CloseHandle(Context->Mutex); Context->Mutex = NULL; }
for (i=0; i<LogSevMaximum; i++) { if (Context->SeverityDescriptions[i]) { MyFree (Context->SeverityDescriptions[i]); } }
SetuplogTerminate(); }
DWORD CALLBACK EditStreamCallback ( IN HANDLE hLogFile, IN LPBYTE Buffer, IN LONG cb, IN PLONG pcb )
/*++
Routine Description:
Callback routine used by the rich edit control to read in the log file.
Arguments:
hLogFile - handle of file to read. This module provides the value through the EDITSTREAM structure.
Buffer - address of buffer that receives the data
cb - number of bytes to read
pcb - address of number of bytes actually read
Return Value:
0 to continue the stream operation, or nonzero to abort it.
--*/
{ DWORD error;
if (!ReadFile (hLogFile, Buffer, cb, pcb, NULL)) { error = GetLastError(); return error; }
return 0; }
BOOL FormatText ( IN HWND hWndRichEdit )
/*++
Routine Description:
Modify the contents of the rich edit control to make the log file look prettier. The modifications are driven by the array FormatStrings. It contains a list of strings to search for, and modifications to make when a target string is found.
Arguments:
hWndRichEdit - handle to the Rich Edit control.
Return Value:
Boolean indicating whether routine was successful.
--*/
{
//
// separate items in the log with a horizontal line
//
PCWSTR NewTerm = L"----------------------------------------" L"----------------------------------------\r\n\r\n";
FINDTEXT FindText; // target text to change
INT Position; // start of where target was found
INT LineIndex; // index of line containing target
CHARRANGE SelectRange; // range where target was found
CHARFORMAT NewFormat; // structure to hold our format changes
INT i; // loop counter
PWSTR pw; // temporary pointer
BOOL Status; // return status
//
// An array of changes we're going to make
//
struct tagFormatStrings { PCWSTR Find; // target string
PCWSTR Replace; // change the target to this
COLORREF Color; // make target text this color
DWORD Effects; // modifications to target's font
} FormatStrings[] = { {NULL, NULL, RGB(0,150,0), CFE_UNDERLINE}, {NULL, NULL, RGB(150,150,0), CFE_UNDERLINE}, {NULL, NULL, RGB(255,0,0), CFE_UNDERLINE}, {NULL, NULL, RGB(255,0,0), CFE_UNDERLINE|CFE_ITALIC}, {NULL, NULL, RGB(0,0,255), 0} };
//
// Number of elements in FormatStrings array
//
#define FORMATSTRINGSCOUNT \
(sizeof(FormatStrings) / sizeof(struct tagFormatStrings)) MYASSERT(FORMATSTRINGSCOUNT == LogSevMaximum + 1);
//
// Initialize those parts of our data structures that won't change
//
Status = TRUE;
NewFormat.cbSize = sizeof(NewFormat); FindText.chrg.cpMax = -1; // search to the end
for (i=0; i<LogSevMaximum; i++) { // load severity strings
if (!(pw = MyLoadString (IDS_LOGSEVINFO+i))) { Status = FALSE; goto cleanup; } FormatStrings[i].Find = MyMalloc((lstrlen(pw)+4)*sizeof(WCHAR)); if(!FormatStrings[i].Find) { MyFree(pw); Status = FALSE; goto cleanup; } lstrcpy ((PWSTR)FormatStrings[i].Find, pw); lstrcat ((PWSTR)FormatStrings[i].Find, L":\r\n"); MyFree(pw);
if(pw = MyMalloc((lstrlen(FormatStrings[i].Find)+3)*sizeof(WCHAR))) { lstrcpy(pw,FormatStrings[i].Find); lstrcat(pw,L"\r\n"); FormatStrings[i].Replace = pw; } else { Status = FALSE; goto cleanup; } }
FormatStrings[LogSevMaximum].Find = pSetupDuplicateString(SETUPLOG_ITEM_TERMINATOR); if (!FormatStrings[LogSevMaximum].Find) { Status = FALSE; goto cleanup; } FormatStrings[LogSevMaximum].Replace = pSetupDuplicateString (NewTerm); if (!FormatStrings[LogSevMaximum].Replace) { Status = FALSE; goto cleanup; }
//
// Change 1 string at a time in the rich edit control
//
for (i=0; i<FORMATSTRINGSCOUNT; i++) { FindText.chrg.cpMin = 0; // start search at beginning
FindText.lpstrText = (PWSTR) FormatStrings[i].Find;
//
// Search for current target until we've found each instance
//
while ((Position = (INT)SendMessage (hWndRichEdit, EM_FINDTEXT, FR_MATCHCASE, (LPARAM) &FindText)) != -1) {
//
// Verify that the target is at the beginning of the line
//
LineIndex = (INT)SendMessage (hWndRichEdit, EM_LINEFROMCHAR, Position, 0);
if (SendMessage (hWndRichEdit, EM_LINEINDEX, LineIndex, 0) != Position) { FindText.chrg.cpMin = Position + lstrlen (FindText.lpstrText); continue; }
//
// Select the target text and get its format
//
SelectRange.cpMin = Position; SelectRange.cpMax = Position + lstrlen (FindText.lpstrText); SendMessage (hWndRichEdit, EM_EXSETSEL, 0, (LPARAM) &SelectRange); SendMessage (hWndRichEdit, EM_GETCHARFORMAT, TRUE, (LPARAM) &NewFormat);
//
// Modify the target's format
//
NewFormat.dwMask = CFM_COLOR | CFM_UNDERLINE | CFM_ITALIC; NewFormat.dwEffects &= ~CFE_AUTOCOLOR; NewFormat.crTextColor = FormatStrings[i].Color; NewFormat.dwEffects |= FormatStrings[i].Effects; SendMessage (hWndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &NewFormat);
//
// Replace the target with new text. Set the starting point for
// the next search at the end of the current string
//
if (FormatStrings[i].Replace != NULL) { SendMessage (hWndRichEdit, EM_REPLACESEL, FALSE, (LPARAM) FormatStrings[i].Replace); FindText.chrg.cpMin = Position + lstrlen (FormatStrings[i].Replace); } else { FindText.chrg.cpMin = Position + lstrlen (FindText.lpstrText); } } }
cleanup:
for (i=0; i<=LogSevMaximum; i++) { if (FormatStrings[i].Find) { MyFree (FormatStrings[i].Find); } if (FormatStrings[i].Replace) { MyFree (FormatStrings[i].Replace); } } return Status; }
BOOL ReadLogFile ( PCWSTR LogFileName, HWND hWndRichEdit )
/*++
Routine Description:
This routine reads the log file and initializes the contents of the Rich Edit control.
Arguments:
LogFileName - path to the file we're going to read.
hWndRichEdit - handle to the Rich Edit control.
Return Value:
Boolean indicating whether routine was successful.
--*/
{ HANDLE hLogFile; // handle to log file
EDITSTREAM eStream; // structure used by EM_STREAMIN message
hLogFile = CreateFile( LogFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hLogFile == INVALID_HANDLE_VALUE) { hLogFile = NULL; return FALSE; }
//
// Read the file into the Rich Edit control.
//
eStream.dwCookie = (DWORD_PTR) hLogFile; eStream.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback; eStream.dwError = 0; SendMessage (hWndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM) &eStream); CloseHandle (hLogFile);
if (!FormatText (hWndRichEdit)) { return FALSE; } SendMessage (hWndRichEdit, EM_SETMODIFY, TRUE, 0); return TRUE; }
INT_PTR DialogProc ( IN HWND hDialog, IN UINT message, IN WPARAM wParam, IN LPARAM lParam )
/*++
Routine Description:
This is the window proc for the dialog box.
Arguments:
Standard window proc arguments.
Return Value:
Bool that indicates whether we handled the message.
--*/
{ HWND hWndRichEdit; // handle to rich edit window
switch (message) {
case WM_INITDIALOG: SetWindowText (hDialog, ((LOGVIEW_DIALOG_DATA *)lParam)->WindowHeading); hWndRichEdit = GetDlgItem (hDialog, IDT_RICHEDIT1); if (!ReadLogFile (((LOGVIEW_DIALOG_DATA *)lParam)->LogFileName, hWndRichEdit)) { MessageBoxFromMessage (hDialog, MSG_UNABLE_TO_SHOW_LOG, NULL, IDS_ERROR, MB_OK|MB_ICONSTOP); EndDialog (hDialog, FALSE); } // if we have the BB window, do the positioning on that. MainWindowHandle point to that window
if (GetBBhwnd()) CenterWindowRelativeToWindow(hDialog, MainWindowHandle, FALSE); else pSetupCenterWindowRelativeToParent(hDialog); PostMessage(hDialog,WM_APP,0,0); break;
case WM_APP:
hWndRichEdit = GetDlgItem (hDialog, IDT_RICHEDIT1); SendMessage(hWndRichEdit,EM_SETSEL,0,0); SendMessage(hWndRichEdit,EM_SCROLLCARET,0,0); break;
case WM_COMMAND: switch (wParam) { case IDOK: EndDialog (hDialog, TRUE); default: return FALSE; } break;
default: return FALSE; }
return TRUE; }
BOOL ViewSetupActionLog ( IN HWND hOwnerWindow, IN PCWSTR OptionalFileName OPTIONAL, IN PCWSTR OptionalHeading OPTIONAL )
/*++
Routine Description:
Formats the setup action log and displays it in a window. The log file is called setuplog.txt and it exists in the windows dir.
Arguments:
hOwnerWindow - handle to window that owns the dialog box
OptionalFileName - full path of the file to be displayed.
OptionalHeading - text to be shown at the top of the window.
Return Value:
Boolean value indicating whether the routine was sucessful.
--*/
{ LOGVIEW_DIALOG_DATA Global; // initialization data for dialog box
WCHAR TmpFileName[MAX_PATH]; // used to create the log file name
PCWSTR TmpHeading; // used to create the heading
HANDLE hRichedDLL; // DLL used for rich edit
INT Status; // what we're going to return
DWORD Result;
//
// Form the pathname of the logfile.
//
if (!ARGUMENT_PRESENT(OptionalFileName)) { Result = GetWindowsDirectory (TmpFileName,MAX_PATH); if( Result == 0) { MYASSERT(FALSE); return FALSE; } pSetupConcatenatePaths (TmpFileName,SETUPLOG_ERROR_FILENAME,MAX_PATH,NULL); Global.LogFileName = pSetupDuplicateString (TmpFileName); } else { if (wcslen(OptionalFileName) > MAX_PATH) { Status = 0; goto err0; } Global.LogFileName = pSetupDuplicateString (OptionalFileName); }
if (!Global.LogFileName) { Status = FALSE; goto err0; }
//
// Form the heading for the dialog box.
//
if (!ARGUMENT_PRESENT(OptionalHeading)) { TmpHeading = MyLoadString (IDS_LOG_DEFAULT_HEADING); } else { TmpHeading = pSetupDuplicateString (OptionalHeading); } if (!TmpHeading) { Status = FALSE; goto err1; }
Global.WindowHeading = FormatStringMessage (IDS_LOG_WINDOW_HEADING, TmpHeading, Global.LogFileName); if (!Global.WindowHeading) { Status = FALSE; goto err2; }
//
// Create the dialog box.
//
if (!(hRichedDLL = LoadLibrary (L"RICHED20.DLL"))) { Status = FALSE; goto err3; } Status = (BOOL)DialogBoxParam (MyModuleHandle, MAKEINTRESOURCE(IDD_VIEWLOG), hOwnerWindow, DialogProc, (LPARAM) &Global);
//
// Clean up and return.
//
FreeLibrary (hRichedDLL); err3: MyFree (Global.WindowHeading); err2: MyFree (TmpHeading); err1: MyFree (Global.LogFileName); err0: return Status; }
VOID LogRepairInfo( IN PCWSTR Source, IN PCWSTR Target ) { static WCHAR RepairLog[MAX_PATH]; static DWORD WinDirLength; static DWORD SourcePathLength; PWSTR SourceName; DWORD LastSourceChar, LastTargetChar; DWORD LastSourcePeriod, LastTargetPeriod; WCHAR Filename[MAX_PATH]; WCHAR Line[MAX_PATH]; WCHAR tmp[MAX_PATH]; BOOLEAN IsNtImage; ULONG Checksum; BOOLEAN Valid; DWORD Result;
if(!RepairLog[0]) { //
// We haven't calculated the path to setup.log yet
//
Result = GetWindowsDirectory( RepairLog, MAX_PATH ); if( Result == 0) { MYASSERT(FALSE); return; } WinDirLength = lstrlen( RepairLog ); pSetupConcatenatePaths( RepairLog, L"repair\\setup.log", MAX_PATH, NULL ); SourcePathLength = lstrlen( SourcePath ); }
//
// Only log the file if it's inside the Windows directory.
//
if( !wcsncmp( Target, RepairLog, WinDirLength )) {
//
// If we're installing an OEM driver, we shouldn't log it because we can't
// repair it. Make sure the file comes from either the local source or
// the windows directory (for files from driver.cab).
//
if (wcsncmp( Source, SourcePath, SourcePathLength ) && wcsncmp( Source, RepairLog, WinDirLength ) ) {
SetupDebugPrint2(L"SETUP: oem driver not logged: %ws -> %ws.", Source, Target); return; }
if( ValidateAndChecksumFile( Target, &IsNtImage, &Checksum, &Valid )) {
//
// Strip off drive letter.
//
swprintf( Filename, L"\"%s\"", Target+2 );
//
// Convert source name to uncompressed form.
//
SourceName = pSetupDuplicateString( wcsrchr( Source, (WCHAR)'\\' ) + 1 ); if(!SourceName) { SetupDebugPrint( L"SETUP: pSetupDuplicateString failed in LogRepairInfo." ); return; } LastSourceChar = wcslen (SourceName) - 1;
if(SourceName[LastSourceChar] == L'_') { LastSourcePeriod = (DWORD)(wcsrchr( SourceName, (WCHAR)'.' ) - SourceName); MYASSERT(LastSourceChar - LastSourcePeriod < 4);
if(LastSourceChar - LastSourcePeriod == 1) { //
// No extension - just truncate the "._"
//
SourceName[LastSourceChar-1] = L'\0'; } else { //
// Make sure the extensions on source and target match.
// If this fails, we can't log the file copy
//
LastTargetChar = wcslen (Target) - 1; LastTargetPeriod = (ULONG)(wcsrchr( Target, (WCHAR)'.' ) - Target);
if( _wcsnicmp( SourceName + LastSourcePeriod, Target + LastTargetPeriod, LastSourceChar - LastSourcePeriod - 1 )) {
SetupDebugPrint2(L"SETUP: unable to log the following file copy: %ws -> %ws.", Source, Target); MyFree (SourceName); return; }
if(LastTargetChar - LastTargetPeriod < 3) { //
// Short extension - just truncate the "_"
//
SourceName[LastSourceChar] = L'\0'; } else { //
// Need to replace "_" with last character from target
//
MYASSERT(LastTargetChar - LastTargetPeriod == 3); SourceName[LastSourceChar] = Target[LastTargetChar]; } } }
swprintf( Line, L"\"%s\",\"%x\"", SourceName, Checksum);
if (GetPrivateProfileString(L"Files.WinNt",Filename,L"",tmp,sizeof(tmp)/sizeof(tmp[0]),RepairLog)) { //
// there is already an entry for this file present (presumably
// from textmode phase of setup.) Favor this entry over what we
// are about to add
//
SetupDebugPrint1(L"SETUP: skipping log of %ws since it's already present in setup.log.", Target); } else { WritePrivateProfileString( L"Files.WinNt", Filename, Line, RepairLog); }
MyFree (SourceName);
} else { SetupDebugPrint1(L"SETUP: unable to compute checksum for %ws.", Target); } } }
BOOL WINAPI pSetuplogSfcError( IN PCWSTR String, IN DWORD Index ) /*++
Routine Description:
This function is used by sfc.dll to log any file signature problems when sfc is run during setup. if you change this you MUST change the caller in \nt\private\sm\sfc\dll\eventlog.c
Arguments:
String - pointer to a filename string for the problem file. Index - this identifies what message should be logged onto the system.
Return Value:
TRUE for success (the message was added to the errorlog), FALSE for failure.
--*/ { DWORD MessageId; DWORD Severity;
#if PRERELEASE
SfcErrorOccurred = TRUE; #endif
switch (Index) { case 0: MessageId= MSG_DLL_CHANGE; Severity = LogSevInformation; break; case 1: MessageId= MSG_DLL_CHANGE_FAILURE; Severity = LogSevError; break; case 2: MessageId= MSG_DLL_CACHE_COPY_ERROR; Severity = LogSevInformation; break; default: MYASSERT(FALSE && "Unknown message id pSetuplogSfcError"); return(FALSE); }
return SetuplogError( SETUPLOG_SINGLE_MESSAGE | Severity, SETUPLOG_USE_MESSAGEID, MessageId, String, NULL, NULL ); }
|