958 lines
22 KiB
C
958 lines
22 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|