0

I have the following C# code for calculating each file's hash in a certain, user specified directory. The point is that it works fine, until it encounters a file that it cannot access. When it finds something like this, it just throws an error message and exits the program. What I want it instead to do is, throw an error message with the name of the file that cannot be accessed, write that there is an error in accessing that file, and continue executing the program with the other files in the directory. If someone can help me edit my code and achieve those things I would be glad.

 private void SHA256Directory(string directory) { try { SHA256 DirectorySHA256 = SHA256Managed.Create(); byte[] hashValue; DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); foreach (FileInfo fInfo in files) { FileStream fStream = fInfo.Open(FileMode.Open); fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); fStream.Close(); } return; } catch(DirectoryNotFoundException) { Console.WriteLine("Error: The directory specified could not be found."); } catch(IOException) { Console.WriteLine("Error: A file in the directory could not be accessed."); } catch(ArgumentNullException) { Console.WriteLine("Error: The argument cannot be null or empty."); } } 

8 Answers 8

8

Move your try/catch inside the foreach. You haven't explained in your post, but I'm guessing that's where you encounter the exception.

In doing so, any exception caused by the code in there will be caught and allow the loop to continue.

Careful, though -- these two lines are still not exception-safe:

DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); 

You'll want to account for that as well.

If you want it to show what exactly what file/directory caused the issue, just toString the exception, for example:

catch(DirectoryNotFoundException ex) { Console.WriteLine("Error: The directory specified could not be found: " + ex.toString()); } 

If toString doesn't give you the desired output, try ex.Message. I always just use toString though.

EDIT credit to Ken Henderson

When using any kind of Stream, you should put it in a using block. The garbage collector will Close the stream eventually, but its good practice to do this, as a using block will close the stream as soon as you're done using it:

using (FileStream fStream = fInfo.Open(FileMode.Open)) { fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); } // No need for fStream.Close() any more, the using block will take care of it for you 
Sign up to request clarification or add additional context in comments.

1 Comment

didn't think this was worthy of another answer, but don't forget that you should either have a using statement when opening the stream or a finally block for the try catch blocks to ensure the stream is closed/disposed
2

You should reorganize your code like this:

private void SHA256Directory(string directory) { try { DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); foreach (FileInfo fInfo in files) { try { SHA256 DirectorySHA256 = SHA256Managed.Create(); byte[] hashValue; FileStream fStream = fInfo.Open(FileMode.Open); fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); fStream.Close(); } catch (...) { // Handle other exceptions here. Through finfo, you can // access the file name } } } catch (...) { // Handle directory/file iteration exceptions here } } 

Comments

1

Scope is the keyword here.

Your try catch surrounds the entire foreach. This means that when there is an error, it will exit out of the foreach. You want to have the try-catch closer to the point of origin (that being fInfo.Open(FileMode.Open)). That way, after an error it can just continue processing the loop.

Comments

1

Try this instead:

private void SHA256Directory(string directory) { SHA256 DirectorySHA256 = SHA256Managed.Create(); byte[] hashValue; DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); foreach (FileInfo fInfo in files) { try { FileStream fStream = fInfo.Open(FileMode.Open); fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); fStream.Close(); } catch(DirectoryNotFoundException) { Console.WriteLine("Error: The directory specified could not be found."); } catch(IOException) { Console.WriteLine("Error: A file in the directory could not be accessed."); } catch(ArgumentNullException) { Console.WriteLine("Error: The argument cannot be null or empty."); } } return; } } 

3 Comments

Well, there could be exceptions in dir.GetFiles that you're not catching right now.
This is true. I assumed he was more concerned with handling errors when he could not access files. I would choose your solution over mine for that reason.
put inner try / catch (designed to catch error from opening file), around only the file.open
0

You should also handle the UnauthorizedAccessException which is thrown if file is not accessible.

Comments

0

Might be I'm overseeing something, because the solution is rather simple, but;

place the Try-Catch block dealing with the access problems inside the for each - in case one file is not accessible, the exception is thrown, catched and after printing the error message the foreach is continued with the next file in the list.

private void SHA256Directory(string directory) { try { SHA256 DirectorySHA256 = SHA256Managed.Create(); byte[] hashValue; DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); foreach (FileInfo fInfo in files) { try { FileStream fStream = fInfo.Open(FileMode.Open); fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); fStream.Close(); } catch(IOException) { Console.WriteLine("Error: A file in the directory could not be accessed."); } } return; } catch(DirectoryNotFoundException) { Console.WriteLine("Error: The directory specified could not be found."); } catch(ArgumentNullException) { Console.WriteLine("Error: The argument cannot be null or empty."); } } 

Comments

0

To know which file is not accessible you could use the following snippet :

catch(FileNotFoundException ex) { Console.writeLine("File not found " + ex.FileName); } 

Comments

0

handle UnauthorizedAccessException and put try statement in foreach statement.

private void SHA256Directory(string directory) { SHA256 DirectorySHA256 = SHA256Managed.Create(); byte[] hashValue; DirectoryInfo dir = new DirectoryInfo(directory); FileInfo[] files = dir.GetFiles(); foreach (FileInfo fInfo in files) { try { FileStream fStream = fInfo.Open(FileMode.Open); fStream.Position = 0; hashValue = DirectorySHA256.ComputeHash(fStream); Console.WriteLine(fInfo.Name); Miscellaneous.ByteArrayToHex(hashValue); Miscellaneous.ByteArrayToBase64(hashValue); Console.WriteLine(); fStream.Close(); } catch (DirectoryNotFoundException) { Console.WriteLine("Error: The directory specified could not be found."); } catch (UnauthorizedAccessException) { Console.WriteLine("Error: A file in the directory could not be accessed.in {0}", fInfo.Name); } catch (ArgumentNullException) { Console.WriteLine("Error: The argument cannot be null or empty."); } catch (IOException) { Console.WriteLine("Error:IOExcepiton occured"); } } return; } 

Comments