|
|
/*++
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 "precomp.h"
#pragma hdrstop
#include <tchar.h>
#if 0
#include <wtypes.h> // to define HRESULT for richedit.h
#include <richedit.h>
#endif
#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"SetArrayToMultiSzValue"; PCWSTR szCreateProcess = L"CreateProcess"; PCWSTR szRegOpenKeyEx = L"RegOpenKeyEx"; PCWSTR szRegQueryValueEx = L"RegQueryValueEx"; PCWSTR szRegSetValueEx = L"RegSetValueEx"; PCWSTR szDeleteFile = L"DeleteFile"; PCWSTR szRemoveDirectory = L"RemoveDirectory";
LPCTSTR szErrorFilename = TEXT("ocmerr.log"); LPCTSTR szActionFilename = TEXT("ocmact.log");
//
// 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;
LPTSTR RetrieveAndFormatMessageV( IN LPCTSTR MessageString, IN UINT MessageId, OPTIONAL IN va_list *ArgumentList )
/*++
Routine Description:
Format a message string using a message string and caller-supplied arguments.
The message id can be either a message in this dll's message table resources or a win32 error code, in which case a description of that error is retrieved from the system.
Arguments:
MessageString - supplies the message text. If this value is NULL, MessageId is used instead
MessageId - supplies message-table identifier or win32 error code for the message.
ArgumentList - supplies arguments to be inserted in the message text.
Return Value:
Pointer to buffer containing formatted message. If the message was not found or some error occurred retrieving it, this buffer will bne empty.
Caller can free the buffer with MyFree().
If NULL is returned, out of memory.
--*/
{ DWORD d; LPTSTR Buffer; LPTSTR Message; TCHAR ModuleName[MAX_PATH]; TCHAR ErrorNumber[24]; PTCHAR p; LPTSTR Args[2];
if(MessageString > SETUPLOG_USE_MESSAGEID) { d = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, MessageString, 0, 0, (LPTSTR)&Buffer, 0, ArgumentList ); } else { d = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | ((MessageId < MSG_FIRST) ? FORMAT_MESSAGE_FROM_SYSTEM : FORMAT_MESSAGE_FROM_HMODULE), (PVOID)hInst, MessageId, MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL), (LPTSTR)&Buffer, 0, ArgumentList ); }
if(!d) { if(GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { return(NULL); }
wsprintf(ErrorNumber, TEXT("%x"), MessageId); Args[0] = ErrorNumber;
Args[1] = ModuleName;
if(GetModuleFileName(hInst, ModuleName, MAX_PATH)) { if(p = _tcschr(ModuleName, TEXT('\\'))) { Args[1] = p+1; } } else { ModuleName[0] = 0; }
d = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, ERROR_MR_MID_NOT_FOUND, MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL), (LPTSTR)&Buffer, 0, (va_list *)Args );
if(!d) { //
// Give up.
//
return(NULL); } }
//
// Make duplicate using our memory system so user can free with MyFree().
//
Message = DuplicateString(Buffer);
LocalFree((HLOCAL)Buffer);
return(Message); }
LPTSTR RetrieveAndFormatMessage( IN LPCTSTR MessageString, IN UINT MessageId, OPTIONAL ... )
/*++
Routine Description:
Format a message string using a message string and caller-supplied arguments.
The message id can be either a message in this dll's message table resources or a win32 error code, in which case a description of that error is retrieved from the system.
Arguments:
MessageString - supplies the message text. If this value is NULL, MessageId is used instead
MessageId - supplies message-table identifier or win32 error code for the message.
... - supplies arguments to be inserted in the message text.
Return Value:
Pointer to buffer containing formatted message. If the message was not found or some error occurred retrieving it, this buffer will bne empty.
Caller can free the buffer with MyFree().
If NULL is returned, out of memory.
--*/
{ va_list arglist; LPTSTR p;
va_start(arglist,MessageId); p = RetrieveAndFormatMessageV(MessageString,MessageId,&arglist); va_end(arglist);
return(p); }
static PVOID pOpenFileCallback( IN PCTSTR Filename, IN BOOL WipeLogFile ) { TCHAR CompleteFilename[MAX_PATH]; HANDLE hFile;
//
// Form the pathname of the logfile.
//
GetWindowsDirectory(CompleteFilename,MAX_PATH); ConcatenatePaths(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; }
static BOOL pWriteFile ( IN PVOID LogFile, IN LPCTSTR Buffer ) { PCSTR AnsiBuffer; BOOL Status; DWORD BytesWritten;
#ifdef UNICODE
if(AnsiBuffer = UnicodeToAnsi (Buffer)) { #else
if (AnsiBuffer = Buffer) { #endif
SetFilePointer (LogFile, 0, NULL, FILE_END);
Status = WriteFile ( LogFile, AnsiBuffer, lstrlenA(AnsiBuffer), &BytesWritten, NULL ); MyFree (AnsiBuffer); } else { Status = FALSE; }
return Status;
}
static 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; }
LPCTSTR MyLoadString( IN UINT StringId )
/*++
Routine Description:
Retrieve a string from the string resources of this module.
Arguments:
StringId - supplies string table identifier for the string.
Return Value:
Pointer to buffer containing string. If the string was not found or some error occurred retrieving it, this buffer will bne empty.
Caller can free the buffer with MyFree().
If NULL is returned, out of memory.
--*/
{ TCHAR Buffer[4096]; UINT Length;
Length = LoadString(hInst,StringId,Buffer,sizeof(Buffer)/sizeof(TCHAR)); if(!Length) { Buffer[0] = 0; }
return(DuplicateString(Buffer)); }
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;
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,TEXT("SetuplogMutex"));
//
// Initialize the log severity descriptions.
//
for(i=0; i < LogSevMaximum; i++) { Context->SeverityDescriptions[i] = MyLoadString(IDS_LOGSEVINFO+i); }
SetuplogInitializeEx(Context, FALSE, szActionFilename, szErrorFilename, 0, 0); SetuplogError( LogSevInformation, SETUPLOG_USE_MESSAGEID, MSG_LOG_GUI_START, 0,0);
}
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(); }
// all this stuff is from syssetup's setuplog code
#if 0
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)) sapiAssert(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 = DuplicateString(SETUPLOG_ITEM_TERMINATOR); if (!FormatStrings[LogSevMaximum].Find) { Status = FALSE; goto cleanup; } FormatStrings[LogSevMaximum].Replace = DuplicateString (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 = SendMessage (hWndRichEdit, EM_FINDTEXT, FR_MATCHCASE, (LPARAM) &FindText)) != -1) {
//
// Verify that the target is at the beginning of the line
//
LineIndex = 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) 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; }
BOOL 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); } CenterWindowRelativeToParent(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
//
// Form the pathname of the logfile.
//
if (!ARGUMENT_PRESENT(OptionalFileName)) { GetWindowsDirectory (TmpFileName,MAX_PATH); ConcatenatePaths (TmpFileName,SETUPLOG_ERROR_FILENAME,MAX_PATH,NULL); Global.LogFileName = DuplicateString (TmpFileName); } else { if (wcslen(OptionalFileName) > MAX_PATH) { Status = 0; goto err0; } Global.LogFileName = DuplicateString (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 = DuplicateString (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 = 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; }
#endif
|