Creating a PowerShell Navigation Provider: #3 Nested GetChildItems() Support

In the second post in this series, we covered the basics of acquiring children and outputting them to PowerShell. Next we are going to add some nested navigation and only allow users to navigate into tags that we know exist.

First some administrative tasks. I decided to remove my custom C# Stackoverflow API that was very minimal and use something more robust. I went with StacMan and added it to the project using the nuget package. I then modified the existing code and views to work as before. I also changed the drive name to SO, to make it easier to type and to make it more relevant. With that out of the way, we can now forge ahead.

The final working code for this post can be found the NestedNavigation tag in the github repo.

To start, I modified the tag acquisition to keep the tags around, so I will only load them once and so I can reuse this code where necessary:

private static StacManResponse<Tag> tags;
private void LoadTags(bool forceReload=false)
{
    if (tags == null || forceReload)
    {
        var stackOverflow = new StacManClient();
        tags = stackOverflow.Tags.GetAll("stackoverflow.com").Result;
    }
}

Next, in my IsItemContainer, instead of blindly returning true, I now check to see if the path sent to us is blank (i.e. at the root) or if the path is a tag in our cache. This allows us to navigate into a valid state:

protected override bool IsItemContainer(string path)
{
    // The path is empty, so we are at the root.  The root is always valid for us.
    if (String.IsNullOrEmpty(path))
        return true;

    LoadTags();

    if (tags.Data == null)
        return false;

    if (tags.Data.Items == null)
        return false;

    var itemFromTag = from tag in tags.Data.Items
        where tag.Name.Equals(path, StringComparison.CurrentCultureIgnoreCase)
        select tag;

    return itemFromTag.Any();   
}

At this point, if you ran the project and started navigating around, you would be able to navigate into first level folders that were valid tags that came back from the API.

Now that I have that working, I am going to modify the GetChildItems call to return questions for the tag folder you are in. To do this, I am going to simply check the path that is sent to the call and if it is non-empty, then query the questions API:

protected override void GetChildItems(string path, bool recurse)
{
    if (string.IsNullOrEmpty(path))
    {
        // Write the tags
        LoadTags();

        foreach (var tag in tags.Data.Items)
        {
            WriteItemObject(tag, tag.Name, false);
        }
    }
    else
    {
        // Write the questions for this tag
        var stackOverflow = new StacManClient();
        var questions = stackOverflow.Questions.GetAll("stackoverflow.com", tagged:path);

        foreach (var q in questions.Result.Data.Items)
        {
            WriteItemObject(q, q.Title, false);
        }
    }
}

At this point questions are returned, but again the output is hideous. Similarly what we did for tags in the last post, we will do for questions. We are going to add a custom view:

<View>
  <Name>QuestionView</Name>
  <ViewSelectedBy>
    <TypeName>StackExchange.StacMan.Question</TypeName>
  </ViewSelectedBy>
  <TableControl>
    <TableHeaders>
      <TableColumnHeader>
        <Label>Title</Label>
        <Width>70</Width>
        <Alignment>left</Alignment>
      </TableColumnHeader>
      <TableColumnHeader>
        <Label>Creator</Label>
        <Width>20</Width>
        <Alignment>left</Alignment>
      </TableColumnHeader>
      <TableColumnHeader>
        <Label>Reputation</Label>
        <Width>10</Width>
        <Alignment>Right</Alignment>
      </TableColumnHeader>
    </TableHeaders>
    <TableRowEntries>
      <TableRowEntry>
        <TableColumnItems>
          <TableColumnItem>
            <PropertyName>Title</PropertyName>
          </TableColumnItem>
          <TableColumnItem>
            <ScriptBlock>$_.Owner.DisplayName</ScriptBlock>
          </TableColumnItem>
          <TableColumnItem>
            <ScriptBlock>$_.Owner.Reputation</ScriptBlock>
          </TableColumnItem>
        </TableColumnItems>
      </TableRowEntry>
    </TableRowEntries>
  </TableControl>
</View>

One interesting thing to call out in this view is that we are using the ScriptBlock element to access nested properties (Owner Name and Reputation) on the question object. So now, if you dir your directory, you will see this:

Title                                         Creator              Reputation
-----                                         -------              ----------
C# Clearing a Table                           user3594691                   1
To list and without tolist                    myfinite                     27
EntitySpaces/C#: How to use a subquery in ... brad                         11
SQL query search for all the data by month... user3337393                   1
Unity control object via socket Udp c#        iboy15                        6
multicolumn combo box in asp.net              nomi ikon                     6
C# How can I start PhantomJS + Selenium wi... Tiago Castro                  1
Override Json Serialization RavenDB           Shea                        101
How to get &quot;Windows Phone Pivot Page&... n179911                    3017
How to write binary data to serial port in C# Stefan Vogel                  1
Can&#39;t get the last row of excel when u... Wen Ling Lo                   1
Position Window before created using shell... Mark Robbins               1113

