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.");
}
}
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
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
}
}
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.
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;
}
}
You should also handle the UnauthorizedAccessException which is thrown if file is not accessible.
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.");
}
}
To know which file is not accessible you could use the following snippet :
catch(FileNotFoundException ex)
{
Console.writeLine("File not found " + ex.FileName);
}
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;
}
Related
I'm working in C# and I try to do a program that get some infoes the files in a Directory. I made it but i have a problem with the error Handling. When the program runs and for example I give just random numbers to list file infoes i get this error message:
"System.IO.DirectoryNotFoundException: "'Could not find a part of the path 'C:\Temp\first_project\first_project\bin\Debug\12345'.'"
Please someone help me to do the error handling.
Thank you in advance.
using System;
using System.IO;
class Test
{
static void Main(string[] args)
{
Console.WriteLine("Please :");
string hely = Console.ReadLine();
string[] __file = Directory.GetFiles(hely);
string[] __dir = Directory.GetDirectories(hely);
foreach (string i in __file)
{
FileInfo fajl = new FileInfo(i);
Console.WriteLine("{0},{1},{2}", fajl.Name, fajl.Extension, fajl.LastWriteTime.ToString());
}
foreach (string i in __dir)
{
DirectoryInfo _file = new DirectoryInfo(i);
Console.WriteLine("{0},{1},{2}", _file.Name, _file.Extension, _file.LastWriteTime.ToString());
}
Console.ReadKey();
}
}
You should check existence of a path with
System.IO.Directory.Exists(directory)
and of a file with
System.IO.File.Exists(filePath)
Then, you need to take the try-catch block inside the for-loop, to catch any possible exceptions that occur because of insufficient rights/permissions.
e.g.
foreach (string i in __file)
{
try
{
FileInfo fajl = new FileInfo(i);
Console.WriteLine("{0},{1},{2}", fajl.Name, fajl.Extension, fajl.LastWriteTime.ToString());
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
You could also create two try-catch blocks - depends on what you want to do.
try
{
foreach (string i in __file)
{
try
{
FileInfo fajl = new FileInfo(i);
Console.WriteLine("{0},{1},{2}", fajl.Name, fajl.Extension, fajl.LastWriteTime.ToString());
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
}
}
catch (System.Exception exLoop)
{
System.Console.WriteLine(exLoop.Message);
throw;
}
Note that in your example, you should first check if the directory "hely" exists:
if (!System.IO.Directory.Exists(hely))
{
System.Console.Error.WriteLine("Directory \"{0}\" does not exist.", hely);
System.Environment.Exit(1);
// or: return;
}
Since exception handling is usually very slow, I would however recommend that you check for the existence of the file/directory explicitly. It would also be a good idea to do so for the file/directory-listing & read-access rights for the respective user. But even if you do so, keep the try-catch, because there might be cases where your program suddenly fails - e.g. when a removable storage is forcefully removed.
Use try catch
using System;
using System.IO;
class Test
{
static void Main(string[] args)
{
Console.WriteLine("Please :");
string hely = Console.ReadLine();
try
{
string[] __file = Directory.GetFiles(hely);
string[] __dir = Directory.GetDirectories(hely);
foreach (string i in __file)
{
FileInfo fajl = new FileInfo(i);
Console.WriteLine("{0},{1},{2}", fajl.Name, fajl.Extension, fajl.LastWriteTime.ToString());
}
foreach (string i in __dir)
{
DirectoryInfo _file = new DirectoryInfo(i);
Console.WriteLine("{0},{1},{2}", _file.Name, _file.Extension, _file.LastWriteTime.ToString());
}
}
catch(System.IO.DirectoryNotFoundException ex)
{
Console.WriteLine("Directory not found");
}
Console.ReadKey();
}
}
You can check if the file exists
foreach (string i in __file)
{
if (File.Exists(i))
{
FileInfo fajl = new FileInfo(i);
Console.WriteLine("{0},{1},{2}", fajl.Name, fajl.Extension, fajl.LastWriteTime.ToString());
}
}
RTFM?
Read Directory.GetFiles method
It says that you will get the DirectoryNotfound exception if the specified path is not found. Obviously folder 'C:\Temp\first_project\first_project\bin\Debug\12345' does not exist.
Proper code would be:
string hely = ...
try
{
string[] files = Directory.GetFiles(hely);
ProcessFiles(files);
}
catch (DirectoryNotFoundException exc)
{
Console.WriteLine(exc.Message);
}
If you don't know how to react on exceptions read MSDN about exception handling
List<string> errorLog = new List<string>();
foreach (DirectoryInfo dir in directories)
{
try
{
dir.Delete(true);
}
catch (System.IO.IOException msg)
{
code = 5;
errorLog.Add(String.Concat(dir.FullName, " ", msg.Message));
Console.WriteLine("Error Removing the directory: {0}", dir.FullName);
}
}
I have a for each loop that will go through a list of directories and remove them, but keep the parent directory. Should an error occur, I would like to log it. I created a list and in the catch add the errors. At the end, I can check the length of errorLog list and if it's more than zero, I can print them. I've seen posts where they call using and streamwriter within the catch, but what happens if something were to occur while writing the error log?
Is what I'm doing considered bad practice? If so, what should I do ?
I think you've got the right idea. There are many solutions, but recently I tried rerouting the console output to file and it worked pretty well. Regarding your solution, it would look like:
try
{
FileStream oStream;
StreamWriter sWriter;
var oldOut = Console.Out;
var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
const string outputFileName = "\\errorlog.txt";
var fullPath = string.Concat(desktopPath, outputFileName);
Console.SetOut(sWriter);
foreach (DirectoryInfo dir in directories)
{
try
{
dir.Delete(true);
}
catch(System.IO.IOException msg)
{
code = 5;
errorLog.Add(String.Concat(dir.FullName," ",msg.Message));
Console.WriteLine("Error Removing the directory: {0}", dir.FullName);
}
}
}
catch(Exception e)
{
//handle error with streams or file
}
finally
{
//ensures that we close the connections and such
Console.SetOut(oldOut);
sWriter.Close();
oStream.Close();
}
The finally block ensures that if any unhandled thing happens, the stream and file will still be closed.
In form1 i have two buttons one to select files from directory single file or multiple files.
The second button is to select files from a directory to get all the files in a selected directory.
Now i have a class i'm using to upload the files/directories to my ftp:
At the top of the class i did:
public static DirectoryInfo d;
public static string[] files;
private FileInfo[] dirflist;
Then i'm using it in the event:
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
try
{
dirflist = d.GetFiles();
//if (dirflist.Length > 0)
//{
foreach (string txf in files)
{
string fn = txf;//txf.Name;
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
string UploadPath = String.Format("{0}/{1}{2}", f.Host, f.TargetFolder == "" ? "" : f.TargetFolder + "/", Path.GetFileName(fn));//f.SourceFile));
if (!UploadPath.ToLower().StartsWith("ftp://"))
UploadPath = "ftp://" + UploadPath;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(UploadPath);
request.UseBinary = true;
request.UsePassive = f.Passive;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Timeout = 300000;
request.Credentials = new NetworkCredential(f.Username, f.Password);
long FileSize = new FileInfo(f.SourceFile).Length;
string FileSizeDescription = GetFileSize(FileSize);
int ChunkSize = 4096, NumRetries = 0, MaxRetries = 50;
long SentBytes = 0;
byte[] Buffer = new byte[ChunkSize];
using (Stream requestStream = request.GetRequestStream())
{
using (FileStream fs = File.Open(f.SourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
int BytesRead = fs.Read(Buffer, 0, ChunkSize);
while (BytesRead > 0)
{
try
{
if (bw.CancellationPending)
return;
requestStream.Write(Buffer, 0, BytesRead);
SentBytes += BytesRead;
string SummaryText = String.Format("Transferred {0} / {1}", GetFileSize(SentBytes), FileSizeDescription);
bw.ReportProgress((int)(((decimal)SentBytes / (decimal)FileSize) * 100), SummaryText);
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex.ToString());
if (NumRetries++ < MaxRetries)
{
fs.Position -= BytesRead;
}
else
{
throw new Exception(String.Format("Error occurred during upload, too many retries. \n{0}", ex.ToString()));
}
}
BytesRead = fs.Read(Buffer, 0, ChunkSize);
}
}
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
System.Diagnostics.Debug.WriteLine(String.Format("Upload File Complete, status {0}", response.StatusDescription));
}
//}
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
ConnectionError = "Error: Please check the ftp address";
break;
case WebExceptionStatus.Timeout:
ConnectionError = "Error: Timout Request";
break;
}
}
}
Now i'm doing loop over the string[] array.
Since i'm selecting multiple files only.
But there might be a case i will select a directory. And then i will need to use the DirectoryInfo(d variable) and the FileInfo[]
If i'm using the FileInfo[] then it's like that:
dirflist = d.GetFiles();
if (dirflist.Length > 0)
{
foreach (FileInfo txf in dirfilist)
{
string fn = txf.Name;
But i don't want to copy over all the code again just for string[] or just for FileInfo[]
I want to make something that i will be able to use FileInfo[] with the foreach or the string[] in the foreach.
And maybe sometimes i will use both upload multiple files and then also to upload a directory with all the files inside.
So maybe it's better to duplicate the whole code and making using once string[] and once FileInfo[] ?
I mean to make two methods one will use FileInfo[] one string[]
How can i use if needed the FileInfo[] or if needed the string[] ?
private void SomeMethod(args)
{
// ...
/* Here I need a specific String Value, or Array of String Values
but sometimes I got it from an array of File,
and sometimes from an array of FileInfo... */
// Call a Function that always returns an array of String
files = GetMyFiles(args);
// resume the job using only files...
/* or replace the above that always manipulates an arrays of FileInfo-s
if you must use FileInfo-s */
}
Then you can overload your function GetMyFiles by passing any argument you want.
string[] GetMyFiles(String DirectoryPath)
// Returns an Array of String that contains all the Files in the Directory.
string[] GetMyFiles(FileInfo MyFileInfo)
// Returns an Array of String with just one File Path.
string[] GetMyFiles()
// Opens a MultiSelect OpenFileDialog,
// then returns the selected Files Path in an Array (or empty Array)
// ...
The other way : Slice your code in multiple parts, then decide which part you're going to use with a conditional check...
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
// Do the maximum you can do here...
// ...
if ImGoingToUseStringArray
{
string[] files = ....
ResumeWithStringArray(files, sender, e);
}
else
{
FileInfo[] dirflist = ....
ResumeWithFileInfo(dirfList, sender, e);
}
}
private void ResumeWithStringArray(string[] files, object sender, DoWorkEventArgs e)
{
// ...
// you can also call another core Function from here
sendMyFile(args)
}
private void ResumeWithFileInfo(FileInfo[] dirflist, object sender, DoWorkEventArgs e)
{
// ...
// you can also call another core Function from here
sendMyFile(args)
}
Anyway, you'll have to use FileInfo to get the FileSize (required in File Transfer I assume) right ? However, you decide the moment you create that FileInfo per File (or are you using several FileInfo-s at the same time ?) If you think your code get too complicated with a list/array of FileInfo from the start, just creates each instance of FileInfo dynamically when it's required (slice your code in parts)
It seems to me the answer to your question only depends on your taste, or only require some changes in the way you're running the logic.
Put all code that handles a single file into a separate method like this:
private void CopyFile(string fn)
{
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
...
}
now decide wheter you want to do the file list stuff or the dir list stuff, and call your new method like this:
File-List:
foreach (string txf in files)
{
this.CopyFile(txt);
}
Dir-List:
dirflist = d.GetFiles();
if (dirflist.Length > 0)
{
foreach (FileInfo txf in dirfilist)
{
this.CopyFile(txt.Name);
}
}
I try to delete one folder with all files inside...But I got an error...I don't know why...the folder is founded with files inside too but when the code try to delete I got the issue...
public static bool DeleteFolder(string FolderName)
{
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
if (iso.DirectoryExists(FolderName))
{
try
{
//get all files in the folder
String[] Files = iso.GetFileNames(FolderName + #"\*");
//delete all files
foreach (var file in Files)
{
iso.DeleteFile(Path.Combine(FolderName, file));
}
//delete the directory
iso.DeleteDirectory(FolderName);
//return true when it's done
if (!iso.DirectoryExists(FolderName))
return true;
}
catch (Exception e)
{
throw new MyException(MyExceptionsMessages.UnknownError + e.Message);
}
}
return true;
}
}
what would happen while searching the file through a string and could i try to continue the loop within the catch block against locked windows file in order to read next file.
TextReader rff = null;
try
{
rff = new StreamReader(fi.FullName);
String lne1 = rff.ReadToEnd();
if (lne1.IndexOf(txt) >= 0)
{
z = fi.FullName;
list22.Add(fi.FullName);
As long as the exception is caught by a try-catch nested inside the loop, you should be able to continue the loop no problem.
I'd say you'll have a try-catch around the statement where you are accessing the file within the loop. Then you can continue the loop after catching any exception.
While catching the exception try to catch only the most specific exception that may be thrown, so if you are looking to handle a locking situation you would look to catch the System.IO.IOException which is raised when files are used by other proccesses.
If you have to do some cleanup to do you should add a finally:
foreach (var fileName in fileNames)
{
var fi = new FileInfo(fileName);
StreamReader reader;
try
{
reader = new StreamReader(fi.FullName);
SomeMethodThatThrowsIOException();
}
catch (IOException ex)
{
continue;
}
finally
{
if (reader != null)
reader.Close();
}
}
or even better (since StreamReader implements IDisposable)
foreach (var fileName in fileNames)
{
try
{
var fi = new FileInfo(fileName);
using (var reader = new StreamReader(fi.FullName))
{
SomeMethodThatThrowsIOException();
}
}
catch (IOException ex) { }
}