C# – Creating a Shell Extension for Review Board

Recently at work we’ve started using Review Board to request and complete code reviews. This has been a nice way to replace our formerly informal code review process of calling someone over to your desk to review.

Along with Review Board we’ve decided to use a tool called “post-review”. This is a command line tool to easily publish a review to Review Board.

The only problem with this is that as much as I like command line tools, I’m never in the command line. I don’t have a memory for switches, and because the command line is never open I’ve got to go through the process of opening the command line first. All of this seemed like just a little too much hassle for me.

So what’s the solution? The obvious solution to me was to create a shell extension. The desired behaviour is that I can right-click a folder in Windows Explorer and click “Code Review”. This will pop open a small window allowing me to input a summary and click Submit to post my review. Easy peasy.

How is this accomplished? Well, there are two pieces to this:

  • The first is to create entries in the registry that will add a new option to the context menu when the user right-clicks on a folder.
  • The second is to create a WPF application that accepts a parameter from the operating system that will define the location of the folder clicked.

Part One – Updating the Registry:

static void Main(string[] args)
{
    var menuName = @"Folder\shell\NewMenuOption";
    var command = @"Folder\shell\NewMenuOption\command";

    RegistryKey regmenu = null;
    RegistryKey regcmd = null;

    try
    {
        regmenu = Registry.ClassesRoot.CreateSubKey(menuName);
        if (regmenu != null)
            regmenu.SetValue("", "Code Review");
        regcmd = Registry.ClassesRoot.CreateSubKey(command);
        if (regcmd != null)
            regcmd.SetValue("", "C:\\Code Review\\ContextCodeReview.exe \"%1\"");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed.");
        Console.WriteLine(ex.ToString());
        Console.ReadLine();
    }
    finally
    {
        if (regmenu != null)
            regmenu.Close();
        if (regcmd != null)
            regcmd.Close();
    }
}

So here we are creating a new listing under Folder that is going to have the name Code Review.

The next value being set is the link to the program — in this case it is called ContextCodeReview.exe along with “%1” which will include the path to the folder clicked on when this option is invoked. This will be passed into the program as a string parameter.

We make sure to close the registry, and if there is an error it is logged to the console.

Surprised at how easy this is so far? You should also note that I put this into a program just to make it easy to be run by other team members. It is just as easy to go into the registry and add the necessary keys yourself.

The next part is also pretty easy. We just need a basic desktop application. In this case we’re using C# and WPF.

Part Two: Implementing the Windows App:

Here is the XAML for the app.

<Window x:Class="ContextCodeReview.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        FocusManager.FocusedElement="{Binding ElementName=Summary}"
        Title="Code Review" Height="215" Width="305">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <StackPanel Orientation="Vertical">
                    <Label Margin="5" Height="25">Summary:</Label>
                </StackPanel>
                <StackPanel Orientation="Vertical">
                    <TextBox Name="Summary" Width="145" Height="25" Margin="5"></TextBox>
                </StackPanel>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button Name="SubmitBtn" Click="SubmitBtn_Click" Width="275" Margin="5">Submit</Button>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

And the code-behind:

public partial class MainWindow : Window
{
    private string _path;

    public MainWindow()
    {
        InitializeComponent();
        _path = Environment.GetCommandLineArgs()[1];
    }

    private void SubmitBtn_Click(object sender, RoutedEventArgs e)
    {
        var process = new System.Diagnostics.Process();
        var startInfo = new System.Diagnostics.ProcessStartInfo();
        startInfo.FileName = "post-review";
        startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
        startInfo.Arguments = String.Format("-p --summary=\"{0}\" --target-groups=WebApps", Summary.Text);
        
        startInfo.WorkingDirectory = String.Format("{0}", _path);
        startInfo.UseShellExecute = false;
        startInfo.RedirectStandardOutput = true;
        process.StartInfo = startInfo;
        process.Start();

        string output = process.StandardOutput.ReadToEnd();

        string messageBoxText = output;
        string caption = "Result";
        MessageBoxButton mbButton = MessageBoxButton.OK;
        MessageBoxImage icon = MessageBoxImage.Information;

        MessageBox.Show(messageBoxText, caption, mbButton, icon);

        Application.Current.Shutdown();
        return;
    }
}

As you can see, it’s just a button and a text box, so I didn’t feel the need to break out MVVM or anything. It couldn’t have been easier. Once the button is click, the click handler reads the text box and inserts the contents into the summary field and publishes the review to a hard coded group.

Now, since then I’ve experienced a bit of scope creep and now there’s the ability to select your target group, publish a specific committed revision, update an existing review, and soon to be a description box. So I’ve since refactored into MVVM, but I think that’s the way of most tools people find useful.

I might end up putting this one in its current version up on github, but not right now.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s