I'm trying to understand how I'm going to use Throw in my code. I have a MainForm class to handle the Windows Form GUI and then I have the Manager class to read and save data from/to files.
I use Try/Catch in both classes, but my instructor want me to use Throw in the Manager class and despite that I'm reading about it, I don't get the point what it will do? Will Throw affect the Try/Catch in the MainForm class?
I also use a message box in the manager class if an exception is catched, but no message box are allow to be in the manager according to the instructor, so how would I do then? Can I use the message box in MainForm class only? Preciate some help to understand and expand my knowledge! Thanks!
MainForm class:
try
{
motelManager.SaveToFile(file);
}
catch
{
MessageBox.Show("Ett fel uppstod!", "Varning!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Manager class:
public void SaveToFile(string filePath)
{
try
{
string newFilePath = filePath.Replace(".bin", "");
filestream = new FileStream(newFilePath + ".bin", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Varning!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
if (filestream != null) filestream.Close();
}
your manager class should look like this:
public void SaveToFile(string filePath)
{
try
{
string newFilePath = filePath.Replace(".bin", "");
filestream = new FileStream(newFilePath + ".bin", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
catch(Exception ex)
{
if (filestream != null) filestream.Close();
throw;
// but don't use
// throw ex;
// it throws everything same
// except for the stacktrace
}
// or do it like this
//catch(Exception ex)
//{
// throw;
// but don't use
// throw ex;
// it throws everything same
// except for the stacktrace
//}
//finally
//{
// if (filestream != null) filestream.Close();
//}
}
and in your main class:
try
{
motelManager.SaveToFile(file);
}
catch (Exception e)
{
MessageBox.Show("Ett fel uppstod!", "Varning!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Throw simply raises the exception to the calling function. (in this case, whoever calls SaveToFile). If there is an error handler there, it will be caught, otherwise it will continue up the call stack until it is caught or at the top level.
It is better to handle the Exception in terms of presentation to the user in the Form - simply because in a larger well structured system the Manager object may well not have any connection to the GUI.
General rule is to catch the exception in the backend [Manager] class to cleanup any resources (i.e. close the file) and then re-throw the exception from the exception handler as follows:
public void SaveToFile(string filePath)
{
try
{
string newFilePath = filePath.Replace(".bin", "");
filestream = new FileStream(newFilePath + ".bin", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
catch(Exception ex)
{
/*
* cleanup resources and rethrow the exception for catching and handling elsewhere
*/
if (filestream != null)
filestream.Close();
throw;
}
}
public void SaveToFile(string filePath)
{
try
{
string newFilePath = filePath.Replace(".bin", "");
filestream = new FileStream(newFilePath + ".bin", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
catch (Exception ex)
{
if (filestream != null) filestream.Close();
//what you want
//MessageBox.Show(ex.Message, "Warning!");
throw (new Exception("Your custom message"));
}
}
And in your manager:
try
{
manager.SaveToFile(filePath);
}
catch (Exception ex)
{
// here shows your 'Your custom message'
MessageBox.Show(ex.Message);
}
In an application with multiple tiers, exceptions that occur in underlying layers are not sent as is to higher layer or to the calling application.
For instance, if something goes wrong in the database related code, you do not send it to client application or to higher layer. Reason for doing so is to provide users with friendly error messages. Say, you had foreign key reference errors during delete operation, you can:
Log the exception information.
Replace with with a user friendly exception message and throw it to layer above.
Layer above may wrap this exception to another higher level message and then throw it ahead. This is similar to what you have been asked to do.
In your code in Manager class, check how many exceptions can possibly occur. If you are using VS, tooltip/help text provides that information. If you are not using VS, check out MSDN for this information.
In the form, handle all the exceptions that can be thrown by manager layer and also a generic exception if something terribly wrong happens. IMHO, this is how your code in manager layer should like
try
{
string newFilePath = filePath.Replace(".bin", "");
FileStream filestream = new FileStream(newFilePath + ".bin", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
catch (ArgumentNullException argNullException)
{
// Log current exception
// Wrap it under your exception type
CustomWrapException customWrap = new CustomWrapException();
customWrap.Message = "Your custom message here.";
customWrap.InnerException = argNullException;
throw customWrap;
}
catch (SecurityException securityException)
{
// Log current exception
// Replace current exception with you custom exception
CustomReplaceException replaceException = new CustomReplaceException();
replaceException.Message = "Your custom message here.";
throw replaceException;
}
finally
{
// Close stream and dispose objects here
}
Your form should be having exception handling like this:
try
{
// Call mananger code from here
}
catch (CustomWrapException wrapException)
{
// replace/wrap if desired
// Display message to user
}
catch (CustomReplaceException replaceException)
{
// replace/wrap if desired
// Display message to user
}
catch (Exception exception)
{
// This is for everything else that may go wrong apart from known possible exceptions
// Display message to user
}
finally
{
}
HTH.
you can use Application ThreadException to catch any exception. And your save logic to wrap with using instead of try catch, in this case it will close your stream.
public void SaveToFile(string filePath)
{
string newFilePath = filePath.Replace(".bin", "");
using(var filestream = new FileStream(newFilePath + ".bin", FileMode.Create))
{
BinaryFormatter b = new BinaryFormatter();
b.Serialize(filestream, animals);
}
}
in the entry point (static void main()) subscribe to this event.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.ThreadException += Application_ThreadException;
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show(".......");
}
it is like this...
try { }
catch(Exception e)
{ throw }
When it throws the exception, it will change the source and the stack trace, so that it will appear that the exception has been thrown from this method, from that very line throw e on the method containing that try-catch block.
Related
I am catching an exception and processing it.
Somewhere up the call tree, I am doing the same.
Once I process my exception at the child level, I want to also go ahead and invoke the exception handler, wherever it is, somewhere up the call tree.
For that, I thought I would do run the throw again.
But instead of breaking somewhere up the call tree, it is breaking in the place where I am doing the throw and crashing, at this line:
throw new Exception("Cannot Write Header Row to Database " + Msg);
code:
public static void NewHeaderRow(string FILE_REV_NUMBER, DateTime FILE_CREATE_DATE, string EDC_DUNS_NUMBER, int RunId)
{
SqlConnection connection = null;
try
{
connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConn"].ToString());
connection.Open();
SqlCommand com;
com = new SqlCommand("dbo.INSERT_PPL_HEADER", connection);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.Add(new SqlParameter("#FILE_REV_NUMBER", FILE_REV_NUMBER));
com.Parameters.Add(new SqlParameter("#FILE_CREATE_DATE", FILE_CREATE_DATE));
com.Parameters.Add(new SqlParameter("#EDC_DUNS_NUMBER", EDC_DUNS_NUMBER));
com.Parameters.Add(new SqlParameter("#RunId", RunId));
if (com.Connection.State == ConnectionState.Closed) com.Connection.Open();
com.ExecuteNonQuery();
}
catch (Exception e)
{
string Msg;
Msg = "Encountered unexpected program issue. Please contact your program administator. Error details...";
Msg = Msg + System.Environment.NewLine;
Msg = Msg + System.Environment.NewLine;
Msg = Msg + e.ToString();
Msg = Msg + System.Environment.NewLine;
Msg = Msg + System.Environment.NewLine;
Msg = Msg + e.Message;
throw new Exception("Cannot Write Header Row to Database " + Msg);
}
finally
{
if (connection == null) { } else connection.Close();
}
}
Try just using the throw keyword, instead of building a new exception.
https://stackoverflow.com/a/2999314/5145250
To add additional information to the exception warp it in another exception object and pass the original exception as argument with new message to keep the original stack trace in inner exception.
throw new Exception("Cannot Write Header Row to Database " + Msg, e);
At some top level you should handle global exceptions to avoid crashing.
The way I was finally able to pin point the problem was to extremely simplify my code so as to be able to see the problem clearly. I just copied my solution to a new location, and gutted out all the non-essential stuff -- stuff I knew was not going to be important for the purposes of troubleshooting.... Very effective way of troubleshooting difficult problems that are hard to trace.... Here is what I ended up with (the simple code).
I was not catching general exception in the code that calls NewHeaderRow.
I was catching System.IO exception.
So, because code had nowhere to go, it crashed....
It is very hard for the eyes to catch this error and also difficult to trace.
private void button1_Click(object sender, EventArgs e)
{
LoadFile();
}
private static int ProcessHeaderRow(string line)
{
int LoadRunNumber = 0;
try
{
//some complex logic was here; error occurs here, so I throw an exception....
throw new Exception("An Error Occurs -- Process Header Row Try block");
}
catch (CustomExceptionNoMessage e)
{
throw new CustomExceptionNoMessage(e.Message);
}
catch (Exception e)
{
//Process the exception, then rethrow, for calling code to also process the exception....
//problem is here...XXXXXXXXXXXXXXXXXX
throw new Exception(e.Message); //crashes
}
return LoadRunNumber;
}
public static bool LoadFile()
{
int RunId = 0;
try
{
RunId = ProcessHeaderRow("10~~happy~007909427AC");
MessageBox.Show("Completed Upload to Cloud...");
}
catch (CustomExceptionNoMessage ce)
{
MessageBox.Show(ce.Message);
}
catch (System.IO.IOException e) //CHANGED THIS LINE, AND I AM UP AND RUNNING (Changed to Exception e)
{
MessageBox.Show(e.Message);
}
return true;
}
public class CustomExceptionNoMessage : Exception
{
public CustomExceptionNoMessage()
{
}
public CustomExceptionNoMessage(string message)
: base(message)
{
}
public CustomExceptionNoMessage(string message, Exception inner)
: base(message, inner)
{
}
}
private void button1_Click(object sender, EventArgs e)
{
LoadFile();
}
This is really short question. I don't understand try-catch mechanism completely.
This is my current code:
public static void WriteText(string filename, string text)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
}
catch(Exception exc)
{
MessageBox.Show("File is probably locked by another process.");
}
}
Background:
Im writing application that shares configuration files with another application.
I need some dialog messagebox with "retry" and "abort" buttons, when that file is used by other application. When that message will appear - I will close that other application and I will try to rewrite that file again by pressing "Retry" button.
Whatr we have is using a counter for re-tries and possibly a thread sleep.
So something like
int tries = 0;
bool completed = false;
while (!completed)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
completed = true;
}
catch(Exception exc)
{
tries++;
//You could possibly put a thread sleep here
if (tries == 5)
throw;
}
}
Even though there's a good answer already I'll submit one that's more tuned towards the OP's question (let the user decide instead of using a counter).
public static void WriteText(string filename, string text)
{
bool retry = true;
while (retry)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
retry=false;
}
catch(Exception exc)
{
MessageBox.Show("File is probably locked by another process.");
// change your message box to have a yes or no choice
// yes doesn't nothing, no sets retry to false
}
}
}
If you need more info on how to implement the messagebox check out the following links;
http://msdn.microsoft.com/en-us/library/0x49kd7z.aspx
MessageBox Buttons?
I would do it like that:
public static void WriteText(string filename, string text, int numberOfTry = 3, Exception ex = null)
{
if (numberOfTry <= 0)
throw new Exception("File Canot be copied", ex);
try
{
var file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
}
catch (Exception exc)
{
WriteText(filename,text,--numberOfTry,ex);
}
}
I like it more like this (example tries to save a RichTextBox on close and allows retrying save or aborting close):
protected override void OnClosing(CancelEventArgs e)
{
if (richTextBox_Query.Modified)
{
DialogResult result;
do
try
{
richTextBox_Query.SaveFile(
Path.ChangeExtension(Application.ExecutablePath, "sql"),
RichTextBoxStreamType.UnicodePlainText);
result = DialogResult.OK;
richTextBox_Query.Modified = false;
}
catch (Exception ex)
{
result = MessageBox.Show(ex.ToString(), "Exception while saving sql query",
MessageBoxButtons.AbortRetryIgnore);
e.Cancel = result == DialogResult.Abort;
}
while (result == DialogResult.Retry);
}
base.OnClosing(e);
}
Below is some of my error logging code. When an exception happens inside my app, I log it to a database. If that database is down or when there's some other problem, I try to log it in an event viewer.
What happens if that event viewer write fails for some reason, too? How do I give up or swallow this new exception?
void SaveLog(string accountId, Exception ex, Category category, Priority priority)
{
try
{
using (var connection = new SqlConnection(…))
{
connection.Open();
command.ExecuteNonQuery();
}
}
catch (Exception exception)
{
// exception while logging!
using (var eventLog = new EventLog { Source = "tis" })
{
eventLog.WriteEntry(
exception.Message + Environment.NewLine +
exception.StackTrace,
EventLogEntryType.Error);
}
}
}
try {
// ...
}
catch (Exception exception) {
try {
// Attempt to write to event log.
}
catch {
}
}
I found a post talking about handling concurrent file access with StreamWriter.
The problem is that the answers do not manage the scenario where the file is being accessed but multiple processes.
Let's tell it shortly :
I have multiple applications
I need a centralised logging system in dataBase
If database fail, I need a fallback on a file system log
There is a known concurrency scenario, where multiple applications (processes) will try to write in that file.
This can be managed by re-attempt the writing after a short delay.
But I don't want ot reattempt if it's a security error or filename syntax error.
The code is here :
// true if an access error occured
bool accessError = false;
// number fo writing attemps
int attempts = 0;
do
{
try
{
// open the file
using (StreamWriter file = new StreamWriter(filename, true))
{
// write the line
file.WriteLine(log);
// success
result = true;
}
}
/////////////// access errors ///////////////
catch (ArgumentException)
{
accessError = true;
}
catch (DirectoryNotFoundException)
{
accessError = true;
}
catch (PathTooLongException)
{
accessError = true;
}
catch (SecurityException)
{
accessError = true;
}
/////////////// concurrent writing errors ///////////////
catch (Exception)
{
// WHAT EXCEPTION SHOULD I CATCH HERE ?
// sleep before retrying
Thread.Sleep(ConcurrentWriteDelay);
}
finally
{
attempts++;
}
// while the number of attemps has not been reached
} while ((attempts < ConcurrentWriteAttempts)
// while we have no access error
&& !accessError
// while the log is not written
&& !result);
My only question is the type of exception that will be raised in the case of concurrency writting. I already know things can be done differently. Let me add a few considerations :
No, I don't want to use NLog in that scenario
Yes I handle concurrency with IOC + Mutex for the in-process concurrency
Yes I really want all log to be written in the same file
It will be an IOException with text:
"The process cannot access the file '{0}' because it is being used by another process."
This is a simplistic approach:
static bool LogError(string filename, string log)
{
const int MAX_RETRY = 10;
const int DELAY_MS = 1000; // 1 second
bool result = false;
int retry = 0;
bool keepRetry = true;
while (keepRetry && !result && retry < MAX_RETRY )
{
try
{
using (StreamWriter file = new StreamWriter(filename, true))
{
// write the line
file.WriteLine(log);
// success
result = true;
}
}
catch (IOException ioException)
{
Thread.Sleep(DELAY_MS);
retry++;
}
catch (Exception e)
{
keepRetry = false;
}
}
return result;
}
If I am using a try/catch/finally block where and how should I initialize variables? For example say I'm trying to use a FileStream . I want to catch any exceptions thrown while creating or using the stream. Then regardless of whether there were any problems or not I want to ensure any stream created is closed.
So I'd do something like this:
System.IO.FileStream fs;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
However this gives me an error in the finally block saying unassigned local variable fs. Yet, if I change the declaration of fs to System.IO.FileStream fs = null it works.
Why do I need to explicitly set fs to null? I've also tried declaring fs in the try block, but then I get the error The name fs does not exsist in the current context in finally block.
BTW: I know I could use a Using block, but the point of my question is to understand the correct usage of a try/catch/finally block.
Assign fs = null;
System.IO.FileStream fs = null;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
See section 5.3 of the specification.
http://msdn.microsoft.com/en-us/library/aa691172(VS.71).aspx
At a given location in the executable
code of a function member, a variable
is said to be definitely assigned if
the compiler can prove, by static flow
analysis, that the variable has been
automatically initialized or has been
the target of at least one assignment.
With your try/catch/finally, the assignment of the try block cannot be guaranteed when you attempt to access the object in the finally block. As you have seen, you can meet the requirement by assigning an initial value to the variable (null, in this case).
The C# design team feels that making sure you explicitly initialize things is a good idea. I tend to agree; I've been bitten by bugs from uninitialized variables enough in C++.
Initializing fs to null is the correct usage. The compiler wants to ensure that you are reading only initialized variables, to avoid serious mistakes. And it cannot garantee that the initialization in your try block is ever executed
The purist in me would want to do something like this:
void DoSomethingWithStream()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(#"C:\test.txt", System.IO.FileMode.Open);
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions that were either thrown by opening the filestream, thrown by closing the filestream, or not caught by the inner try/catch
}
}
Taken to the extreme, though, this would be messy:
void DoSomethingWithStream_PartDeux()
{
try
{
System.IO.FileStream fs = new System.IO.FileStream(#"C:\test.txt", System.IO.FileMode.Open);
try
{
try
{
// do something with the file stream
}
catch (Exception ex)
{
// handle exceptions caused by reading the stream,
// if these need to be handled separately from exceptions caused by opening the stream
}
finally
{
fs.Close();
}
}
finally
{
// FileStream.Close might throw an exception, so put FileStream.Dispose in a separate try/finally
fs.Dispose();
}
}
catch (Exception ex)
{
// handle exceptions
}
}
Accessing a database could be even worse:
void DoSomethingWithDatabase()
{
var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!");
try
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
try
{
// read data from data reader (duh)
}
finally
{
reader.Close();
}
}
finally
{
reader.Dispose();
}
}
finally
{
connection.Close();
}
}
finally
{
connection.Dispose();
}
}
But then, in most cases I don't really see a need to explicitly close your streams / connections / data readers if you are going to dispose of them immediately afterward (unless you're just really paranoid). So, the database code above could just as easily be this:
void DoSomethingWithDatabase_PartDeux()
{
using (var connection = new System.Data.SqlClient.SqlConnection("Connect to mah database!"))
{
var command = new System.Data.SqlClient.SqlCommand("Get mah data!", connection);
connection.Open();
using(var reader = command.ExecuteReader())
{
// read data from data reader (duh)
}
}
}
Maybe I've just been tainted from coding with Dr. Wily's evil API. Using the initialize-variable-to-null trick doesn't work with his framework:
void DoSomethingWithDrWilyEvilBoobyTrap()
{
Dr.Wily.Evil.BoobyTrap trap = null;
try
{
trap = new Dr.Wily.Evil.BoobyTrap(Dr.Wily.Evil.Evilness.Very);
// do something with booby trap
}
catch (Exception ex)
{
// handle exceptions
}
finally
{
if (trap != null) // Exception thrown here!
trap.Dispose(); // Exception thrown here as well!
}
}
Here's a sneak peek at some of the souce code in his API:
public enum Evilness
{
Slight,
Moderate,
Very,
}
class BoobyTrap : IDisposable
{
public Evilness Evil { get; protected set; }
public BoobyTrap(Evilness evil)
{
this.Evil = evil;
}
public void DoEvil()
{
// ... snip (sorry, it's just too evil) ...
}
public static bool IsNull(BoobyTrap instance)
{
throw new Exception("I bet you thought this function would work, didn't you? Well it doesn't! You should know whether or not your variables are null. Quit asking me!");
}
public static bool operator !=(BoobyTrap x, object y)
{
if(y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the != operator. Mwahahaha!!!");
return x.Equals(y);
}
public static bool operator ==(BoobyTrap x, object y)
{
if (y == null)
throw new Exception("You cannot check if an instance of a BoobyTrap is null using the == operator. Mwahahaha!!!");
return x.Equals(y);
}
#region IDisposable Members
public void Dispose()
{
switch (this.Evil)
{
case Evilness.Moderate:
case Evilness.Very:
throw new Exception("This object is cursed. You may not dispose of it.");
}
}
#endregion
}
You are close. I'd set the declaration to be null.
System.IO.FileStream fs = null;
try
{
fs = new System.IO.FileStream("C:\test.txt", System.IO.FileMode.Open);
//do something with the file stream
}
catch (Exception exp)
{
//handle exceptions
}
finally
{
//ERROR: "unassigned local variable fs"
if (fs != null)
{
fs.Close();
}
}
I think that is very acceptable when you can't use a using statement.