Since we are in PowerShell, we get a lot of querying functionality for free, so you can try things like:

#questions from people with rep over 100
dir so:\c# | where {$_.Owner.Reputation -gt 100}

#questions with an answer
dir so:\xml | where {$_.AnswerCount -gt 0}

#questions that have a positive score
dir | where {$_.Score -gt 0}    

As you can see combining PowerShell semantics with data from an API can be an interesting way for your users to access your data. In our next post, we will look at tab completion, so we can behave similarly to other drives within a PowerShell environment.

Creating a PowerShell Navigation Provider: #2 Basic GetChildItems() Support

As you saw in the first post in this series, getting a PowerShell provider up and running is pretty easy. Next we are going to add some basic GetChildItems() support and provide a nice experience to your users.

A good starting point for this article is the GetStackOverflowTags tag in the GitHub repo for this project. I have added a basic StackOverflow API at this point that allows me to get a list of tags from StackOverflow ordered by the most popular tags.

After adding the API, I simply updated the GetChildItems() call to consume the API and return the object to the PowerShell window.

protected override void GetChildItems(string path, bool recurse)
{
    var stackOverflow = new StackOverflowAPI.StackOverflow();
    var tags = stackOverflow.GetTags().Result;

    foreach (var tag in tags)
    {
        WriteItemObject(tag, tag.name,true);
    }
}

Now, anytime you call dir, you get output that looks like this:

PSPath            : PowerShellProvider\MyPowerShellProvider::java
PSParentPath      : 
PSChildName       : java
PSDrive           : MyDrive
PSProvider        : PowerShellProvider\MyPowerShellProvider
PSIsContainer     : True
has_synonyms      : True
is_moderator_only : False
is_required       : False
count             : 669099
name              : java

Not very user friendly. You could use various PowerShell constructs to format the output like you want, for example, I could do dir | Format-Table -Property Name,Count to get the output:

name                                                                  count
----                                                                  -----
java                                                                  669099
c#                                                                    663666
javascript                                                            647161
php                                                                   603881
...

Instead of forcing the user to type that every time, we can modify our project to use a PowerShell manifest and a custom formatting file to get our desired output on a dir command. Create a new file called MyPowerShellProvider.psd1 and set its build action to Content and CopyToOutputDirectory to Copy if newer.

Copy the contents of the sample manifest file and update the following lines:

# Script module or binary module file associated with this manifest
RootModule = 'PowerShellProvider.dll'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = 'AE0DA9B1-93DF-4959-9C97-BAA45D5F8EE0'

# Author of this module
Author = 'John Koerner'

# Company or vendor of this module
CompanyName = ''

# Copyright statement for this module
Copyright = 'See the license file for this repo'

Update your InstallProvider.ps1 file to point to the new psd1 file:

Import-Module .\MyPowerShellProvider.psd1

At this point, your provider should still work as before, but now it is loading the information based on the .psd1 file. Next we will create a custom formatting file. I am simply going to modify the table output to use the Name and Count fields. Create a new file named MyViewDefinition.ps1xml and set the Copy to Output Directory to Copy if newer. For the contents of this file, use the following:

<Configuration>
  <ViewDefinitions>
    <View>
      <Name>MyView</Name>
      <ViewSelectedBy>
        <TypeName>StackOverflowAPI.Tag</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Label>Name</Label>
            <Width>25</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
          <TableColumnHeader>
            <Label>Count</Label>
            <Width>10</Width>
            <Alignment>left</Alignment>
          </TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Name</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Count</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

Some of the important bits here are that we set the TypeName node in the ViewSelectedBy node to our type that we are using. This can give us a lot of flexibility if we want different formatting for different types of data. We then set table headers to our desired header information and then one TableColumnItem for each header we defined, telling it from which property it can obtain the value.

Finally we can update the .psd1 file to leverage this formatting, by updating the FormatsToProcess line to include our ps1xml file.

FormatsToProcess = @("MyViewDefinition.ps1xml")

Now when you run dir on MyDrive: you will get the following output:

Name                      Count
----                      -----
java                      671397
c#                        665365
javascript                649489
php                       606076
android                   534244
jquery                    503075
python                    318616
...

