.NET Forum / .NET Framework / New Users / August 2005
finally is not always being executed...
|
|
Thread rating:  |
cmullins - 14 Aug 2005 04:01 GMT Hi,
I was under the impression that the finally block always got executed no matter what. Well I ran into a situtation that I can duplicate from a console app as well as a web application.
For the console app if the try block is executing/processing its clode and the console app is closed the finally block is not called.
The same is true for the web app. If the page_load or button_click is processing cose and it is wrapped by a try/catch/finally or try/finally the finally is not called if the web page is closed.
Give it a try, I was blown away by this. I thought no mwatter what the finally block was called and I have very important logic that needs to be called.
Below is very sloppy code, but it will do the trick to show this isn't working. The same goes for the console. Just put the try/finally in the main.
Code: using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.IO;
public partial class GeneralTest : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Response.Write(DateTime.Now.ToString()); } protected void Button1_Click(object sender, EventArgs e) { try { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int x = 0; x < 100000000; x++) { for (int y = 0; y < 100000000; y++) { sb.Append(x.ToString() + " " + y.ToString()); } } } finally { FileStream fsUpdateBackupDateTime = new FileStream(@"c:\zzzzcjm.txt", FileMode.OpenOrCreate); StreamWriter sw = new StreamWriter(fsUpdateBackupDateTime); sw.Write(DateTime.Now.ToString()); sw.Flush(); sw.Close(); } }
}
I guess is there anything that can be done or a way to make the finally or a block of code to be called if the console app or web application is closed?
Thanks for your time!! cmullins
cmullins - 14 Aug 2005 04:05 GMT I forgot to add this was tested in vs2003 and vs2005. The block of code is from my vs2005 test.
Thanks again, cmullins
Lloyd Dupont - 14 Aug 2005 04:23 GMT At first I was not surprised I was thinking he's just going to crash the computer! Silly me, of course .NET handles not enough memory gracefully! I mean just throw OutOfMemoryException() ;-) I've got this zzccmmxxbbll.txt file no problem!
just to show you I attached my console application and crash test. BTW there is a faster way to create a not enough memory situation (which stress less the system!).
just write int[] array = new int[int.Maxvalue]; ;-)
 Signature If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
>I forgot to add this was tested in vs2003 and vs2005. The block of code is > from my vs2005 test. > > Thanks again, > cmullins Lloyd Dupont - 14 Aug 2005 04:24 GMT have you think you might simply not have rights to write the file. after all it's an ASP.NET application you are speaking about!
 Signature If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them. Jack Handey.
