When to use try/catch blocks? - c#

I've done my reading and understand what a Try/Catch block does and why it's important to use one. But I'm stuck on knowing when/where to use them. Any advice? I'll post a sample of my code below in hopes that someone has some time to make some recommendations for my example.
public AMPFileEntity(string filename)
{
transferFileList tfl = new transferFileList();
_AMPFlag = tfl.isAMPFile(filename);
_requiresPGP = tfl.pgpRequired(filename);
_filename = filename.ToUpper();
_fullSourcePathAndFilename = ConfigurationSettings.AppSettings.Get("sourcePath") + _filename;
_fullDestinationPathAndFilename = ConfigurationSettings.AppSettings.Get("FTPStagePath") + _filename;
_hasBeenPGPdPathAndFilename = ConfigurationSettings.AppSettings.Get("originalsWhichHaveBeenPGPdPath");
}
public int processFile()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" ");
sb.AppendLine(" --------------------------------");
sb.AppendLine(" Filename: " + _filename);
sb.AppendLine(" AMPFlag: " + _AMPFlag);
sb.AppendLine(" Requires PGP: " + _requiresPGP);
sb.AppendLine(" --------------------------------");
sb.AppendLine(" ");
string str = sb.ToString();
UtilityLogger.LogToFile(str);
if (_AMPFlag)
{
if (_requiresPGP == true)
{
encryptFile();
}
else
{
UtilityLogger.LogToFile("This file does not require encryption. Moving file to FTPStage directory.");
if (File.Exists(_fullDestinationPathAndFilename))
{
UtilityLogger.LogToFile(_fullDestinationPathAndFilename + " alreadyexists. Archiving that file.");
if (File.Exists(_fullDestinationPathAndFilename + "_archive"))
{
UtilityLogger.LogToFile(_fullDestinationPathAndFilename + "_archive already exists. Overwriting it.");
File.Delete(_fullDestinationPathAndFilename + "_archive");
}
File.Move(_fullDestinationPathAndFilename, _fullDestinationPathAndFilename + "_archive");
}
File.Move(_fullSourcePathAndFilename, _fullDestinationPathAndFilename);
}
}
else
{
UtilityLogger.LogToFile("This file is not an AMP transfer file. Skipping this file.");
}
return (0);
}
private int encryptFile()
{
UtilityLogger.LogToFile("This file requires encryption. Starting encryption process.");
// first check for an existing PGPd file in the destination dir. if exists, archive it - otherwise this one won't save. it doesn't overwrite.
string pgpdFilename = _fullDestinationPathAndFilename + ".PGP";
if(File.Exists(pgpdFilename))
{
UtilityLogger.LogToFile(pgpdFilename + " already exists in the FTPStage directory. Archiving that file." );
if(File.Exists(pgpdFilename + "_archive"))
{
UtilityLogger.LogToFile(pgpdFilename + "_archive already exists. Overwriting it.");
File.Delete(pgpdFilename + "_archive");
}
File.Move(pgpdFilename, pgpdFilename + "_archive");
}
Process pProc = new Process();
pProc.StartInfo.FileName = "pgp.exe";
string strParams = #"--encrypt " + _fullSourcePathAndFilename + " --recipient infinata --output " + _fullDestinationPathAndFilename + ".PGP";
UtilityLogger.LogToFile("Encrypting file. Params: " + strParams);
pProc.StartInfo.Arguments = strParams;
pProc.StartInfo.UseShellExecute = false;
pProc.StartInfo.RedirectStandardOutput = true;
pProc.Start();
pProc.WaitForExit();
//now that it's been PGPd, save the orig in 'hasBeenPGPd' dir
UtilityLogger.LogToFile("PGP encryption complete. Moving original unencrypted file to " + _hasBeenPGPdPathAndFilename);
if(File.Exists(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd"))
{
UtilityLogger.LogToFile(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd already exists. Overwriting it.");
File.Delete(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");
}
File.Move(_fullSourcePathAndFilename, _hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");
return (0);
}
}
}

The basic rule of thumb for catching exceptions is to catch exceptions if and only if you have a meaningful way of handling them.
Don't catch an exception if you're only going to log the exception and throw it up the stack. It serves no meaning and clutters code.
Do catch an exception when you are expecting a failure in a specific part of your code, and if you have a fallback for it.
Of course you always have the case of checked exceptions which require you to use try/catch blocks, in which case you have no other choice. Even with a checked exception, make sure you log properly and handle as cleanly as possible.

Like some others have said, you want to use try-catch blocks around code that can throw an Exception AND code that you are prepared to deal with.
Regarding your particular examples, File.Delete can throw a number of exceptions, for example, IOException, UnauthorizedAccessException. What would you want your application to do in those situations? If you try to delete the file but someone somewhere else is using it, you will get an IOException.
try
{
File.Delete(pgpdFilename + "_archive")
}
catch(IOException)
{
UtilityLogger.LogToFile("File is in use, could not overwrite.");
//do something else meaningful to your application
//perhaps save it under a different name or something
}
Also, keep in mind that if this does fail, then the File.Move you do outside of your if block next will also fail (again to an IOException - since the file was not deleted it is still there which will cause the move to fail).

I was taught to use try/catch/finally for any methods/classes where multiple errors could occur and that you can actually handle. Database transactions, FileSystem I/O, streaming, etc. Core logic usually doesn't require try/catch/finally.
The great part about try/catch/finally is that you can have multiple catches so that you can create a series of exception handlers to deal with very specific error or use a general exception to catch whatever errors you don't see coming.
In your case, you're using File.Exists which is good, but their maybe another problem with the disk that may throw another error that File.Exists cannot handle. Yes, it's a boolean method, but say the File is locked and what happens if you try to write to it? With the catch, you can plan for a rare scenario, but without try/catch/finally, you may be exposing the code to completely unforeseen conditions.

The other guys have given quite a number of good pointers and references.
My input is a short one:
When to use it is one thing, equally or more importanly is how to use it properly.
PS: "it" is refeings to "trying-catching exceptions".

Related

Try-Catch block with two catch clauses with similar code

I'm using the try-catch block with two catch clauses. I want to load an XML file from a specific path and I'm checking if the directory exists in the first clause to create the directory, and in the second if the file exists to create the file. However, I know that if the directory doesn't exist, the file will not either.
So I am thinking if there is a way to not duplicate the code. I know I can create a boolean variable and then check if its true and create the file but I thought there might be a nice, clean solution that I just don't know how to search for.
XmlDocument document = new XmlDocument();
try
{
document.Load(folderPath + #"\XMLfile.xml"); // folderPath variable is assigned before depending on user input
}
catch(System.IO.DirectoryNotFoundException)
{
// if folder doesn't exist then the file will not either
System.IO.Directory.CreateDirectory(folderPath);
document.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
}
catch (System.IO.FileNotFoundException)
{
// if folder exists then the file might as well, if not, creating the file's structure
document.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
}
Ideally I would like to know if there is a way to avoid duplicating the code but still keep both exceptions. Is a boolean variable for example createFile in both catch clauses the only way to do in a somewhat nice way?
How about
XmlDocument document = new XmlDocument();
try
{
document.Load(folderPath + #"\XMLfile.xml");
}
catch (Exception e) when (e is DirectoryNotFoundException || e is FileNotFoundException)
{
Directory.CreateDirectory(folderPath);
document.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
}
Directory.CreateDirectory(folderPath); does nothing if the directory already exists. So, it's safe to call it there.
If you are using C# 6 or above, you can use when keyword to filter out exception on specified criteria:
XmlDocument document = new XmlDocument();
try
{
document.Load(folderPath + #"\XMLfile.xml"); // folderPath variable is assigned before depending on user input
}
catch (Exception ex) when (ex is System.IO.DirectoryNotFoundException || ex is System.IO.FileNotFoundException)
{
// if folder doesn't exist then the file will not either
System.IO.Directory.CreateDirectory(folderPath);
document.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
}
However, you don't have exactly the same catch blocks, so you might even consider leaving everything as is. Otherwise, you'll need to introduce another check inside the catch block to invoke CreateDirectory.
Check this thread. You will have basic folder picker and you don't have to scratch your head with try catch blocks. Depends mostly on your application. If it's something server sided, you should read something about Streams (FileStream, StreamReader, etc..) here
You are using exceptions to handle normal control flow. The errors you are catching should be handled without exceptions.
Nevertheless you still can get exceptions due to race conditions, so you still need exception handling.
Also you should consider adding additional handling for malformed xml.
public static XmlDocument LoadOrCreateXmlDocument(string folderPath)
{
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
string fileName = Path.Combine(folderPath + #"\XMLfile.xml");
if (TryLoadXmlDocument(fileName, out var document))
{
return document;
}
XmlDocument newDocument = new XmlDocument();
newDocument.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
return newDocument;
}
public static bool TryLoadXmlDocument(string fileName, out XmlDocument document)
{
document = new XmlDocument();
if (File.Exists(fileName))
{
try
{
document.Load(fileName);
return true;
}
catch (FileNotFoundException)
{
return false;
}
}
return false;
}
Try to avoid executing much code from catch. This is a recipe for bigger problems.
Better is to use the different exceptions to determine what to do next and return this result at the end of the method.
So use the catch only to handle the exceptions and proceed with further action later, not from within the catch.
For example:
bool documentLoaded = LoadXML(); // Your code in this method
if (!documentLoaded)
CreateXML();
Maybe it is better to use methods System.IO.Directory.Exists() and System.IO.Directory.Exists() instead of catching exceptions. So your code will be more clear.
XmlDocument document = new XmlDocument();
if (!System.IO.Directory.Exists(folderPath))
System.IO.Directory.CreateDirectory(folderPath);
var filename = folderPath + #"\XMLfile.xml";
if (System.IO.File.Exists(filename);
document.Load(filename);
else
document.LoadXml("<?xml version=\"1.0\"?> \n" +
"<elements> \n" +
"</elements>");
}

Why am I unable to write to this file?

I wrote an Async method in C# to write to a file, however I keep on getting the following exception:
The process cannot access the file
'C:\XXX\XXX\XXX\XXX\EventBuffer.txt' because it is being used by
another process.
I've had a look at similar questions already posted on SO such as this one, and this one but it seems like the cause of my issue is different.
I used a process monitor to see which processes are trying to access the directory in which the file is in but the only process trying to access it is the one I'm debugging (Will post a snippet soon of the debug process window).
It isn't that file access was being attempted before it was closed upon last access, because I can get the exception when I attempt to access the file for the first time. I have tried to implement a delay after the StreamWriter is instantiated incase the write method was being attempted, I wasn't using the using block before and was disposing of the object using it's dispose methods but in one of the similar questions a this solved the issue.
public static async void UpdateEventBufferFile(EventDetails EventDtls)
{
string line;
try
{
using (StreamWriter EventBufferFile = new StreamWriter(FilePath, true)) // creates the file
{
//All barcode data space sperated for split detection
line = EventDtls.SiteID + " " + EventDtls.McID + " " + EventDtls.EventID + " "
+ EventDtls.EventDT + " " + EventDtls.AdditionalInfo;
await Task.Run(() => LogFileManager.SystematicLog(" Events " + line + " added to buffer file", " BufferFileWriter.cs"));
await EventBufferFile.WriteLineAsync(line); //no need for new line char WriteLine does that
await EventBufferFile.FlushAsync();
//The using block is suffice to dispose of the object the below is no longer required
//EventBufferFile.Dispose();
//EventBufferFile.Close();
//EventBufferFile = null;
}
}
catch (Exception ex)
{
}
}
I have near identical methods utilised within other classes that don't cause the same issue, which annoys me quite a bit.
The method is not being invoked from within a Loop. Invocation is done in a seprate static class in the method below:
public static void AddCentralEvents(int SiteID, int McID, int EventID, DateTime EventDT, string AdditionalInfo)
{
EventDetails EventDetailsObj = new EventDetails();
EventDetailsObj.SiteID = SiteID;
EventDetailsObj.McID = McID;
EventDetailsObj.EventID = EventID;
EventDetailsObj.EventDT = EventDT;
EventDetailsObj.AdditionalInfo = AdditionalInfo;
Task.Run(() => BufferFileWriter.UpdateEventBufferFile(EventDetailsObj));
}
The error is self explanatory, you are using an ASYNC method (in a loop perhaps) and while your first task hasn't completed it's run (i.e. written to the file) that's why you are ending up with that error.
Have you tried writing with a synchronized method? If you have a requirement to periodically write to a file (i.e. logging) use a logging framework.
I recommend using log4net It is 'one of the' the best out there.

File Copy failing to create files with same name

I am copying files from one location to another. I encountered an issue where sometimes a file with the same name would try to save and it would break the program. So I added the logic below to add a number to the filename before copying it:
int counter = 0;
try
{
File.Copy(FileToCopy, FileToSave);
}
catch (Exception)
{
string CurrentFileName = FileToSave.Split('\\', '\\')[4];
string CurretFilePrefix = CurrentFileName.Split('.')[0];
string CurrentFileSuffix = CurrentFileName.Split('.')[1];
string UpdatedFileName = CurretFilePrefix + "_" + counter + "." + CurrentFileSuffix;
File.Copy(FileToCopy, UpdatedFileName);
counter++;
}
However, this is now causing a crash saying the file already exists:
When I check the file does not exist:
Why am I getting this exception? How do I save copies of these files?
A good approach to this problem would include (i) a loop; (ii) use of Path expressions; (iii) avoidance of try-catch when you can test if the file exists; (iv) use of a specific Exception for the extremely unlikely case that two threads or processes are trying to do this same copy at the same time and each get past the File.Exists check; (v) avoidance of while-true-forever loops as even the best code can contain mistakes that could cause a spin-wait forever in production code on a server and it's better to instead have an exception that tells you when something has gone wrong.
int counter = 0;
string proposedDest = dest;
while(counter < 5000)
{
if (!File.Exists(proposedDest))
{
try
{
File.Copy(fileToCopy, proposedDest);
break;
}
catch (IOException ex) when ((uint)ex.HResult == 0x80070050)
{
}
}
counter++;
proposedDest = Path.Combine(Path.GetDirectoryName(dest),
Path.GetFileNameWithoutExtension(dest) +
"_" + counter + Path.GetExtension(dest));
}
;
if (counter == 5000)
throw new Exception($"Could not copy file {fileToCopy} too many retries");
[A better approach would also not use hard coded constants littered through the code ;)]
As CurrentFileName is relative, you are trying to save the file in the location of your executable. Check that directory if your file exists.
Also, there are much better ways to find if the file exists, and also to get the filename and extension of a given file.
i.e. you should use the File.Exists method to determine if the file exists, instead of using exceptions for flow control
if (File.Exists(FileToSave))
{
FileToSave = GetNewFileName(FileToSave)
}
try
{
File.Copy(FileToCopy, FileToSave);
}
catch (Exception)
{
//something went really wrong
}
You should also use the Path methods for getting parts of the name, instead of "knowing" to get the fifth part of the filename (Path.GetExtension, Path.GetFileNameWithoutExtension)
private string GetNewFileName(string oldFileName){
var counter = 0;
var extension = Path.GetExtension(oldFileName);
var directory = Path.GetDirectoryName(oldFileName);
var fileName = Path.GetFileNameWithoutExtension(oldFileName);
var newFileName = Path.Combine(directory,
string.Format("{1}_{2}{3}", filename, counter, extension);
while (File.Exists(newFileName)){
counter++;
newFileName = Path.Combine(directory,
string.Format("{1}_{2}{3}", filename, counter, extension);
}
return newFileName;
}
use this instead:
string CurrentFileName = System.IO.Path.GetFileName(FileToSave);
string CurretFilePrefix = System.IO.Path.GetFileNameWithoutExtension(FileToSave);
string CurrentFileSuffix = System.IO.Path.GetExtension(FileToSave);
string UpdatedFileName = CurretFilePrefix + "_" + counter + "." + CurrentFileSuffix;
Then you have
File.Copy(FileToCopy, UpdatedFileName);
Are you sure this shouldn't be
File.Copy(FileToSave, UpdatedFileName); // FileToSave instead of FileToCopy

Temp Files Asp.net

Is it possilbe to create temp files and images in asp.net applications using something like this:
If no, how can i do it?
(ImagePB is a previously treated Bitmap)
if (System.IO.File.Exists(System.IO.Path.GetTempPath() + #"img" + imgID.ToString() + "PB" + extencao) == true)
{
try
{
System.IO.File.Delete(System.IO.Path.GetTempPath() + #"img" + imgID.ToString() + "PB" + extencao);
imagePB.Save(System.IO.Path.GetTempPath() + #"img" + imgID.ToString() + "PB" + extencao, imgFormat);
}
catch (Exception)
{ }
}
Yes, GetTempPath() should return a temporary file path, and the code you have posted should work. http://msdn.microsoft.com/en-us/library/system.io.path.gettemppath.aspx has more information about how GetTempPath() get's the path.
Though, it does not verify if the Temp Path directory exists, or is writeable by the application. I haven't run into a situation where GetTempPath() does return an inaccessible path. You'd probably want to account for this in your application to handle this situation.
Also be mindful, this is very possibly C:\Windows\Temp. It could have limited disk space, or deleted at any time by someone else when disk space is needed. You may want to create a temp path within your application, and delete it when you no longer need it.

Trouble Moving files in C#?

I am making a software that will move files from the downloads folder to a specific sub folder in a directory. The sub folder is selected by the user by a combobox. I keep getting this error: System.IO.IOException: Cannot create a file when that file already exists. Also, these error come up on people's computer who install my program...exceptions and things. How do i turn it off. Also, why do i get this error? Here is my code:
string pathUser4 = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string pathDownload4 = (pathUser4 + #"\Downloads\");
string sourceFile = pathDownload4 + listBox1.Text;
string pathdoc5 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string pathDownload5 = (pathdoc5 + #"\iracing\setups\");
string destinationFile = pathDownload5 + comboBox1.Text;
File.Move(sourceFile, destinationFile);
if (comboBox1.Text == "Select File Destination")
{
MessageBox.Show("Please Select A Destination Folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Each File.Move should be wrapped in a try/catch block as you can never expect an IO operation to execute without error. It could be something as simple as the user having a file handle open, or the file existing in the destination folder, either way, you don't want a single file to throw an exception that stops the entire operation. You will want to catch the exceptions and log them either to an error log file or to the event log, this way you can see the errors that occurred but it will not interrupt anything.
Secondly, for any desktop application I would add global error handling to log any uncaught errors. You can do this by putting this code at the beginning of your program,
AppDomain.CurrentDomain.UnhandledException += (a, exception) => File.AppendAllText("errorlog.txt", exception.ToString() + "\n"
This will keep the user from ever seeing ugly exceptions being thrown. Also be sure you are not giving the users the .pdb files as this will cause exceptions to contain paths of the computer it was compiled on which can contain your username and other sensitive information you wouldn't want a client to see.
You can register the global exception handling when the main window is initialized, you want it to be the first thing you do before any thing else because again you never know when an exception will be thrown so you have to think defensively.
public partial class MainWindow : Window
{
public MainWindow()
{
AppDomain.CurrentDomain.UnhandledException += (a, exception) => File.AppendAllText("errorlog.txt", exception.ToString() + "\n");
InitializeComponent();
}
}
C# uses exceptions extensively so it will be good concept for you to study up on if you are not familiar with this type of error handling. All exceptions derive from the Exception class so when you write catch (Exception e) this will catch all exceptions (because a base reference can hold an object of a derived type), however if you know the specific exception a method will throw you can catch a more specific exception (always before the more general catch) and handle it in a specific way. In this example you may have an IOException from the File.Move() that you want to catch and handle differently.
try
{
string pathUser4 = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string pathDownload4 = (pathUser4 + #"\Downloads\");
string sourceFile = pathDownload4 + listBox1.Text;
string pathdoc5 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string pathDownload5 = (pathdoc5 + #"\iracing\setups\");
string destinationFile = pathDownload5 + comboBox1.Text;
File.Move(sourceFile, destinationFile);
if (comboBox1.Text == "Select File Destination")
{
MessageBox.Show("Please Select A Destination Folder", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception e)
{
File.AppendAllText("ErrorLog.txt", e.ToString() + "\n");
}
The example code from MSDN for File.Move should get you pointed at the various things you need to deal with, such as an already existing file and basic error handling.
using System;
using System.IO;
class Test
{
public static void Main()
{
string path = #"c:\temp\MyTest.txt";
string path2 = #"c:\temp2\MyTest.txt";
try
{
if (!File.Exists(path))
{
// This statement ensures that the file is created,
// but the handle is not kept.
using (FileStream fs = File.Create(path)) {}
}
// Ensure that the target does not exist.
if (File.Exists(path2))
File.Delete(path2);
// Move the file.
File.Move(path, path2);
Console.WriteLine("{0} was moved to {1}.", path, path2);
// See if the original exists now.
if (File.Exists(path))
{
Console.WriteLine("The original file still exists, which is unexpected.");
}
else
{
Console.WriteLine("The original file no longer exists, which is expected.");
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
}
}
The error may caused by your code, or by some invalid input.
As #Despertar mentioned, I suggest all the program include error handling and log features in your code. It will be very helpful for your debug.
But I suggest use open source log library, not do it by yourself. For example, log4net, NLog, etc.

Categories

Resources