At this point you have a drive that can show a single level of depth and the dir output doesn’t look half bad. In the next article, we will really dig into navigation and work on navigating in and out of folders and have valid dir contents at every depth.

Creating a PowerShell Navigation Provider: #1 The Basics

This post will cover the basics of getting your environment setup to create a PowerShell navigation provider. All of the code is available on GitHub. I have created a v0.1 tag which represents the code that will be covered in this post.

  • Create a new Class Library project in Visual Studio.
  • Add a reference to System.Management.Automation from the C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0 directory.

  • Modify your project debug settings to start an external program and start C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

  • Add a CmdletProvider attribute to your class, have it inherit from NavigationCmdletProvider and implement the required members:

    [CmdletProvider("MyPowerShellProvider", ProviderCapabilities.None)]
    public class MyProvider : NavigationCmdletProvider
    {
        protected override bool IsValidPath(string path)
        {
            return true;  // All paths are valid for now
        }
    }
    

The next thing we will do is create a script to install the provider when we start debugging.

  • Add a new file to the project and call it InstallProvider.ps1. This installs our PowerShell module into the current PowerShell session. The contents of the file are:

    Import-Module .\PowerShellProvider.dll
    
  • Set the Copy to Output Directory property to Copy if newer on the file.

  • You can manually run this file every time you start PowerShell or you can update your debug settings and add the following to the command line arguments:

    -NoExit  -File .\InstallProvider.ps1
    

Now we can start implementing the overrides we need to get the provider working. The first method you want to implement is the InitializeDefaultDrives. This method is called when you install the provider and will pre-create drives so users don’t need to explicitly call New-Drive.

protected override Collection<PSDriveInfo> InitializeDefaultDrives()
{
    PSDriveInfo drive = new PSDriveInfo("MyDrive", this.ProviderInfo, "", "", null);
    Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>() {drive};
    return drives;      
}

At this point you can start debugging and if you call Get-PSDrive, you will see the drive, but you will not be able to navigate to it. Let’s fill out a few more overrides, so we can navigate to our provider. To start, we will allow the user to navigate to any folder they type in. We will implement ItemExists and IsItemContainer:

protected override bool ItemExists(string path)
{
    return true;
}

protected override bool IsItemContainer(string path)
{
    return true;
}

Now when you debug, you can navigate into the drive by typing cd MyDrive: and you can change folders to your heart’s content. You’ll notice if you type dir you still get an error. Let’s fix that. Simply override the GetChildItems method:

protected override void GetChildItems(string path, bool recurse)
{
    WriteItemObject("Hello", "Hello", true);
} 

Now, you can navigate around and when you type dir, you will always get back your Hello object. Congratulations, you have a working navigation provider. In the next post in this series we will wire up the provider to a backend API and return custom objects instead of just strings.

The full code for the provider is:

[CmdletProvider("MyPowerShellProvider", ProviderCapabilities.None)]
public class MyPowerShellProvider : NavigationCmdletProvider
{

    protected override bool IsValidPath(string path)
    {
        return true;
    }

    protected override Collection<PSDriveInfo> InitializeDefaultDrives()
    {
        PSDriveInfo drive = new PSDriveInfo("MyDrive", this.ProviderInfo, "", "", null);
        Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>() {drive};
        return drives;
    }

    protected override bool ItemExists(string path)
    {
        return true;
    }

    protected override bool IsItemContainer(string path)
    {
        return true;
    }

    protected override void GetChildItems(string path, bool recurse)
    {
        WriteItemObject("Hello", "Hello", true);
    }
}

You now have the most basic of basic providers available to you. In the next article, I cover the basics of getting GetChildItems() working to return something more real and more readable.

Creating a PowerShell Navigation Provider: Introduction

PowerShell has some really nice ways that it can be extended. One of those ways is via Providers. If you open up PowerShell and call Get-PSDrive you will see a list of the drives on your machine along with some other providers to access things like the registry, certificate stores, and aliases. Using these providers is very similar to navigating a normal drive, for example, I can navigate around the registry by simply typing cd HKCU:. Now I can see items in the registry by typing dir and I see items in my registry (I used dir | Format-Table -Property Name for brevity):

Name
----
HKEY_CURRENT_USER\AppEvents
HKEY_CURRENT_USER\Console
HKEY_CURRENT_USER\Control Panel
HKEY_CURRENT_USER\Environment
HKEY_CURRENT_USER\EUDC
HKEY_CURRENT_USER\Identities
HKEY_CURRENT_USER\Keyboard Layout
HKEY_CURRENT_USER\Network
HKEY_CURRENT_USER\Policies
HKEY_CURRENT_USER\Printers
HKEY_CURRENT_USER\Software
HKEY_CURRENT_USER\System
HKEY_CURRENT_USER\WXP
HKEY_CURRENT_USER\Volatile Environment

Navigating these items is now as easy as using cd to change directories. So cd Software\Microsoft\Windows\CurrentVersion takes me into that node within the registry. From here you can now use other commands to update the registry. As you can see, this is a convenient way to access data in the registry and since it is inside of PowerShell, it can all be scripted.

Creating a basic PowerShell provider is a relatively simple task. Over the next few blog posts, I will walk through the basics of creating a Navigation Cmdlet Provider, so you can add your own custom drive to PowerShell.

Updated Restart Explorer PowerShell Module to Restore Windows

Yesterday I posted a simple PowerShell to restart explorer. I have continued to tinker with it and gave it the ability to find the currently open explorer windows and re-open those after it restarts explorer.

function Restart-Explorer
{
    Param([switch] $SuppressReOpen)

    #Gather up the currently open windows, so we can re-spawn them.
    $x = New-Object -ComObject "Shell.Application"
    $count = $x.Windows().Count
    $windows = @();
    $explorerPath = [System.IO.Path]::Combine($env:windir, "explorer.exe");
    for ($i=0; $i -lt $count; $i++)
    {
        # The location URL contains the Path that the explorer window is currently open to
        $url = $x.Windows().Item($i).LocationURL;

        $fullName = $x.Windows().Item($i).FullName;

        # This also catches IE windows, so I only add items where the full name is %WINDIR%\explorer.exe 
        if ($fullName.Equals($explorerPath))
        {
            $windows += $url
        }
    }

    Stop-Process -ProcessName explorer

    if (!$SuppressReOpen)
    {
        foreach ($window In $windows){

            if ([string]::IsNullOrEmpty($window)){
                Start-Process $explorerPath
            }
            else
            {
                Start-Process $window
            }
        }
    }
}

Set-Alias re Restart-Explorer
Export-ModuleMember -function Restart-Explorer -Alias re

So now when I run re, explorer restarts and my explorer windows are restored. If for some reason I didn’t want to restore the windows I added a SuppressReOpen switch to prevent the script from re-opening explorer windows.

Creating a PowerShell Module to Restart Explorer.exe

I’ve been doing a fair bit of development lately with Shell Extensions and have to constantly restart my explorer.exe process. Quickly, I found that I could do this through powershell using the Stop-Process commandlet.

Stop-Process -ProcessName explorer

This was easy enough to type, but I found myself doing it quite a bit and wanted it to be quicker and easier to remember. I had wanted to learn more about PowerShell modules, so I figure this is as good of a time as any to try it out.

By default PowerShell will look in your %AppProfile%\Documents\WindowsPowerShell\Modules folder for any modules that should be loaded when you start a new shell. (If you want to see all the places it looks, you can look at the $env:PSModulePath variable) From there, each module will have its own folder. So I created a RestartExplorer folder and created a RestartExplorer.psm1 file which contains my module code:

function Restart-Explorer()
{
    Stop-Process -ProcessName explorer
}

Set-alias re Restart-Explorer
Export-ModuleMember -Function Restart-Explorer -Alias re

Now when I type re, explorer restarts for me. A few things to note in this script. The Export-ModuleMember commandlet tells PowerShell which components from this script should be available to the shell. If I did not include the -Alias re then re would not work from the shell window. If you have a lot of functions and aliases you want to export from a module, you could simply export them all using wildcards:

Export-ModuleMember -Function * -Alias *

While this is a simple example, it shows how easy it is to write modules in PowerShell and have them “just work” once you set them up.

Microsoft Reference Source is a Good Friend

With recent versions of .Net, Microsoft has provided reference source to allow you to navigate and debug issues with the .Net framework. The reference source website is a great tool to have in your back pocket when researching how something in the .Net framework works. You can download a full solution (minus some resource files) for use in Visual Studio or you can link directly to a line of code when sending emails or instant messages to colleagues.

Recently I was working with the Managed AddIn Framework, and I was trying to determine how to load settings for an AddIn. I did some reading and really didn’t find much on the subject. I tried creating a .config file for the AddIn and magically it was able to load the settings from there. I don’t like magic in my code, so I wanted to know how the settings were being loaded. I downloaded the reference source and was able to dig into the AddInActivator class and find the calls into the AddInServer. Inside that code, I found that they explicitly load the configuration file based on the AddIn assmebly name:

AppDomain domain;
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = Path.GetDirectoryName(token._addin.Location);
setup.ConfigurationFile = token._addin.Location + ".config";

