Introduction
FileHasher is a tool to hash files recursively across a selected folder. This is my first attempt to write a useful tool in C# apart from the initial "Hello Worlds". It uses the SHA512Managed
class to derive the hash from a file's open stream.
The article has example code for:
- Threading
- Delegates and Events
- Hashing
Implementation
The core functionality in this is the GetHashCode(FileInfo file)
method which retrieves the hash code for a given file. To retrieve the hash, we need to convert the FileStream
into a byte array which is then passed on to the SHA512Managed.ComputeHash
method which returns a byte array containing the hash.
try
{
BinaryReader br = new BinaryReader(file.OpenRead()) ;
bHash = mSha512.ComputeHash(br.ReadBytes((int)file.Length)) ;
br.Close() ;
sHash = Convert.ToBase64String(bHash) ;
}
This hash is in a non-printable form, so we now encode it using Convert.ToBase64String
so that we can show it in the UI and save it into a file. The above method is called off a recursive method which loops through all the sub folders in the user selected folder.
private void Recurse()
{
try
{
DirectoryInfo [] dirs ;
string sHash ;
DirectoryInfo dirInfo = new DirectoryInfo(msPathName) ;
FileInfo [] files ;
files = dirInfo.GetFiles("*.*") ;
foreach(FileInfo f in files)
{
if(f.FullName != "." || f.FullName != "..")
{
sHash = "" ;
if(mbAddFiles)
{
StatusUpdate(f.FullName) ;
sHash = GetHashCode(f) ;
}
mlFileCount++ ;
ProgressCallBack(mlFileCount,f.FullName,
sHash, f.Length);
}
}
dirs = dirInfo.GetDirectories("*.*") ;
foreach(DirectoryInfo dir in dirs)
{
msPathName = dir.FullName ;
Recurse() ;
}
}
catch(Exception ex)
{
throw ex ;
}
}
This is the point where it becomes interesting. It is really a CPU intensive job to read all the files and then compute the hash for that. If you run this off a UI code directly you will get the job done, but your UI will be un-responsive.
To ensure that the UI is responsive, the recursive method is run off a thread. This way the main thread in the application is free to take care of the UI and the "worker thread" is building the hashes.
mThread = new System.Threading.Thread(new
System.Threading.ThreadStart(fileAdder.HashFiles)) ;
mThread.Start();
The requirement for a good UI becomes more challenging at this point. We need a UI which is not only responsive but shows real-time data of what the thread is doing. To enable that, we have to implement callbacks so that the UI can be updated. But the problem here is that the UI is on a thread which is different than the "worker thread". Cross thread data exchanges are not straightforward.
For that reason, you will see four delegates declared in FileAdder.cs.
#region "Delegates"
public delegate void ProgressCallBackHandler(int lPos,
string sFile,string sHash,double dSize) ;
public delegate void HashCompleteCallBackHandler() ;
public delegate void UpdateTreeCallBackHandler(string sFile,string sHash) ;
public delegate void UpdateStatusCallBackHandler(string sText) ;
#endregion
These are raised as events in the Recurse
method and when the operation is complete.
public event ProgressCallBackHandler ProgressCallBack ;
public event HashCompleteCallBackHandler HashCallBack ;
public event UpdateStatusCallBackHandler StatusUpdate ;
The MainForm
, which is the UI class, locks onto these events for notifications. This scenario works fine with textboxes and progress bars.
But for a tree view, we need an extension to this. We need to call the Invoke
method to enable the tree view class to update the UI from across thread data.
tvOne.Invoke(new UpdateTreeCallBackHandler(this.UpdateTree),
new object[]{sFile,sHash}) ;
Since I had a setup for the thread working, I have added other cosmetic features like "Abort", "Pause" ...
Recommended Reads
fooling around computers during my college days, now into full fledged development in Microsoft Technology in a big company.
check out my site for some small programs I have written.