Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#3.5

File Compare And Hash extension for Windows explorer

4.97/5 (18 votes)
4 Aug 2015MIT4 min read 22K   973  
A windows explorer extension for easy binary/text file comparisons and calculating the MD5, SHA1, SHA256 hashes.

Image 1

*I recommend that you download the latest code and binaries from codeplex TFS.

Introduction

This is a shell extension for windows explorer allowing for easy binary or text file comparison
as well as calculating the MD5, SHA1, SHA256 from the selected files.

Background

File comparison and integrity tools have been around for a long time. Even MS-DOS has one (just type fc /?to a DOS command prompt), but using them was not very user friendly.
Windows explorer allows the use of COM objects  that implement certain interfaces as extensions and that integration usually comes in very handy.
.Net and some libraries like Dave Kerr's tools made it a lot easier to write shell extensions.
Also there are some nice text diff tools like Christopher Erker's DeltaScope that were missing the  shell integration, so I put these pieces together, added some of my own ideas like Levensthein distance and created this extension meant to be easy to use and to provide useful features at the same time.

Using the code

The code for the Windows explorer integration is relying heavily on the sharpshell project. To give you an idea about how to add additional item to windows explorer's context menu, here is a code snippet for extending the SharpContextMenu class:

C#
ContextMenuStrip _menu;
ToolStripMenuItem _binaryCompareItem;
ToolStripMenuItem _textCompareItem;
ToolStripMenuItem _binaryCompare2Items;
ToolStripMenuItem _textCompare2Items;
ToolStripMenuItem _md5Item;
ToolStripMenuItem _sha1Item;
ToolStripMenuItem _sha256Item;
ToolStripMenuItem _aboutItem;
ToolStripMenuItem _rootItem;
/// <summary>
/// Creates the context menu. This can be a single menu item or a tree of them.
/// </summary>
/// <returns>
/// The context menu for the shell context menu.
/// </returns>
protected override ContextMenuStrip CreateMenu()
{

    //  Create the menu strip.
    _menu = new ContextMenuStrip();

    //  Create a menu item to hold all of the subitems.
    _rootItem = new ToolStripMenuItem
    {
        Text = "Compare&&Hash",
        Image = FileCompareByHash.Resource.compare22

    };

    //  Now add the child items.
    _binaryCompareItem = new ToolStripMenuItem{Text = "Binary Compare with...",};
    _binaryCompareItem.Click += (sender, args) => CompareAgainst(sender as ToolStripMenuItem);

    _textCompareItem = new ToolStripMenuItem { Text = "Text Compare with..." };
    _textCompareItem.Click += (sender, args) => CompareAgainst(sender as ToolStripMenuItem);


    _binaryCompare2Items = new ToolStripMenuItem { Text = "Compare binary files...", };
    _binaryCompare2Items.Click += (sender, args) => ShowComparison(true);

    _textCompare2Items = new ToolStripMenuItem { Text = "Compare text files...", };
    _textCompare2Items.Click += (sender, args) => ShowComparison(false);

    _md5Item = new ToolStripMenuItem{Text = "Calculate MD5",};
    _md5Item.Click += (sender, args) => ShowHash<MD5CryptoServiceProvider>();

    _sha1Item = new ToolStripMenuItem{Text = "Calculate SHA1",};
    _sha1Item.Click += (sender, args) => ShowHash<SHA1CryptoServiceProvider>();

    _sha256Item = new ToolStripMenuItem{Text = "Calculate SHA256",};
    _sha256Item.Click += (sender, args) => ShowHash<SHA256CryptoServiceProvider>();

    _aboutItem = new ToolStripMenuItem{Text = "About...",};
    _aboutItem.Click += (sender, args) => About(this);

    //  Add all the items.
    _rootItem.DropDownItems.Add(_binaryCompareItem);
    _rootItem.DropDownItems.Add(_textCompareItem);
    _rootItem.DropDownItems.Add(_binaryCompare2Items);
    _rootItem.DropDownItems.Add(_textCompare2Items);
    _rootItem.DropDownItems.Add(new ToolStripSeparator());
    _rootItem.DropDownItems.Add(_md5Item);
    _rootItem.DropDownItems.Add(_sha1Item);
    _rootItem.DropDownItems.Add(_sha256Item);
    _rootItem.DropDownItems.Add(new ToolStripSeparator());
    _rootItem.DropDownItems.Add(_aboutItem);
    //  Add the item to the context menu.
    _menu.Items.Add(_rootItem);

    if (_menu != null)
    {
        UpdateMenu();
    }
    //  Return the menu.
    return _menu;
}
private void UpdateMenu()
{
    int itemcnt = SelectedItemPaths.Count();
    switch (itemcnt)
    {
        case 1:
            _rootItem.DropDownItems.Remove(_textCompare2Items);
            _rootItem.DropDownItems.Remove(_binaryCompare2Items);
            break;
        case 2:
            _rootItem.DropDownItems.Remove(_textCompareItem);
            _rootItem.DropDownItems.Remove(_binaryCompareItem);
            break;
        default:
            _rootItem.DropDownItems.Remove(_textCompare2Items);
            _rootItem.DropDownItems.Remove(_binaryCompare2Items);
            _rootItem.DropDownItems.Remove(_textCompareItem);
            _rootItem.DropDownItems.Remove(_binaryCompareItem);
            _rootItem.DropDownItems.RemoveAt(0);//the separator
            break;
    }
}

 