I then took this information and was able to build up a nice email to my colleagues with links directly to the lines of code that we needed to understand to know how the MAF framework loaded configuration files.

I encourage every .Net developer to get a copy of the reference source on your machine and leverage it when you need to get a better understanding of how the .Net framework works.

Visual Studio 2013 – Paste JSON as Class

There is a really cool new feature in Visual Studio 2013 that lets you paste JSON or XML and create a class hierarchy from it. For example, if you have the following JSON:

{
"People" :[{
    "First Name" : "John",
    "Last Name" : "Koerner", 
    "Address" : {
        "Street" : "1234 Main",
        "City" : "Somewhere",
        "State" : "MI",
        "Zip" : 12345
    }
  }]
}

You can open up a .cs file in VS 2013, and under the Edit->Paste Special menu, you have the option to Paste JSON as Classes, which will create the following class hierarchy:

public class Rootobject
{
    public Person[] People { get; set; }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public int Zip { get; set; }
}

Even though this is a really small feature, it’s just one of those small things that just makes working in visual studio that much easier.

Improving the Performance of the Android Emulator on Windows

This is a collection of tips and tricks I have found that have greatly improved the performance of emulators on windows. When I first loaded the emulator, it took just under 2 minutes to load and get to a point where an app could be started. After making some tweaks, it now takes about 20 seconds.

On the left we have the default ARM setup and on the right we have an x86 emulator setup (at 3x speed):

Android Timings ARM Android Timings x86

The three major factors that contributed to the increase:

  • Using the x86 emulator instead of the ARM emulator
  • Enabling Use Host GPU on the virtual device
  • Ensuring HAX is installed

Using the x86 emulator

To create an x86 emulator, you need to ensure you have downloaded the x86 system image for your android version:

Once that is done, simply create a new emulator using that CPU:

Enabling Use Host GPU

Enabling the host GPU allows your video card to do some of the heavy lifting for display rendering and should give you a good deal of a performance increase. This setting is on the VHD creation dialog:

Enabling HAX

HAX is a hypervisor developed by Intel that was built specifically to improve Android emulation on Intel hardware. You can install this from the Intel website, or run the IntelHaxm executable included with the Android SDK. This can be found at:

extras\intel\Hardware_Accelerated_Execution_Manager

Note that this only works with some intel processors. See the troubleshooting section for more info.

After the install, you can check if the service is working by running sc query intelhaxm. You should see a state of 4 indicating it is running:

$ sc query intelhaxm

SERVICE_NAME: intelhaxm
        TYPE               : 1  KERNEL_DRIVER
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Also, when you start your emulator, you should see a message indicating HAX is working:

Starting emulator for AVD 'Nexus-7GPU'
emulator: device fd:924
HAX is working and emulator runs in fast virt mode

Troubleshooting

When trying to set this up, I did run into a few snags and there are some things to check if you are getting error messages.

Make sure your processor supports HAX

You can check if your processor supports HAX on the Intel Ark website. Select your processor and under the Advanced Technologies section, look for the value IntelĀ® Virtualization Technology (VT-x) and ensure it is set to yes.

Verify VT-x is enabled in the BIOS

If your processor supports VT-x there may be a setting in the BIOS that enables or disables the feature. Verify that this setting is set to enabled.

Disable any other hypervisors

I found that on my Windows 8 installation Hyper-V was installed and this was interfering with HAX. You have to disable the feature (not just disable the services) in order to work around this. To disable the feature, open Programs and Features, select Turn Windows features on or off and uncheck Hyper-V.

Stop ignoring an update in IntelliJ

Occasionally IntelliJ pops up the update dialog asking me to update to the latest versoin:

Usually I’ll just click Remind Me Later and continue on my way, but this time I clicked the Ignore This Update button. Now anytime I go to check for updates, the event log states:

Update Info
You already have the latest version of IntelliJ IDEA installed.
To configure automatic update settings, see the Updates dialog of your IDE settings

After some digging, I found a post from Dmitry Jemerov on the Jet Brains support forum which stated:

In the current build, you need to edit the settings manually (under in other.xml).

That’s good info, unfortunately it doesn’t provide any information on the location of other.xml. After a quick check of the normal locations, I was able to find other.xml at %userprofile%\.IdeaIC12\config\options.

Once in that file you need to search for myIgnoredBuildNumbers and simply clear out the content, so it looks like this:

<option name="myIgnoredBuildNumbers">
</option>

Now IntelliJ will notify you of the update again and you can keep on telling it to remind you later.