*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:
ContextMenuStrip _menu;
ToolStripMenuItem _binaryCompareItem;
ToolStripMenuItem _textCompareItem;
ToolStripMenuItem _binaryCompare2Items;
ToolStripMenuItem _textCompare2Items;
ToolStripMenuItem _md5Item;
ToolStripMenuItem _sha1Item;
ToolStripMenuItem _sha256Item;
ToolStripMenuItem _aboutItem;
ToolStripMenuItem _rootItem;
protected override ContextMenuStrip CreateMenu()
{
_menu = new ContextMenuStrip();
_rootItem = new ToolStripMenuItem
{
Text = "Compare&&Hash",
Image = FileCompareByHash.Resource.compare22
};
_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);
_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);
_menu.Items.Add(_rootItem);
if (_menu != null)
{
UpdateMenu();
}
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);
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:
static class Levensthein
{
public static void Reset()
{
_costMatrix = null;
}
static ushort[,] _costMatrix = null;
static public ushort LD(string s, string t)
{
ushort n = (ushort)s.Length;
ushort m = (ushort)t.Length;
if (_costMatrix == null || _costMatrix.GetUpperBound(0) < n || _costMatrix.GetUpperBound(1) < m)
_costMatrix = new ushort[n + 1, m + 1];
ushort cost;
if (n == 0) return m;
if (m == 0) return n;
for (ushort i = 0; i <= n; _costMatrix[i, 0] = i++) ;
for (ushort j = 0; j <= m; _costMatrix[0, j] = j++) ;
for (ushort i = 1; i <= n; i++)
{
for (ushort j = 1; j <= m; j++)
{
if (s[i - 1] == t[j - 1])
cost = 0;
else
cost = 1;
_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);
}
}
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.
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.
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.
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.