The inner working of SharpShell's are out of scope for this article and I encourage you to visit the article at the link provided. The other big piece of software I borrowed from codeproject (with minor changes) deals with displaying the file differences and is also out of scope of this article.
One of my notable additions is the file similarity using the Levenshtein distance for text file comparison.
I have used it just to give you an idea of how much the text contents are related. It should work decently on text files of  small sizes.
Below is the code I used to calculate it:

C#
static class Levensthein
{
    //*****************************
    // Compute Levenshtein distance
    //*****************************
    public static void Reset()
    {
        _costMatrix = null;
    }
    static ushort[,] _costMatrix = null;
    static public ushort LD(string s, string t)
    {
        ushort n = (ushort)s.Length; //length of s
        ushort m = (ushort)t.Length; //length of t
        //reallocate only if necessary
        if (_costMatrix == null || _costMatrix.GetUpperBound(0) < n || _costMatrix.GetUpperBound(1) < m)
            _costMatrix = new ushort[n + 1, m + 1]; // matrix
        ushort cost; // cost
        // Step 1
        if (n == 0) return m;
        if (m == 0) return n;
        // Step 2
        for (ushort i = 0; i <= n; _costMatrix[i, 0] = i++) ;
        for (ushort j = 0; j <= m; _costMatrix[0, j] = j++) ;
        // Step 3
        for (ushort i = 1; i <= n; i++)
        {
            //Step 4
            for (ushort j = 1; j <= m; j++)
            {
                // Step 5
                if (s[i - 1] == t[j - 1])
                    cost = 0;
                else
                    cost = 1;
                // Step 6
                _costMatrix[i, j] = (ushort)System.Math.Min(System.Math.Min(_costMatrix[i - 1, j] + 1, _costMatrix[i, j - 1] + 1),
                            _costMatrix[i - 1, j - 1] + cost);
            }
        }
        // Step 7
        return _costMatrix[n, m];
    }
}

Using the Explorer extension

Comparing two text files

As the top image shows, to compare 2 text files you highlight them, right click on the selection and from the pop up menu navigate to "Compare&Hash"->"Compare text files..." or "Compare binary files...".
If the files were on different folders or drives, you have an extra step as seen below.

Comparing two files in different folders

When the files to compare are in different directories, select the first file, right click on it and select "Text Compare with..." or "Binary Compare with...". A file dialog will appear in order to select the file to compare to. Select the second file and click "Open". Depending on your selection, a message box will show the text differences or will show you if the files were identical.
You have the option to continue checking the differences between the selected files like size, modified time or calculate specific  hashes for them.

Image 2

Getting the hash for a single file

Right click on the file, select the hashing algorithm and you will have the option to copy the specific hash to the clipboard.

Image 3

Getting the hash for multiple files in the same folder

If all files to be hashed were in the same folder, you can select them, right click and choose the hashing algorithm. A message box with the selected files and their respective hashes will appear as seen below.

Image 4

Points of Interest

You can download the whole source code to rebuild my project for this extension and make the installation script, but anything related with SharpShell is present only as binaries (again see the provided links for code and more info).
There is also a download consisting of a zip with an install/uninstall script for your convenience that contains only binaries.
Before you use it, you should be aware that there is a debate about hosting the CLR in Windows explorer as this code does, that has not arrived to a definitive conclusion.
The bottom line is that as long as you use only .net 4.0 or later, you are most likely safe. I have tested this code on Windows 7 and had no issue.

History

August 2015: Released version 1.2.

If you want to be sure you are getting only the latest, the most up to date code and binaries are on codeplex.

License

This article, along with any associated source code and files, is licensed under The MIT License