Skip to content

Instantly share code, notes, and snippets.

@Bill-Stewart
Created July 11, 2023 22:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Bill-Stewart/ae21669dedbd4d5d11fbba62baf9dd1b to your computer and use it in GitHub Desktop.
Save Bill-Stewart/ae21669dedbd4d5d11fbba62baf9dd1b to your computer and use it in GitHub Desktop.
Get-Message.ps1
# Get-Message.ps1
# Written by Bill Stewart (bstewart AT iname.com)
# Version history:
#
# 0.1.0 (2023-07-11)
# * Initial version.
#requires -version 3
<#
.SYNOPSIS
Gets the Windows message string for a specified message ID number.
.DESCRIPTION
Gets the Windows message string for a specified message ID number. A Windows message ID can be a 32-bit value, where the high (most significant) 16 bits are a "facility code" and the low (least significant) 16 bits are the Windows message ID. The Windows message string is obtained by providing the low 16 bits of the Windows message ID number to the FormatMessageW API.
.PARAMETER MessageID
Specifies the message ID number. This parameter must be in the range -2147483648 to 2147483647 (i.e., a 32-bit signed integer). A larger value can be passed as a hex value (e.g., 0x80070035 rather than 2147942453).
.OUTPUTS
Objects with the following properties:
* ID_Dec_Signed - Message ID as a signed decimal 32-bit value
* ID_Dec_Unsigned Message ID as an unsigned decimal 32-bit value
* ID_Hex - Message ID as a hexadecimal value
* LowWord_Dec - Low 16 bits of message ID (decimal)
* LowWord_Hex - Low 16 bits of message ID (hex)
* Message - Message string (if found)
.EXAMPLE
PS C:\> Get-Message -2147024843
This command converts the signed decimal value to unsigned decimal value 2147942453, which is hex 0x80070035. The low 16 bits of this value is 0x35 (decimal 53), which corresponds to the English message string "The network path was not found."
.EXAMPLE
PS C:\> Get-Message 1325
Message ID 1325 is hex 0x052D, which corresponds to the English message string "Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain."
.EXAMPLE
PS C:\> Get-Message 0x57
Message ID 0x57 is decimal code 87, which corresponds to the English message string "The parameter is incorrect."
.LINK
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew
#>
[CmdletBinding()]
param(
[Parameter(Mandatory,ValueFromPipeline)]
[Int[]]
$MessageID
)
begin {
# Windows API constants
$LOAD_LIBRARY_AS_DATAFILE = 0x00000002
$FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
$FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
$FORMAT_MESSAGE_FROM_HMODULE = 0x00000800
$FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
# C# P/Invoke
$APIDefs = @"
// [B4E431E4639B4B6680D2827F8EEF8ED2.Win32API]::LoadLibraryExW()
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibraryExW(
string lpLibFileName,
IntPtr hFile,
uint dwFlags);
// [B4E431E4639B4B6680D2827F8EEF8ED2.Win32API]::FormatMessageW()
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int FormatMessageW(
uint dwFlags,
IntPtr lpSource,
int dwMessageId,
int dwLanguageId,
ref IntPtr lpBuffer,
int nSize,
IntPtr[] Arguments);
// [B4E431E4639B4B6680D2827F8EEF8ED2.Win32API]::FreeLibrary()
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FreeLibrary(
IntPtr hLibModule
);
// [B4E431E4639B4B6680D2827F8EEF8ED2.Win32API]::LocalFree()
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LocalFree(IntPtr hMem);
"@
Add-Type -Name Win32API `
-MemberDefinition $APIDefs `
-Namespace "B4E431E4639B4B6680D2827F8EEF8ED2" `
-ErrorAction Stop
$Win32API = [B4E431E4639B4B6680D2827F8EEF8ED2.Win32API]
function Get-Low16Bits {
param(
$value
)
$bytes = [BitConverter]::GetBytes($value)
(($bytes[1] -as [Int]) -shl 8) -bor $bytes[0]
}
function Get-WindowsMessage {
param(
[Int]
$messageID
)
$fullMessageID = [BitConverter]::ToUInt32([BitConverter]::GetBytes($messageID),0)
$messageID = Get-Low16Bits $fullMessageID
$flags = $FORMAT_MESSAGE_ALLOCATE_BUFFER -bor
$FORMAT_MESSAGE_IGNORE_INSERTS -bor
$FORMAT_MESSAGE_FROM_SYSTEM
$hMod = [IntPtr]::Zero
if ( ($fullMessageID -ge 2100) -and ($fullMessageID -lt 2999) ) {
$flags = $flags -bor $FORMAT_MESSAGE_FROM_HMODULE
$hMod = $Win32API::LoadLibraryExW("netmsg.dll", # lpLibFileName
[IntPtr]::Zero, # hFile
$LOAD_LIBRARY_AS_DATAFILE) # dwFlags
}
if ( ($fullMessageID -ge 12000) -and ($fullMessageID -le 12176) ) {
$flags = $flags -bor $FORMAT_MESSAGE_FROM_HMODULE
$hMod = $Win32API::LoadLibraryExW("wininet.dll", # lpLibFileName
[IntPtr]::Zero, # hFile
$LOAD_LIBRARY_AS_DATAFILE) # dwFlags
}
$message = $null
$pBuf = [IntPtr]::Zero
$numChars = $Win32API::FormatMessageW($flags, # dwFlags
$hMod, # lpSource
$messageID, # dwMessageId
0, # dwLanguageId
[ref] $pBuf, # lpBuffer
0, # nSize
[IntPtr]::Zero) # Arguments
if ( $numChars -gt 0 ) {
$message = ([Runtime.InteropServices.Marshal]::PtrToStringAuto($pBuf)).Trim() -replace '\r\n',' '
[Void] $Win32API::LocalFree($pBuf)
}
if ( $hMod -ne [IntPtr]::Zero ) {
[Void] $Win32API::FreeLibrary($hMod)
}
[PSCustomObject] @{
"ID_Dec_Signed" = [BitConverter]::ToInt32([BitConverter]::GetBytes($fullMessageID),0)
"ID_Dec_Unsigned" = $fullMessageID
"ID_Hex" = "0x{0:X8}" -f $fullMessageID
"LowWord_Dec" = $messageID
"LowWord_Hex" = "0x{0:X4}" -f $messageID
"Message" = $message
}
}
}
process {
foreach ( $MessageIDItem in $MessageID ) {
Get-WindowsMessage $MessageIDItem
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment