0

I'm writing a PowerShell script that uses the reflection APIs to get all the namespaces in an assembly. Anyways, that's not relevant. Here is the relevant portion of the code:

function Get-Namespaces($assembly)
{
    $assemblyClass = [Reflection.Assembly]
    $assemblyObject = $assemblyClass::ReflectionOnlyLoadFrom($assembly)
    $types = $assemblyObject.GetTypes() # This is the part that's having issues
    return $types | ? IsPublic | select Namespace -Unique
}

cd $PSScriptRoot

$assemblies = @()
$assemblies += Get-WpfAssemblies
$assemblies += Get-UwpAssembly

$namespaces = $assemblies | % {
    % { Get-Namespaces $_ }
}

For some reason, the part that initializes $types seems to be having issues; specifically, it's telling me to catch the exception and check the LoaderExceptions property of the caught exception for more information. So when I try to do just that:

try { $assemblyObject.GetTypes() } catch { echo $_.LoaderExceptions }

and run it, the script prints nothing.

Why does this happen and how can I fix it?


For people who would like to try out the script in its entirety, I've made a publicly available GitHub gist. (Note that it will only work if you have the Windows 10 dev tools installed, but I'm sure reasonably experienced PowerShell users can modify the script to run on their machines.)

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
James Ko
  • 32,215
  • 30
  • 128
  • 239
  • `try` will only catch terminating errors so it is possible that your catch block is not getting executed. What shows in console exactly when you just have `$assemblyObject.GetTypes()` – Matt Feb 07 '16 at 03:11
  • @Matt This message: `Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.` – James Ko Feb 07 '16 at 03:12
  • I don't think this is getting populated but do you see your error here: `$error`. That is an automatic variable that contains all recent errors. `$error[0]` should be the last error. Be warned that it contain all errors in your session. – Matt Feb 07 '16 at 03:16
  • 1
    `$_` in the catch block is `System.Management.Automation.ErrorRecord`. It does not have the property `LoaderExceptions`. Thus, `echo` in the non-strict mode gets null. Is this the case? – Roman Kuzmin Feb 07 '16 at 05:58
  • `$_.Exception.InnerException.LoaderExceptions` – user4003407 Feb 07 '16 at 06:50

3 Answers3

0

Unfortunately, I'm not on a Windows PC to try this, but with some google searching, it looks like the correct syntax should be:

try {
    ....
} catch [System.Reflection.ReflectionTypeLoadException] {
    echo $_.LoaderExceptions
}

Check out http://www.vexasoft.com/blogs/powershell/7255220-powershell-tutorial-try-catch-finally-and-error-handling-in-powershell. Seems to have some good information on exception handling in PowerShell.

kalypzo
  • 102
  • 2
  • 6
0

The (topmost) exception you're catching is probably an ErrorRecord, which doesn't have a property LoaderExceptions. PowerShell expands missing properties to $null values, which get converted to an empty string for output. You can check the exception type as well as its properties and methods by inspecting the current object in the catch block with the Get-Member cmdlet:

try { $assemblyObject.GetTypes() } catch { Get-Member -InputObject $_ }

Since PowerShell has a tendency of hiding relevant information in nested exceptions you may want to do something like this to unroll them:

try {
  ...
} catch {
  $_.InvocationInfo.Line.Trim() + "`n"
  $_.InvocationInfo.PositionMessage + "`n"

  $e = $_.Exception
  do {
    $e.Message
    if ($e.LoaderExceptions) { $e.LoaderExceptions }
    $e = $e.InnerException
  } while ($e)
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
0

The problem was that PowerShell was interpreting what was echoed as a return value:

function Generate-ErrorMessage
{
    try
    {
        blah
    }
    catch
    {
        echo $_
    }
}

$message = Generate-ErrorMessage # Will contain some PowerShell message about not being able to find 'blah'

The solution was to use Console.WriteLine directly:

function Generate-ErrorMessage
{
    try
    {
        blah
    }
    catch
    {
        [Console]::WriteLine($_)
    }
}

Generate-ErrorMessage # prints the message to the console

Not as pretty, but it works as expected.


EDIT: Write-Host also works:

try { blah }
catch { Write-Host $_ }

For other commands, you can take a look here.


EDIT 2: In fact, Out-Host is even better for logging:

try { blah }
catch { $_ | gm | Out-Host } # displays more detailed info than Write-Host
Community
  • 1
  • 1
James Ko
  • 32,215
  • 30
  • 128
  • 239