>I forgot to add this was tested in vs2003 and vs2005. The block of code is > from my vs2005 test. > > Thanks again, > cmullins cmullins - 14 Aug 2005 13:31 GMT Since, what I'm writting cannot be a webapp, I believe I'll create a windows app instead. I'll will hook into one of the closing events for my logic, use two threads and a stop button or something close to one of those.
Thanks again for the replies!! cmullins
> I forgot to add this was tested in vs2003 and vs2005. The block of code is > from my vs2005 test. > > Thanks again, > cmullins Scott M. - 14 Aug 2005 05:38 GMT In a console app, what you describe makes perfect sense...stop the application and there is nothing left to keep processing, but in a web app (ASP.NET) all the server-side processing takes place before the client ever sees the page. You can't wrap Page_Load (or any event handler for that matter) inside of a Try...Catch, but you certainly can place a Try...Catch inside of an event handler.
BUT.....there is a difference in what you've experience in a console app. vs. a web app. In VS.NET, if you close the browser (presumably, you are talking about closing it before the page processing is completed), you are telling VS.NET that you do not wish to continue to debug the application and so the code processing is terminated. But, in a real deployment, if a browser were to be closed, it would not signal the server to stop processing the application (i.e.. Application_End).
> Hi, > [quoted text clipped - 69 lines] > Thanks for your time!! > cmullins Willy Denoyette [MVP] - 14 Aug 2005 11:44 GMT No there is nothing you can do in the case of a OOM exception, you consumed all the memory available for the process, so there is nothing left for the program to continue it's execution of the finally block.
Willy.
> Hi, > [quoted text clipped - 69 lines] > Thanks for your time!! > cmullins cmullins - 14 Aug 2005 16:10 GMT Willy,
Actually, there is no exception being generated. So, the OOM exception isn't coming into play. Basically, if I manually throw an exception then of course the finally is called.
However, in my situation I'm actually copying files from one location to another. At the end of the copy process I'm writing out the date time of when the process started. This way only modified files will be copied the next time. What I want to do is if the user closes the console then write out the date time. Otherwise, the date time isn't being updated since the finally isn't called.
Thanks, cmullins
> No there is nothing you can do in the case of a OOM exception, you consumed > all the memory available for the process, so there is nothing left for the [quoted text clipped - 75 lines] > > Thanks for your time!! > > cmullins Jon Skeet [C# MVP] - 14 Aug 2005 17:35 GMT > Actually, there is no exception being generated. So, the OOM exception > isn't coming into play. In that case, presumably the code you posted isn't actually the code in question, which makes it much harder to work out what's actually going on.
Are you sure that the finally block itself isn't generating an exception, by the way? Have you tried putting something which is bound to work (and let you know that it's executed) as the first bit of the finally block?
Could you post a short but complete program which demonstrates the problem? A console application would be much easier to work with than an ASP.NET app.
See http://www.pobox.com/~skeet/csharp/complete.html for details of what I mean by that.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
cmullins - 14 Aug 2005 18:01 GMT Jon,
Sure thing. Here is the code I was using. If the code completes the finally block is called correctly. It is only when the console is closed while files are being copied.
using System; using System.Collections; using System.Text; using System.IO;
namespace BackupComputerConsole { class StartBackup { FileStream fs; System.IO.StreamWriter sw; DateTime lastBackupDateTimeValue; DateTime thisBackupDateTime; const string BackupLastDateTimeFileName = @"c:\backupLastDateTime.txt"; const string BackupLogFileName = @"c:\backuplog.txt";
static void Main(string[] args) { // Ensure the parameters are passed if(args.Length!=3) { Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("Usage: "); Console.WriteLine(""); Console.WriteLine("StartBackup.exe <string: Directory to copy> <string: Destination Directory> <bool: Overwrite files>"); Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("Example: "); Console.WriteLine(""); Console.WriteLine(@"StartBackup.exe c:\ x:\ true"); Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } else { new StartBackup().OnStart(args[0], args[1], Boolean.Parse(args[2])); } }
#region Private Properties
private DateTime LastBackupDateTime { get { if (this.lastBackupDateTimeValue == DateTime.MinValue) { try { FileStream fsGetLastBackupDateTime = new FileStream(@"c:\backupLastDateTime.txt", FileMode.Open); StreamReader sr = new StreamReader(fsGetLastBackupDateTime); this.lastBackupDateTimeValue = DateTime.Parse(sr.ReadLine()); sr.Close(); } catch { this.lastBackupDateTimeValue = DateTime.MinValue; } } return this.lastBackupDateTimeValue; } }
#endregion
#region Private Methods
private string CheckAndCreateDirectory(string directoryName) { if (directoryName.Substring(3).Length > 0) { // Append the directory with a slash if (!directoryName.EndsWith(@"\")) { directoryName += @"\"; }
// Create the directory if it doesn't exist if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } } return directoryName; }
private void UpdateLastBackup() { FileStream fsUpdateBackupDateTime = new FileStream(BackupLastDateTimeFileName, FileMode.OpenOrCreate); StreamWriter sw = new StreamWriter(fsUpdateBackupDateTime); sw.Write(this.thisBackupDateTime); sw.Flush(); sw.Close(); }
private void OnStart(string directoryToCopy, string directoryDestination, bool overWriteFiles) { try { this.thisBackupDateTime = DateTime.Now; fs = new FileStream(BackupLogFileName, FileMode.OpenOrCreate); sw = new StreamWriter(fs); //Create a Directory object using DirectoryInfo DirectoryInfo directoryToCopyInfo = new DirectoryInfo(directoryToCopy);
//Pass the Directory for displaying the contents getDirsFiles(directoryToCopyInfo, directoryDestination, overWriteFiles); } finally { // Update Backup Time this.UpdateLastBackup();
sw.Flush(); sw.Close(); fs.Close(); }
} private void getDirsFiles(DirectoryInfo singledirectory, string directoryDestination, bool overWriteFiles) { //create an array of files using FileInfo object FileInfo[] files;
try { //get all files for the current directory files = singledirectory.GetFiles();
//iterate through the directory and print the files foreach (FileInfo file in files) { try { this.CopyFile(file.FullName, directoryDestination + @"\" + singledirectory.FullName.Substring(3), overWriteFiles); } catch (Exception subdirectoryFileCopyException) { sw.WriteLine("Problem with copying file " + file.FullName + "." + Environment.NewLine + "Error: " + subdirectoryFileCopyException.StackTrace + Environment.NewLine + subdirectoryFileCopyException.Message); sw.WriteLine(""); continue; } } } catch (Exception e) { sw.WriteLine("Problem with " + singledirectory + ". " + Environment.NewLine + "Error: " + e.StackTrace + Environment.NewLine + e.Message); sw.WriteLine("");
} try { //get sub-folders for the current directory DirectoryInfo[] dirs = singledirectory.GetDirectories();
//This is the code that calls //the getDirsFiles (calls itself recursively) //This is also the stopping point //(End Condition) for this recursion function //as it loops through until //reaches the child folder and then stops. foreach (DirectoryInfo dir in dirs) { getDirsFiles(dir, directoryDestination, overWriteFiles); } } catch (Exception e) { sw.WriteLine("Problem reading next directories. " + Environment.NewLine + "Error: " + e.StackTrace + Environment.NewLine + e.Message); sw.WriteLine("");
}
}
private void CopyFile(string sourceFileAndDirectory, string destinationDirectoryName, bool overwrite) { try { FileInfo sourceFileInfo = new FileInfo(sourceFileAndDirectory); // Only copy files that have been modified since the last backup if (sourceFileInfo.LastWriteTime > this.LastBackupDateTime) { // NOte, Future versions should probably pass this in as a parameter or be set from a configuration file Console.WriteLine("Copying " + sourceFileAndDirectory); string destination = (this.CheckAndCreateDirectory(destinationDirectoryName) + new FileInfo(sourceFileAndDirectory).Name); File.Copy(sourceFileAndDirectory, destination, overwrite); } else { // Note, uncommenting this line shows files that were skipped, but slows down the overall copy process // Future versions should probably pass this in as a parameter or be set from a configuration file //Console.WriteLine("Skipping " + sourceFileAndDirectory); } } catch (IOException io) { sw.WriteLine("Problem with " + sourceFileAndDirectory + ". " + Environment.NewLine + "Error: " + io.StackTrace + Environment.NewLine + io.Message); sw.WriteLine(""); } }
#endregion } }
Thanks again, cmullins
> > Actually, there is no exception being generated. So, the OOM exception > > isn't coming into play. [quoted text clipped - 14 lines] > See http://www.pobox.com/~skeet/csharp/complete.html for details of > what I mean by that. Jon Skeet [C# MVP] - 14 Aug 2005 18:41 GMT > Sure thing. Here is the code I was using. If the code completes the > finally block is called correctly. It is only when the console is closed > while files are being copied. Ah - I see, when the whole process is terminated abruptly. I don't think there's anything that can be done about that. For anyone who's interested, here's a shorter example which demonstrates the problem:
using System; using System.IO; using System.Threading;
class Test { static void Main() { try { Thread.Sleep (15000); Console.WriteLine ("You didn't kill me!"); } finally { using (StreamWriter writer = new StreamWriter ("c:\\test\\test.txt")) { writer.WriteLine ("Finally block"); } } } }
You don't need to kill the console - just hit Ctrl-C and the process will be killed without the finally block being executed.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Willy Denoyette [MVP] - 14 Aug 2005 20:02 GMT >> Sure thing. Here is the code I was using. If the code completes the >> finally block is called correctly. It is only when the console is closed [quoted text clipped - 30 lines] > You don't need to kill the console - just hit Ctrl-C and the process > will be killed without the finally block being executed. That's correct, when hitting Ctrl-C the process will get killed by a call to ProcessExit() from the default console Ctrl handler, from this point on the CLR won't execute any manage code and forces a rude abort of the EE. However no-one stops you from installing your own Console Ctrl handler to take care of this,or simply refuse to terminate the process, at this point you can simply resume the process.
Willy.
Metallikanz! - 15 Aug 2005 06:00 GMT This is a scary situation though, even the official Microsoft documentation on this subject fails to mention this (I cannot believe they are unaware of this fact):
"A Finally block is always executed when execution leaves any part of the Try statement. No explicit action is required to execute the Finally block; when execution leaves the Try statement, the system will automatically execute the Finally block and then transfer execution to its intended destination. The Finally block is executed regardless of how execution leaves the Try statement: through the end of the Try block, through the end of a Catch block, through an Exit Try statement, through a GoTo statement, or by not handling a thrown exception"
Metallikanz!
>>> Sure thing. Here is the code I was using. If the code completes the >>> finally block is called correctly. It is only when the console is closed [quoted text clipped - 39 lines] > > Willy. Jon Skeet [C# MVP] - 15 Aug 2005 07:09 GMT > This is a scary situation though, even the official Microsoft > documentation on this subject fails to mention this (I cannot believe [quoted text clipped - 9 lines] > statement, through a GoTo statement, or by not handling a thrown > exception" In this case, however, execution never actually leaves the try statement, and certainly not in any of the ways specified above.
Note that you would never be able to make it absolutely always execute the finally block - what would you expect to happen if the power was turned off?
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|