|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
logapi.c
Abstract:
Public exposure of an error logging API, based on windows\setup\setuplog.
Author:
Jim Schmidt (jimschm) 28-Apr-1997
Revision History:
jimschm 16-Dec-1998 Added UseCountCs (duh!!)
--*/
#include "precomp.h"
#include <setuplog.h>
SETUPLOG_CONTEXT LogContext; INT UseCount;
#define MAX_STRING_RESOURCE 0x08000
//
// NOTE: Watch the case. We expose an API named SetupLogError, which is different than
// the lib-based SetuplogError function.
//
LPSTR pUnicodeToAnsiForDisplay ( PCWSTR UnicodeStr ) { INT Len; LPSTR AnsiBuffer; CHAR CodePage[32]; DWORD rc;
//
// Allocate buffer to be freed by caller
//
Len = (lstrlenW (UnicodeStr) + 1) * sizeof (WCHAR);
AnsiBuffer = (LPSTR) MyMalloc (Len); if (!AnsiBuffer) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; }
//
// Convert to UNICODE based on thread's Locale; convert assuming string
// is for display purposes
//
if (!GetLocaleInfoA (GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, CodePage, 32)) { MyFree (AnsiBuffer); return NULL; }
rc = WideCharToMultiByte ( atoi (CodePage), WC_COMPOSITECHECK|WC_DISCARDNS, UnicodeStr, -1, AnsiBuffer, Len, NULL, NULL );
if (rc == 0) { MyFree (AnsiBuffer); return NULL; }
return AnsiBuffer; }
PWSTR pAnsiToUnicodeForDisplay ( LPCSTR AnsiStr ) { INT Len; LPWSTR UnicodeBuffer; CHAR CodePage[32]; DWORD rc;
//
// Allocate buffer to be freed by caller
//
Len = (lstrlenA (AnsiStr) + 1) * sizeof (WCHAR);
UnicodeBuffer = (LPWSTR) MyMalloc (Len); if (!UnicodeBuffer) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; }
//
// Convert to UNICODE based on thread's Locale
//
if (!GetLocaleInfoA (GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, CodePage, 32)) { MyFree (UnicodeBuffer); return NULL; }
rc = MultiByteToWideChar ( atoi (CodePage), MB_USEGLYPHCHARS, AnsiStr, -1, UnicodeBuffer, Len );
if (rc == 0) { MyFree (UnicodeBuffer); return NULL; }
return UnicodeBuffer; }
PVOID pOpenFileCallback ( IN LPCTSTR Filename, IN BOOL WipeLogFile )
/*++
Routine Description:
Opens the log and optionally overwrites an existing copy.
Arguments:
FileName - Specifies the name of the file to open or create
WipeLogFile - TRUE if an existing log should be overwritten, FALSE if it should be appended
Return Value:
Pointer to the file handle.
--*/
{ TCHAR CompleteFilename[MAX_PATH]; HANDLE hFile;
//
// Form the pathname of the logfile. (uses real Windows directory)
//
lstrcpyn(CompleteFilename,WindowsDirectory,SIZECHARS(CompleteFilename)); if (!pSetupConcatenatePaths (CompleteFilename, Filename, SIZECHARS(CompleteFilename), NULL)) { return 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 )
/*++
Routine Description:
Writes an entry to the Setup Error Log by converting string to ANSI and calling WriteFile. The message is appended to the log.
Arguments:
LogFile - The handle to an open log file Buffer - The UNICODE message to write
Return Value:
Boolean indicating whether the operation was successful. Error code is set to a Win32 error code if the return value is FALSE.
--*/
{ PCSTR AnsiBuffer; BOOL Status; DWORD DontCare;
if (0xffffffff == SetFilePointer (LogFile, 0, NULL, FILE_END)) { return FALSE; }
#ifdef UNICODE
//
// Convert to ANSI for file output
//
if (AnsiBuffer = pUnicodeToAnsiForDisplay (Buffer)) { Status = WriteFile ( LogFile, AnsiBuffer, lstrlenA (AnsiBuffer), &DontCare, NULL ); MyFree (AnsiBuffer); } else { Status = FALSE; }
#else
Status = WriteFile ( LogFile, Buffer, lstrlen (Buffer), &DontCare, NULL );
#endif
if (Status) { FlushFileBuffers (LogFile); }
return Status;
}
static LPTSTR pFormatLogMessage ( 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.
This routine supports only MessageIds that are Win32 error codes. It does not support messages for string resources.
Arguments:
MessageString - Supplies the message text. For logapi.c, this should always be non-NULL.
MessageId - Supplies a Win32 error code, or 0 if MessageString is to be used.
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]; LPTSTR Args[2];
if (MessageString > (LPCTSTR) 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) GetModuleHandle (NULL), MessageId, MAKELANGID (LANG_NEUTRAL,SUBLANG_NEUTRAL), (LPTSTR) &Buffer, 0, ArgumentList ); }
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; }
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; }
BOOL WINAPI SetupOpenLog ( BOOL Erase )
/*++
Routine Description:
Opens the log for processing. Must be called before SetupLogError is called. A use count is maintained so a single process can call SetupOpenLog and SetupCloseLog from multiple threads.
Arguments:
Erase - TRUE to erase an existing log, or FALSE to append to an existing log
Return Value:
Boolean indicating whether the operation was successful. Error code is set to a Win32 error code if the return value is FALSE.
--*/
{ BOOL b = TRUE; INT i; DWORD rc; BOOL locked = FALSE;
__try { EnterCriticalSection (&LogUseCountCs); locked = TRUE; //
// Perform initialization of log APIs
//
if (!UseCount) { LogContext.OpenFile = (PSPLOG_OPENFILE_ROUTINE) pOpenFileCallback; LogContext.CloseFile = CloseHandle; LogContext.AllocMem = pSetupMalloc; LogContext.FreeMem = pSetupFree; LogContext.Format = (PSPLOG_FORMAT_ROUTINE) pFormatLogMessage; LogContext.Write = (PSPLOG_WRITE_ROUTINE) pWriteFile; LogContext.Lock = pAcquireMutex; LogContext.Unlock = ReleaseMutex;
LogContext.Mutex = CreateMutexW(NULL,FALSE,L"SetuplogMutex");
for (i = 0 ; i < LogSevMaximum ; i++) { LogContext.SeverityDescriptions[i] = MyLoadString (IDS_LOGSEVINFORMATION + i); }
//
// We don't want to allow anyone to erase the existing log, so we just
// ignore the value of Erase and always append to the log.
//
b = SetuplogInitialize (&LogContext, FALSE); rc = GetLastError();
} else { rc = ERROR_ALREADY_INITIALIZED; }
UseCount++; } __finally { //
// Clean up and exit
//
if (!b) { SetupCloseLog(); }
SetLastError (rc); if(locked) { LeaveCriticalSection (&LogUseCountCs); } }
return b; }
VOID WINAPI SetupCloseLog ( VOID )
/*++
Routine Description:
Cleans up all resources associated with the log
Arguments:
none
Return Value:
none
--*/
{ INT i; BOOL locked=FALSE;
__try { EnterCriticalSection (&LogUseCountCs); locked = TRUE; if (!UseCount) { __leave; }
UseCount--; if (!UseCount) { if(LogContext.Mutex) { CloseHandle(LogContext.Mutex); LogContext.Mutex = NULL; }
for (i=0; i<LogSevMaximum; i++) { if (LogContext.SeverityDescriptions[i]) { MyFree (LogContext.SeverityDescriptions[i]); } }
SetuplogTerminate(); } } __finally { if(locked) { LeaveCriticalSection (&LogUseCountCs); } } }
BOOL WINAPI SetupLogErrorA ( IN PCSTR MessageString, IN LogSeverity Severity )
/*++
Routine Description:
Writes an entry to the Setup Error Log. If we're being compiled UNICODE, we convert the MessageString to UNICODE and call SetupLogErrorW. If we're being compiled ANSI, we call the log API directly.
Arguments:
MessageString - Pointer to a buffer containing unformatted message text
Severity - Severity of the error:
LogSevInformation LogSevWarning LogSevError LogSevFatalError
Return Value:
Boolean indicating whether the operation was successful. Error code is set to a Win32 error code if the return value is FALSE.
--*/
{ INT Len; PWSTR UnicodeBuffer; BOOL b = FALSE; CHAR CodePage[32]; DWORD rc;
__try {
if (!UseCount) { rc = ERROR_FILE_INVALID; } else {
#ifdef UNICODE
UnicodeBuffer = pAnsiToUnicodeForDisplay (MessageString);
//
// Call UNICODE version of the log API, preserve error code
//
if (UnicodeBuffer) { b = SetupLogErrorW (UnicodeBuffer, Severity); rc = GetLastError(); MyFree (UnicodeBuffer); } else { rc = GetLastError(); }
#else
//
// ANSI version -- call SetuplogError directly
//
b = SetuplogError (Severity, "%1", 0, MessageString, 0, 0); rc = GetLastError();
#endif
} }
__except (TRUE) { //
// If caller passes in bogus pointer, fail with invalid parameter error
//
rc = ERROR_INVALID_PARAMETER; b = FALSE; }
SetLastError(rc); return b; }
BOOL WINAPI SetupLogErrorW ( IN PCWSTR MessageString, IN LogSeverity Severity )
/*++
Routine Description:
Writes an entry to the Setup Error Log. If compiled with UNICODE, we call the SetuplogError function directly. If compiled with ANSI, we convert to ANSI and call SetupLogErrorA.
Arguments:
MessageString - Pointer to a buffer containing unformatted message text
Severity - Severity of the error:
LogSevInformation LogSevWarning LogSevError LogSevFatalError
Return Value:
Boolean indicating whether the operation was successful. Error code is set to a Win32 error code if the return value is FALSE.
--*/
{ BOOL b = FALSE; PCSTR AnsiBuffer; DWORD rc;
__try {
if (!UseCount) { rc = ERROR_FILE_INVALID; } else {
#ifdef UNICODE
//
// UNICODE version: Call SetuplogError directly
//
// Log the error -- we always link to a UNICODE SetuplogError, despite the TCHAR header file
b = SetuplogError (Severity, L"%1", 0, MessageString, NULL, NULL); rc = GetLastError();
#else
//
// ANSI version: Convert down to ANSI, then call SetupLogErrorA
//
AnsiBuffer = pUnicodeToAnsiForDisplay (MessageString);
if (AnsiBuffer) { b = SetupLogErrorA (AnsiBuffer, Severity); rc = GetLastError(); MyFree (AnsiBuffer); } else { rc = GetLastError(); }
#endif
} } __except (TRUE) { rc = ERROR_INVALID_PARAMETER; b = FALSE; }
SetLastError(rc); return b; }
|