/*++ 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_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