956 lines
24 KiB
C
956 lines
24 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 "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
|
||
);
|
||
}
|
||
|