/*++ Copyright (c) 1990 Microsoft Corporation Module Name: message.c Abstract: This module contains the Win32 Message Management APIs Author: Steve Wood (stevewo) 24-Jan-1991 Revision History: 02-May-94 BruceMa Fix FormatMessage to accept Win32 status codes wrapped as HRESULTS --*/ #include "basedll.h" DWORD APIENTRY BaseDllFormatMessage( BOOLEAN ArgumentsAreAnsi, DWORD dwFlags, LPVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, PWSTR lpBuffer, DWORD nSize, va_list *arglist ); DWORD APIENTRY FormatMessageA( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list *lpArguments ) { NTSTATUS Status; DWORD Result; PWSTR UnicodeSource; PWSTR UnicodeBuffer; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; if (dwFlags & FORMAT_MESSAGE_FROM_STRING) { if (strlen (lpSource) >= MAXSHORT) { Status = STATUS_INVALID_PARAMETER; } else { RtlInitAnsiString( &AnsiString, lpSource ); Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE ); } if (!NT_SUCCESS( Status )) { BaseSetLastNTError( Status ); return 0; } UnicodeSource = UnicodeString.Buffer; } else { UnicodeSource = (PWSTR)lpSource; } if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { UnicodeBuffer = (PWSTR)lpBuffer; } else { UnicodeBuffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), nSize * sizeof( WCHAR ) ); } if (UnicodeBuffer != NULL) { Result = BaseDllFormatMessage( TRUE, dwFlags, (LPVOID)UnicodeSource, dwMessageId, dwLanguageId, UnicodeBuffer, nSize, lpArguments ); } else { BaseSetLastNTError( STATUS_NO_MEMORY ); Result = 0; } if (UnicodeSource != (PWSTR)lpSource) { RtlFreeUnicodeString( &UnicodeString ); } if (Result != 0) { UnicodeString.Length = (USHORT)(Result * sizeof( WCHAR )); UnicodeString.MaximumLength = (USHORT)(UnicodeString.Length + sizeof( UNICODE_NULL )); if (Result >= MAXSHORT) { Status = STATUS_INVALID_PARAMETER; if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { UnicodeBuffer = *(PWSTR *)lpBuffer; *(LPSTR *)lpBuffer = NULL; } } else { if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { UnicodeString.Buffer = *(PWSTR *)lpBuffer; UnicodeBuffer = UnicodeString.Buffer; Status = RtlUnicodeStringToAnsiString( &AnsiString, &UnicodeString, TRUE ); if (NT_SUCCESS( Status )) { *(LPSTR *)lpBuffer = AnsiString.Buffer; } else { *(LPSTR *)lpBuffer = NULL; } } else { UnicodeString.Buffer = UnicodeBuffer; AnsiString.Buffer = lpBuffer; AnsiString.Length = 0; AnsiString.MaximumLength = (USHORT)nSize; Status = RtlUnicodeStringToAnsiString( &AnsiString, &UnicodeString, FALSE ); // // Convert the error to be concistent. // if (Status == STATUS_BUFFER_OVERFLOW) { Status = STATUS_BUFFER_TOO_SMALL; } } } if (!NT_SUCCESS( Status )) { BaseSetLastNTError( Status ); Result = 0; } else { // // Ajust return value, since Result contains Unicode char counts, // we have to adjust it to ANSI char counts // Result = AnsiString.Length; } } else { if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { UnicodeBuffer = NULL; } } if (UnicodeBuffer != NULL) { RtlFreeHeap( RtlProcessHeap(), 0, UnicodeBuffer ); } return Result; } DWORD APIENTRY FormatMessageW( DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, PWSTR lpBuffer, DWORD nSize, va_list *lpArguments ) { return BaseDllFormatMessage( FALSE, dwFlags, (LPVOID)lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, lpArguments ); } BOOLEAN CreateVirtualBuffer( OUT PVIRTUAL_BUFFER Buffer, IN ULONG CommitSize OPTIONAL, IN ULONG ReserveSize OPTIONAL ) { MEMORY_BASIC_INFORMATION MemoryInformation; SIZE_T MemoryInformationLength; if (!ARGUMENT_PRESENT( LongToPtr(CommitSize) )) { CommitSize = 1; } if (!ARGUMENT_PRESENT( LongToPtr(ReserveSize) )) { ReserveSize = ROUND_UP( CommitSize, 0x10000 ); } Buffer->Base = VirtualAlloc( NULL, ReserveSize + BASE_SYSINFO.PageSize, MEM_RESERVE, PAGE_READWRITE ); if (Buffer->Base == NULL) { return FALSE; } MemoryInformationLength = VirtualQuery( Buffer->Base, &MemoryInformation, sizeof( MemoryInformation ) ); if (MemoryInformationLength == sizeof( MemoryInformation )) { ReserveSize = (ULONG)MemoryInformation.RegionSize - BASE_SYSINFO.PageSize; if (VirtualAlloc( Buffer->Base, CommitSize, MEM_COMMIT, PAGE_READWRITE ) != NULL ) { MemoryInformationLength = VirtualQuery( Buffer->Base, &MemoryInformation, sizeof( MemoryInformation ) ); if (MemoryInformationLength == sizeof( MemoryInformation )) { CommitSize = (ULONG)MemoryInformation.RegionSize; Buffer->CommitLimit = (PVOID) ((char *)Buffer->Base + CommitSize); Buffer->ReserveLimit = (PVOID) ((char *)Buffer->Base + ReserveSize); return TRUE; } } } VirtualFree( Buffer->Base, 0, MEM_RELEASE ); return FALSE; } BOOLEAN ExtendVirtualBuffer( IN PVIRTUAL_BUFFER Buffer, IN PVOID Address ) { SIZE_T NewCommitSize; PVOID NewCommitLimit; if (Address >= Buffer->Base) { if (Address < Buffer->CommitLimit) { return TRUE; } if (Address >= Buffer->ReserveLimit) { return FALSE; } NewCommitSize = ((SIZE_T)ROUND_UP_TO_PAGES( (ULONG_PTR)Address + 1 ) - (ULONG_PTR)Buffer->CommitLimit); NewCommitLimit = VirtualAlloc( Buffer->CommitLimit, NewCommitSize, MEM_COMMIT, PAGE_READWRITE ); if (NewCommitLimit != NULL) { Buffer->CommitLimit = (PVOID) ((ULONG_PTR)NewCommitLimit + NewCommitSize); return TRUE; } } return FALSE; } BOOLEAN TrimVirtualBuffer( IN PVIRTUAL_BUFFER Buffer ) { Buffer->CommitLimit = Buffer->Base; return (BOOLEAN)VirtualFree( Buffer->Base, 0, MEM_DECOMMIT ); } BOOLEAN FreeVirtualBuffer( IN PVIRTUAL_BUFFER Buffer ) { return (BOOLEAN)VirtualFree( Buffer->Base, 0, MEM_RELEASE ); } int VirtualBufferExceptionHandler( IN DWORD ExceptionCode, IN PEXCEPTION_POINTERS ExceptionInfo, IN OUT PVIRTUAL_BUFFER Buffer ) { PVOID BadAddress; // // If this is an access violation touching memory within // our reserved buffer, but outside of the committed portion // of the buffer, then we are going to take this exception. // if (ExceptionCode == STATUS_ACCESS_VIOLATION) { BadAddress = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ]; if (BadAddress >= Buffer->CommitLimit && BadAddress < Buffer->ReserveLimit ) { // // This is our exception. If there is room to commit // more memory, try to do so. If no room or unable // to commit, then execute the exception handler. // Otherwise we were able to commit the additional // buffer space, so update the commit limit on the // caller's stack and retry the faulting instruction. // if (ExtendVirtualBuffer( Buffer, BadAddress )) { return EXCEPTION_CONTINUE_EXECUTION; } else { return EXCEPTION_EXECUTE_HANDLER; } } } // // Not an exception we care about, so pass it up the chain. // return EXCEPTION_CONTINUE_SEARCH; } HMODULE BasepNetMsg; DWORD APIENTRY BaseDllFormatMessage( BOOLEAN ArgumentsAreAnsi, DWORD dwFlags, LPVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, PWSTR lpBuffer, DWORD nSize, va_list *arglist ) { VIRTUAL_BUFFER Buffer; NTSTATUS Status; PVOID DllHandle; ULONG MaximumWidth; ULONG LengthNeeded, Result; PMESSAGE_RESOURCE_ENTRY MessageEntry; ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; PWSTR MessageFormat; PWSTR lpAllocedBuffer; PWSTR lpDst; BOOLEAN IgnoreInserts; BOOLEAN ArgumentsAreAnArray; /* If this is a Win32 error wrapped as an OLE HRESULT then unwrap it */ if (((dwMessageId & 0xffff0000) == 0x80070000) && (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) && !(dwFlags & FORMAT_MESSAGE_FROM_HMODULE) && !(dwFlags & FORMAT_MESSAGE_FROM_STRING)) { dwMessageId &= 0x0000ffff; } if (lpBuffer == NULL) { BaseSetLastNTError( STATUS_INVALID_PARAMETER ); return 0; } lpAllocedBuffer = NULL; if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { *(PVOID *)lpBuffer = NULL; } if (!CreateVirtualBuffer( &Buffer, (nSize + 1)*sizeof (WCHAR), 0 )) { return 0; } if (dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS) { IgnoreInserts = TRUE; } else { IgnoreInserts = FALSE; } if (dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY) { ArgumentsAreAnArray = TRUE; } else { ArgumentsAreAnArray = FALSE; } Result = 0; try { UnicodeString.Buffer = NULL; MaximumWidth = dwFlags & FORMAT_MESSAGE_MAX_WIDTH_MASK; if (MaximumWidth == FORMAT_MESSAGE_MAX_WIDTH_MASK) { MaximumWidth = 0xFFFFFFFF; } UnicodeString.Buffer = NULL; if (dwFlags & FORMAT_MESSAGE_FROM_STRING) { MessageFormat = lpSource; } else { if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE) { DllHandle = BasepMapModuleHandle( (HMODULE)lpSource, TRUE ); } else if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) { retrySystem: DllHandle = (PVOID)BaseDllHandle; } else { BaseSetLastNTError( STATUS_INVALID_PARAMETER ); leave; } retrySystem2: Status = RtlFindMessage( DllHandle, PtrToUlong(RT_MESSAGETABLE), (ULONG)dwLanguageId, dwMessageId, &MessageEntry ); if (!NT_SUCCESS( Status )) { if (Status == STATUS_MESSAGE_NOT_FOUND) { if (dwFlags & FORMAT_MESSAGE_FROM_HMODULE && dwFlags & FORMAT_MESSAGE_FROM_SYSTEM ) { dwFlags &= ~FORMAT_MESSAGE_FROM_HMODULE; goto retrySystem; } if ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM && DllHandle == (PVOID)BaseDllHandle ) { // // The message isn't in kernel32.dll, conditionally // load netmsg.dll to see if the message is there. // Leave the dll mapped for subsequent message lookups // if (!BasepNetMsg) { BasepNetMsg = LoadLibraryExW(L"netmsg.dll",NULL,LOAD_LIBRARY_AS_DATAFILE); } if (BasepNetMsg) { DllHandle = BasepNetMsg; goto retrySystem2; } } SetLastError( ERROR_MR_MID_NOT_FOUND ); } else { BaseSetLastNTError( Status ); } leave; } if (!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) { RtlInitAnsiString( &AnsiString, MessageEntry->Text ); Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE ); if (!NT_SUCCESS( Status )) { BaseSetLastNTError( Status ); leave; } MessageFormat = UnicodeString.Buffer; } else { MessageFormat = (PWSTR)MessageEntry->Text; } } Status = RtlFormatMessage( MessageFormat, MaximumWidth, IgnoreInserts, ArgumentsAreAnsi, ArgumentsAreAnArray, arglist, Buffer.Base, (ULONG)((PCHAR)Buffer.ReserveLimit - (PCHAR)Buffer.Base), &LengthNeeded ); RtlFreeUnicodeString( &UnicodeString ); if (NT_SUCCESS( Status )) { if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { lpAllocedBuffer = (PWSTR)LocalAlloc( LMEM_FIXED, LengthNeeded ); if (lpAllocedBuffer == NULL) { BaseSetLastNTError( STATUS_NO_MEMORY ); leave; } lpDst = lpAllocedBuffer; } else if ((LengthNeeded / sizeof( WCHAR )) > nSize) { BaseSetLastNTError( STATUS_BUFFER_TOO_SMALL ); leave; } else { lpDst = lpBuffer; } RtlMoveMemory( lpDst, Buffer.Base, LengthNeeded ); Result = (LengthNeeded - sizeof( WCHAR )) / sizeof( WCHAR ); } else { BaseSetLastNTError( Status ); } } except( VirtualBufferExceptionHandler( GetExceptionCode(), GetExceptionInformation(), &Buffer ) ) { if (GetExceptionCode() == STATUS_ACCESS_VIOLATION) { BaseSetLastNTError( STATUS_NO_MEMORY ); } else { BaseSetLastNTError( GetExceptionCode() ); } Result = 0; } if (lpAllocedBuffer != NULL) { if (Result) { *(PVOID *)lpBuffer = lpAllocedBuffer; } else { LocalFree( lpAllocedBuffer ); } } FreeVirtualBuffer( &Buffer ); return ( Result ); }