Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent "Program has stopped working" dialog when calling an .exe from Powershell

I am working on a PS script to generate .xml representations of a huge number of Crystal Reports (Windows 7). In this script, I create an object representing all the files that need to be parsed, then loop over them, calling an .exe on them one-by-one. Occasionally, this .exe crashes. This is fine because it's pretty rare and the reports that can't be processed can be flagged and reviewed manually. The problem is that I have thousands of .rpt files to process, and when the .exe crashes, Windows pops up a dialog asking to debug or continue.

enter image description here

Things I have tried in order to solve the issue:

  • HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting\DebugApplications: I put my exe name here and set the value to 0 (don't debug)

  • HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Windows Error Reporting\DebugApplications: same as above

  • Set the loop that calls the exe to SilentlyContinue

  • Turn off error reporting as follows: Control Panel > Action Center > Change Action Center Settings > Problem Reporting Settings > Change report settings for all users > "Never Check for Solutions" > OK > OK (This only disables the "Windows can check online..." dialog)

Still, I'm getting the popup. There is another reg key, which disables the "Program has stopped working" UI entirely, but I don't want to do that because as a developer of other applications, I need to know when things crash. I just want to exclude this script or the exe it calls from showing the UI.

If I can do that, then the script can run unattended.

The .exe that misbehaves is the latest binary release from here: https://github.com/ajryan/RptToXml and seems to fail when it encounters a null byte in the report file.

Here is my code:

[xml]$MainConfigFile = Get-Content "settings.config.xml"
[xml]$SSRSConfigFile = Get-Content "ssrs.config.xml"
[xml]$CrystalConfigFile = Get-Content "crystal.config.xml"

# create settings objects from xml objects
$MainSettings = @{
    OutputPath = $MainConfigFile.Settings.OutputPath
    SearchString = $MainConfigFile.Settings.SearchString
    ParseCrystal = $MainConfigFile.Settings.ParseCrystal
    ParseSSRS = $MainConfigFile.Settings.ParseSSRS
}

$CrystalSettings = @{
    InputFolderPath = $CrystalConfigFile.CrystalSettings.InputFolderPath
    ContinueOnError = $CrystalConfigFile.CrystalSettings.ContinueOnError
}

$RsSettings = @{
    ReportServerUri = $SSRSConfigFile.RsSettings.ReportServerUri
    RsVersion = $SSRSConfigFile.RsSettings.RsVersion
}

Clear

Write-Host "Ensure these settings are correct before you proceed:" -ForegroundColor Yellow
Write-Host ""

Write-Host "Main Settings" -ForegroundColor Green
$MainSettings
Write-Host ""

Write-Host "Crystal Settings" -ForegroundColor Green
$CrystalSettings
Write-Host ""

Write-Host "SSRS Settings" -ForegroundColor Green
$RsSettings
Write-Host ""

# user must confirm
[string]$SettingsOK=(Read-Host "[Y] to proceed, [N] to quit:")
if ($SettingsOK -ne "Y") {exit}

Write-Host ""
Write-Host "______________________________________"
Clear

# is the output path syntax valid?
if (!(Test-Path -Path $MainSettings.OutputPath -IsValid)) {
    Write-Host -ForegroundColor Green "Output path syntax is invalid:" $MainSettings.OutputPath
    exit
    } else {
    Write-Host -ForegroundColor Green "Output path syntax is correct:" $MainSettings.OutputPath
    }

# does the output path exist?
if (!(Test-Path -Path $MainSettings.OutputPath)) {
    Write-Host -ForegroundColor Yellow "Output path does not exist:" $MainSettings.OutputPath
    [string]$CreateOutputPathOK=(Read-Host "[Y] to create the directory, [N] to quit.")
        if ($CreateOutputPathOK -ne "Y") {exit} else {New-Item -Path $MainSettings.OutputPath -ItemType Directory}
    } else {
    Write-Host -ForegroundColor Green "Output path already exists:" $MainSettings.OutputPath
}

Write-Host ""
Write-Host "______________________________________"
Clear

# get all .rpt files in the input folder, recursively
$CrystalFiles=Get-ChildItem -Path $CrystalSettings.InputFolderPath -Include "*.rpt" -Recurse

Write-Host ""
# count files first and ask the user if they want to see the output, otherwise proceed
Write-Host -ForegroundColor Yellow $CrystalFiles.Count ".rpt files were found in" $CrystalSettings.InputFolderPath
[string]$ShowFilesOK=(Read-Host "[Enter] to proceed, [Y] to view the list of files in the directory, [N] to quit.")
if ($ShowFilesOK -eq "Y") {
    Clear
    # loop through the collection of files and display the file path of each one
    $CrystalFiles | ForEach-Object -Process {$_.FullName.TrimStart($CrystalSettings.InputFolderPath)}
    Write-Host "______________________________________"
    # user must confirm
    Write-Host ""
    Write-Host -ForegroundColor Yellow "The above .rpt files were found in" $CrystalSettings.InputFolderPath 
} elseif ($ShowFilesOK -eq "N") {exit}

Write-Host ""
[string]$ProcessingOK=(Read-Host "[Y] to proceed with .rpt file processing, [N] to quit:")
if ($ProcessingOK -ne "Y") {exit}

Write-Host ""
Write-Host "______________________________________"
Clear

# create a dir inside the output path to hold this run's output
Write-Host -ForegroundColor Green "Creating folder to hold this run's output..."
$RunDir = (New-Item -Path $MainSettings.OutputPath -Name "$(Get-Date -f yyyy-mm-dd__hh_mm_ss)" -ItemType Directory)
$RunDir.FullName

# use .NET ArrayList because immutable PS arrays are very slow
$Success = New-Object System.Collections.ArrayList
$Failure = New-Object System.Collections.ArrayList

#loop through the collection again, this time processing each file and dumping the output to the output dir
$CrystalFiles | ForEach-Object -ErrorAction SilentlyContinue -Process {
    $RelativePathName = $_.FullName.TrimStart($CrystalSettings.InputFolderPath)
    $XmlFileName = "$RunDir\$RelativePathName.xml"

    # force-create the file to ensure the parent folder exists, otherwise RptToXML will crash trying to write the file
    New-Item -Path $XmlFileName -Force
    # then simply delete the empty file
    Remove-Item -Path $XmlFileName

    Write-Host -ForegroundColor Green "Processing file" $RelativePathName
    CMD /c .\RptToXML\RptToXml.exe $_.FullName $RunDir\$($_.FullName.TrimStart($CrystalSettings.InputFolderPath)).xml
    if ($LASTEXITCODE -eq 0) {
        Write-Host "Success" $Success.Add($RelativePathName)} else {Write-Host "Failure" $Failure.Add($RelativePathName)}
    }

$Success | Export-CSV "$RunDir\CrystalSuccess.txt"
$Failure | Export-CSV "$RunDir\CrystalFailure.txt"
like image 500
Eric Avatar asked Feb 10 '17 15:02

Eric


1 Answers

I hate to do this, answering my own question, but I've found a workaround for now.

Before the loop:

# Disable WER temporarily
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\Windows Error Reporting" -Name DontShowUI -Value 1

After the loop:

# Reset the WER UI reg key
Set-ItemProperty "HKCU:\Software\Microsoft\Windows\Windows Error Reporting" -Name DontShowUI -Value 0

This will be improved by calling this script from another script:

  • Get the current value of the key(s) to be modified
  • Change them
  • Call the script which returns control to the caller even if it crashes
  • Return the reg keys to their original value
like image 154
Eric Avatar answered Oct 06 '22 19:10

Eric