Get-WERConfig.psm1
# Copyright (c) William Aftring (william.aftring@outlook.com) # Licensed under the MIT license #region GLOBALS $Script:WERRoot = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps' $Script:CustomDumpFlagArray = @( "MiniDumpNormal!0x00000000", "MiniDumpWithDataSegs!0x00000001", "MiniDumpWithFullMemory!0x00000002", "MiniDumpWithHandleData!0x00000004", "MiniDumpFilterMemory!0x00000008", "MiniDumpScanMemory!0x00000010", "MiniDumpWithUnloadedModules!0x00000020", "MiniDumpWithIndirectlyReferencedMemory!0x00000040", "MiniDumpFilterModulePaths!0x00000080", "MiniDumpWithProcessThreadData!0x00000100", "MiniDumpWithPrivateReadWriteMemory!0x00000200", "MiniDumpWithoutOptionalData!0x00000400", "MiniDumpWithFullMemoryInfo!0x00000800", "MiniDumpWithThreadInfo!0x00001000", "MiniDumpWithCodeSegs!0x00002000", "MiniDumpWithoutAuxiliaryState!0x00004000", "MiniDumpWithFullAuxiliaryState!0x00008000", "MiniDumpWithPrivateWriteCopyMemory!0x00010000", "MiniDumpIgnoreInaccessibleMemory!0x00020000", "MiniDumpWithTokenInformation!0x00040000", "MiniDumpWithModuleHeaders!0x00080000", "MiniDumpFilterTriage!0x00100000", "MiniDumpWithAvxXStateContext!0x00200000", "MiniDumpWithIptTrace!0x00400000", "MiniDumpScanInaccessiblePartialPages!0x00800000", "MiniDumpValidTypeFlags!0x01ffffff" ) $Script:WerPropertyArray = @( "DumpType!DWord", "DumpFolder!ExpandString", "DumpCount!DWord", "CustomDumpFlags!DWord" ) class WERConfig { [string]$AppName [string]$DumpType [string]$DumpFolder [uint64]$DumpCount [uint64]$CustomDumpFlags [uint64]$DumpTypeValue [string]$KeyPath WERConfig([string]$KeyPath) { $this.KeyPath = $KeyPath $Name = $KeyPath.Substring($KeyPath.LastIndexOf("\") + 1) $this.AppName = if ( $Name -eq "LocalDumps") { "GLOBAL" } else { $Name } } SetDumpType([string]$DumpType) { $DumpInt = -1 switch ($DumpType) { "CustomDump" { $DumpInt = 0 } "MiniDump" { $DumpInt = 1 } "FullDump" { $DumpInt = 2 } default { $DumpInt = -1 } } $this.DumpTypeValue = $DumpInt $this.DumpType = $DumpType } WriteToRegistry() { # Confirming all of the properties exist Write-Verbose "Writing WER config to registry" foreach ($KeyPropString in $Script:WerPropertyArray) { $KeyPropSplit = $KeyPropString.Split("!") $KeyPropName = $KeyPropSplit[0] $KeyPropType = $KeyPropSplit[1] if (Get-ItemProperty -Path $this.KeyPath -Name $KeyPropName -ErrorAction SilentlyContinue) { Write-Verbose "Setting property $KeyPropName" switch ($KeyPropName) { "DumpType" { Set-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpTypeValue -ErrorAction Stop } "DumpFolder" { Set-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpFolder -ErrorAction Stop } "DumpCount" { Set-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpCount -ErrorAction Stop } "CustomDumpFlags" { Set-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.CustomDumpFlags -ErrorAction Stop } } } else { Write-Verbose "Creating property $KeyPropName" switch ($KeyPropName) { "DumpType" { New-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpTypeValue -PropertyType $KeyPropType } "DumpFolder" { New-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpFolder -PropertyType $KeyPropType } "DumpCount" { New-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.DumpCount -PropertyType $KeyPropType } "CustomDumpFlags" { New-ItemProperty -Path $this.KeyPath -Name $KeyPropName -Value $this.CustomDumpFlags -PropertyType $KeyPropType } } } } } } #endregion #region PRIVATE function Read-WERKey { [CmdletBinding()] param( [string]$AppName, [string]$KeyPath ) Write-Verbose "Processing WER Key for $AppName" $DumpsKey = Get-ItemProperty $KeyPath $Config = [WERConfig]::new($KeyPath) $DumpType = "" switch ($DumpsKey.DumpType) { 0 { $DumpType = "CustomDump" } 1 { $DumpType = "MiniDump" } 2 { $DumpType = "FullDump" } default { $DumpType = "MiniDump" } } $Config.DumpType = $DumpType $Config.DumpTypeValue = if ($null -ne $DumpsKey.DumpType) { $DumpsKey.DumpType } else { 1 } $Config.DumpFolder = if ($DumpsKey.DumpFolder) { $DumpsKey.DumpFolder } else { "%LOCALAPPDATA%\CrashDumps" } $Config.DumpCount = if ($DumpsKey.DumpCount) { $DumpsKey.DumpCount } else { 10 } $Config.CustomDumpFlags = $DumpsKey.CustomDumpFlags return $Config } #endregion #region PUBLIC function Get-WERConfig { [CmdletBinding()] param( $AppName = "GLOBAL" ) if ($AppName -eq "All") { Write-Verbose "Processing Global Key $Script:WERRoot" if (Test-Path $Script:WERRoot) { Read-WERKey -KeyPath $Script:WERRoot -AppName "GLOBAL" } Write-Verbose "Checking for specific app config" (Get-ChildItem $Script:WERRoot -ErrorAction SilentlyContinue) | ForEach-Object { $KeyPath = $_.Name Write-Verbose "Processing $KeyPath" Read-WERKey -KeyPath "Registry::$KeyPath" -AppName $KeyPath.Substring($KeyPath.LastIndexOf("\") + 1) } } elseif ($AppName -eq "GLOBAL") { Write-Verbose "Processing Global Key $Script:WERRoot" if (Test-Path $Script:WERRoot) { Read-WERKey -KeyPath $Script:WERRoot -AppName "GLOBAL" } } if ($AppName.Contains("*")) { $KeyPath = $Script:WERRoot + "\$AppName" Get-ChildItem -Path $KeyPath | ForEach-Object { $AppKey = $_.Name Write-Verbose "Processing $AppKey" Read-WERKey -KeyPath "Registry::$AppKey" -AppName $AppKey.Substring($AppKey.LastIndexOf("\") + 1) } } else { $KeyPath = $Script:WERRoot + "\$AppName" if (!$KeyPath.EndsWith(".exe")) { $KeyPath += ".exe" } if (Test-Path $KeyPath) { Read-WERKey -KeyPath $KeyPath -AppName $AppName } } } #endregion function Set-WERConfig { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")] param( [string]$AppName = "GLOBAL", [ValidateSet("CustomDump", "MiniDump", "FullDump")] [string]$DumpType, [string]$DumpFolder, [uint64]$DumpCount, [uint64]$CustomDumpFlags = $null ) Write-Verbose "AppName $AppName" $KeyPath = $Script:WERRoot $WERConfig = "" if ($DumpType -eq "CustomDump" -and $null -eq $CustomDumpFlags) { Write-Error "Missing parameter CustomDumpFlags" -ErrorAction Stop } if ($AppName -eq "GLOBAL") { if (!(Test-Path $KeyPath)) { Write-Error "$KeyPath not found" -ErrorAction Stop } $WERConfig = Read-WERKey -AppName $AppName -KeyPath $KeyPath } elseif ($AppName) { # Normalizing the AppName if (!$AppName.EndsWith(".exe")) { $AppName += ".exe" } $KeyPath += "\$AppName" if (!(Test-Path $KeyPath)) { Write-Error "$KeyPath not found" -ErrorAction Stop } $WERConfig = Read-WERKey -AppName $AppName -KeyPath $KeyPath } if ($DumpType) { $WERConfig.SetDumpType($DumpType) } if ($DumpCount) { $WERConfig.DumpCount = $DumpCount } if ($DumpFolder) { $WERConfig.DumpFolder = $DumpFolder } if ($CustomDumpFlags) { $WERConfig.CustomDumpFlags = $CustomDumpFlags } try { if ($PSCmdlet.ShouldProcess($WERConfig.KeyPath, "Update WER configuration")) { $WERConfig.WriteToRegistry() $WERConfig } } catch { Write-Error $_ -ErrorAction Stop } } function New-WERConfig { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")] param( [string]$AppName = "GLOBAL", [ValidateSet("CustomDump", "MiniDump", "FullDump")] [string]$DumpType = $null, [string]$DumpFolder = $null, [uint64]$DumpCount = $null, [uint64]$CustomDumpFlags = 0 ) Write-Verbose "AppName $AppName" $KeyPath = $Script:WERRoot if ($AppName -ne "GLOBAL") { Write-Verbose "Appending AppName" if (!$AppName.EndsWith(".exe")) { $AppName += ".exe" } $KeyPath += "\$AppName" } Write-Verbose "Checking if $KeyPath exists" if (Test-Path $KeyPath) { throw "$KeyPath already exists" } New-Item $KeyPath -Force | Out-Null #FIXME(will): These outputs end up being wrong because of the defaults $WERConfig = [WERConfig]::new($KeyPath) if ($DumpType) { $WERConfig.SetDumpType($DumpType) } if ($DumpFolder) { $WERConfig.DumpFolder = $DumpFolder } if ($DumpCount) { $WERConfig.DumpCount = $DumpCount } if ($CustomDumpFlags) { $WERConfig.CustomDumpFlags = $CustomDumpFlags } try { if ($PSCmdlet.ShouldProcess($WERConfig.KeyPath, "Write config to registry")) { $WERConfig.WriteToRegistry() $WERConfig = Read-WERKey -KeyPath $KeyPath -AppName $AppName } $WERConfig } catch { Write-Error $_ -ErrorAction Stop } } function Remove-WERConfig { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [string[]]$AppName, [switch]$Force ) Begin { $OldSetting = $ConfirmPreference if ($Force) { $ConfirmPreference = "None" } } Process { foreach ($_AppName in $AppName) { Write-Verbose "AppName $_AppName" $KeyPath = $Script:WERRoot if ($_AppName -eq "GLOBAL") { if (!($Force)) { Write-Warning "Removing the global WER settings will remove all application specific settings" } if (!$PSCmdlet.ShouldProcess("Global WER Configuration")) { return } } else { if (!$_AppName.EndsWith(".exe")) { $_AppName += ".exe" } $KeyPath += "\$_AppName" } Remove-Item $KeyPath -Recurse } } End { $ConfirmPreference = $OldSetting } } function Get-WERInfo { Get-WERConfig | Format-Table Get-WinEvent -FilterHashTable @{LogName = "Application"; Id = 1001 } -MaxEvents 5 } |