Microcode: Exploring More of .NET with Get-Assembly

In a previous post, I introduced a function called Get-Type, which allows you to see all of the types currently loaded by .NET.  What it doesn't do is help you find assemblies, the DLL files containing new types.  So while there might be 19000-odd types loaded when I launch the Windows PowerShell Integrated Script Editor (aka Graphical PowerShell), it's the tip of the iceberg for what .NET can do.

Since the Iceberg is so huge, you probably do not want to try to load every bit of the iceberg just to see what it contains.  So you're left with a classic Schrödinger's Cat problem (observing something may change what you observe).

Luckily, assemblies are located in a common spot on the filesystem, so it's actually really easy to find all of the assemblies that exist for public consumption without loading them.  This will catch assemblies installed for public use, but it will not catch an assembly that lives in the same directory as a program.

In this post, I'll show you how to write a Get-Assembly function, and use it with the Object Pipeline to explore and load up specific assemblies.  Since Assemblies exist underneath the Windows Directory, I can search the directory with Get-ChildItem (or dir, if you prefer).

Get-ChildItem (Join-Path $env:Windir "Assembly") -recurse -filter "*.dll"

If you run this, you should notice two things.  First, there's a lot of output that goes by far too quickly to be useful to a person. Second, there's a bunch of files DLLs, and some files named something like Assembly.Ni.Dll.  We cannot load those assemblies, so we'll remove them from the list by adding on a Where-Object.  Then, to make loading up the assembly easier, I'll pipe each result to Add-Member in order to add a method to load the assembly.

function Get-Assembly()
{
Get-ChildItem (Join-Path $env:Windir "Assembly") -recurse -filter "*.dll" |
Where-Object {
! $_.Name.Substring(0,$_.Name.IndexOf($_.Extension)).EndsWith(".ni")
} | Add-Member ScriptMethod Load -passThru {
[Reflection.Assembly]::LoadFrom($this.FullName)
}
}

This Get-Assembly gets rid of the DLLs we can't use, but it's still not ideal for human consumption because of how files are displayed.  Files from different directories show up with a header for that directory, so running this produces a lot of white space when you're really interested in the name or the full path. Luckily, I can always summarize the object with Select-Object.  The next bit will sort all of the assemblies by their name and output just that property.

Get-Assembly |
Sort-Object Name |
Select-Object Name

I can also use the Get-Assembly command to load up assemblies to explore with Get-Type.  This next statement will walk through all of the assemblies related to Linq and load them up:

Get-Assembly |
Where-Object { $_.Name -like "*Linq*" } |
Foreach-Object { $_.Load() }

I can also save the loaded assemblies into a variable so I can use them later.  For instance, I'll save the Linq assemblies and then use Get-Type to look through each of the types in those assemblies.

$assemblies = Get-Assembly |
Where-Object { $_.Name -like "*Linq*" } |
Foreach-Object { $_.Load() }

Get-Type | Where-Object {
$type = $_
$assemblies | Where-Object { $type.Assembly -eq $_ }
}
   

Get-Type and Get-Assembly work well together as a way to explore all that .NET has to offer.  On my box, there's over 500 assemblies, of which only 41 are currently loaded in PowerShell.  If 41 assemblies contain about 20000 types, imagine just how many pre-canned solutions exist in .NET just waiting for you to use.

Hope this Helps,

James Brundage [MSFT]

Download scripts:

Module Name

Scripts

DotNet

Get-Assembly

 

Get-Type