windows-nt/Source/XPSP1/NT/base/pnp/setupapi/logapi.c
2020-09-26 16:20:57 +08:00

729 lines
14 KiB
C

/*++
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, 0, 0);
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;
}