Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all articles
Browse latest Browse all 3333

Weekend Scripter: Use PowerShell to Find and Explore .NET Framework Classes

$
0
0

SummaryThe Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to find and to explore .NET Framework classes.

PoshReflectionExplorer? Or not.

Microsoft Scripting Guy, Ed Wilson, is here. Well the day finally arrived. This morning the Scripting Wife dropped me off at the airport, and I begin my trek across the United States to Seattle Washington for the Microsoft internal conference, TechReady 15. I do not know if I have mentioned it or not, but there are 38 Windows PowerShell sessions going on this week at TechReady 15. Dude, I can tell you that I will have my work cut out for me attempting to see all of them. So I have my laptop running the latest build of Windows 8, and the customer preview of Office 2013, and I got a free upgrade to First Class, and 5 ½ hours of free time during the flight to enjoy. Sweet.

I seem to remember an email or a comment on a recent Hey, Scripting Guy! Blog post about exploring .NET Framework classes via Windows PowerShell. I wrote a Windows PowerShell script to do this very thing more than four years ago when I was working on the Windows PowerShell Scripting Guide book for Microsoft Press. I am not going to show you the script (which is a rather ugly Windows PowerShell 1.0 script), but I will show you the techniques that I used in the script to create my explorer.

 

First find the current appdomain

The first thing to do is to find the current appdomain. There is one used by the Windows PowerShell console, and a different one used for the Windows PowerShell ISE. To find the current appdomain, use the static currentdomain property from the system.appdomain .NET Framework class. (By the way, this works in Windows PowerShell 3.0 as well). First, the current appdomain for the Windows PowerShell console.

PS C:\> [appdomain]::CurrentDomain

 

FriendlyName           : DefaultDomain

Id                     : 1

ApplicationDescription :

BaseDirectory          : C:\WINDOWS\system32\WindowsPowerShell\v1.0\

DynamicDirectory       :

RelativeSearchPath     :

SetupInformation       : System.AppDomainSetup

ShadowCopyFiles        : False

 

Now, using the same command in the Windows PowerShell ISE, you can see different results.

PS C:\Users\ed.IAMMRED> [appdomain]::currentdomain

 

 

FriendlyName           : PowerShell_ISE.exe

Id                     : 1

ApplicationDescription :

BaseDirectory          : C:\Windows\system32\WindowsPowerShell\v1.0\

DynamicDirectory       :

RelativeSearchPath     :

SetupInformation       : System.AppDomainSetup

ShadowCopyFiles        : False

 

The currentdomain static property returns a system.appdomain object. This object contains a number of methods in addition to the displayed properties. I can find this information by piping the results from the currentdomain static property to the Get-Member cmdlet. This command is shown here.

[appdomain]::CurrentDomain | get-member

The method I want to use is the getassemblies method. The getassemblies method is not a static method, but because the currentdomain static property returns a system.appdomain object. I can call the method directly from that object. Here is the command and associated output from the Windows PowerShell console (on a Windows PowerShell 2.0 machine. In Windows PowerShell 3.0, the versions are all v4.0.xxxxx).

PS C:\> [appdomain]::currentdomain.GetAssemblies()

 

GAC    Version        Location

---    -------        --------

True   v2.0.50727     C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorlib.dll

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.ConsoleHo...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e0...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands....

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Configuration.Install\2...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.WSMan.Management\1.0...

True   v2.0.50727     C:\Windows\assembly\GAC_64\System.Transactions\2.0.0.0__b77...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands....

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands....

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Security\...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c5619...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Management\2.0.0.0__b03...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.DirectoryServices\2.0.0...

True   v2.0.50727     C:\Windows\assembly\GAC_64\System.Data\2.0.0.0__b77a5c56193...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Configuration\2.0.0.0__...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Security\2.0.0.0__b03f5...

True   v2.0.50727     C:\Windows\assembly\GAC_MSIL\System.Data.SqlXml\2.0.0.0__b7...

The getassemblies method returns instances of the System.Reflection.Assembly .NET Framework class. This class contains a number of very interesting methods and properties. The output from Get-Member on the returned system.reflection.assembly .NET framework class is shown here.

PS C:\> [appdomain]::currentdomain.GetAssemblies() | Get-Member

 

   TypeName: System.Reflection.Assembly

 

Name                      MemberType Definition

----                      ---------- ----------

ModuleResolve             Event      System.Reflection.ModuleResolveEventHandler ...

CreateInstance            Method     System.Object CreateInstance(string typeName...

Equals                    Method     bool Equals(System.Object o)

GetCustomAttributes       Method     System.Object[] GetCustomAttributes(bool inh...

GetExportedTypes          Method     type[] GetExportedTypes()

GetFile                   Method     System.IO.FileStream GetFile(string name)

GetFiles                  Method     System.IO.FileStream[] GetFiles(), System.IO...

GetHashCode               Method     int GetHashCode()

GetLoadedModules          Method     System.Reflection.Module[] GetLoadedModules(...

GetManifestResourceInfo   Method     System.Reflection.ManifestResourceInfo GetMa...

GetManifestResourceNames  Method     string[] GetManifestResourceNames()

GetManifestResourceStream Method     System.IO.Stream GetManifestResourceStream(t...

GetModule                 Method     System.Reflection.Module GetModule(string name)

GetModules                Method     System.Reflection.Module[] GetModules(), Sys...

GetName                   Method     System.Reflection.AssemblyName GetName(), Sy...

GetObjectData             Method     System.Void GetObjectData(System.Runtime.Ser...

GetReferencedAssemblies   Method     System.Reflection.AssemblyName[] GetReferenc...

GetSatelliteAssembly      Method     System.Reflection.Assembly GetSatelliteAssem...

GetType                   Method     type GetType(string name), type GetType(stri...

GetTypes                  Method     type[] GetTypes()

IsDefined                 Method     bool IsDefined(type attributeType, bool inhe...

LoadModule                Method     System.Reflection.Module LoadModule(string m...

ToString                  Method     string ToString()

CodeBase                  Property   System.String CodeBase {get;}

EntryPoint                Property   System.Reflection.MethodInfo EntryPoint {get;}

EscapedCodeBase           Property   System.String EscapedCodeBase {get;}

Evidence                  Property   System.Security.Policy.Evidence Evidence {get;}

FullName                  Property   System.String FullName {get;}

GlobalAssemblyCache       Property   System.Boolean GlobalAssemblyCache {get;}

HostContext               Property   System.Int64 HostContext {get;}

ImageRuntimeVersion       Property   System.String ImageRuntimeVersion {get;}

Location                  Property   System.String Location {get;}

ManifestModule            Property   System.Reflection.Module ManifestModule {get;}

ReflectionOnly            Property   System.Boolean ReflectionOnly {get;}

For instance, one thing you might be interested in finding out is if the assembly resides in the Global Assembly Cache (GAC). In the Windows PowerShell 2.0 console, all assemblies are in fact in the GAC. But in the Windows PowerShell 2.0 ISE, and in the Windows PowerShell 3.0 console, this is not the case. If you find yourself using an assembly very often, you might want the assembly in the GAC. Here is how to find assemblies from the current appdomain that are not in the GAC.

PS C:\> [appdomain]::currentdomain.GetAssemblies() | where {!($_.globalassemblycache)}

 

GAC    Version        Location                                                                     

---    -------        --------                                                                      

False  v2.0.50727     C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe                

False  v2.0.50727     C:\Windows\system32\WindowsPowerShell\v1.0\CompiledComposition.Microsoft.Po...

 

Each loaded .NET Framework assembly contributes .NET Framework classes. To see the classes exposed by the assembly, you can use the gettypes method from the System.Reflection.Assembly class returned by the GetAssemblies method from the appdomain class. As you might expect, there are numerous .NET Framework classes. Interestingly enough, the more filter does not appear to work consistently when working interactively via the Windows PowerShell console, and it does not work at all in the Windows PowerShell ISE. So you might want to consider redirecting the output to a text file. One thing that will help is to sort the output by basetype. Here is the command to do that.

PS C:\> [appdomain]::currentdomain.GetAssemblies() | Foreach-Object {$_.gettypes()} | sort basetype

Do not expect to quickly find exotic, little known, unused .NET Framework classes. Most of the output, for the IT Pro will be rather pedestrian, lots of error classes, lots of enum, lots of structures, and the like. The output headings appear here:

IsPublic IsSerial Name                                     BaseType                

-------- -------- ----                                              --------      

The first couple of pages of output do not event list a base type. Then, when we get to the first grouping of types that do expose a base type, the output is disappointing. Here are the first three lines from that section.

False    True     ModuleLoadExceptionHandlerException      <CrtImplementationDeta...

False    False    CSharpMemberAttributeConverter           Microsoft.CSharp.CShar...

False    False    CSharpTypeAttributeConverter             Microsoft.CSharp.CShar...

False    False    WmiAsyncCmdletHelper                     Microsoft.PowerShell.C...

What is going on here? Remember that last year, I wrote a Hey Scripting Guy! blog entitled, Change a PowerShell Preference Variable to Reveal Hidden Data. Well, if you do not remember it, don’t worry, I did not remember the title either. But I did a search for preference variables, and I found it on the first try. Basically, what you need to do is change the $FormatEnumerationLimit preference variable. By default, the enumeration limit value is 4; and so after four items. it does not use any more space. I like to change it to 20.

But unfortunately, this does not solve the problem. The problem here is that the .NET Framework class names are extremely long...in some cases, really long. Therefore, using the basic redirection arrow does not help capture all the output. In this case, you need to move beyond the defaults and specify a custom width for the output. The best way to do this is to use the Out-File cmdlet. By setting the width to 180, you will capture most (but not all) of the really long .NET Framework class names. (Each time you make the file wider, you also increase the file size and make the file a bit more difficult to use.) For example, a width of 500 characters will create a file about 8 MB in size. A width of 180 will be around 3.5 MB in size (with over 10,000 lines in it). Here is the command I used.

PS C:\> [appdomain]::currentdomain.GetAssemblies() | % {$_.gettypes()} | sort basetype | Out-File -FilePath c:\fso\gettypes.txt -Width 180 –Append

Now that you have the list, you can peruse it at your leisure. Use Get-Member or MSDN to help you find things. I can tell you, from experience that it can spend a very long time looking through stuff. Have fun, and I will talk to you on Monday.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 


Viewing all articles
Browse latest Browse all 3333

Trending Articles