I have an application where I load an image dynamically. Let's say, that image doesn't exists and we want to notify the user and then exit. In my main loop, I have exception handling which works just fine when I'm reading files with StreamReader. However, if I am throwing exception from another function, the app just crashes and in the error report I see thrown exception (IOException). To get an idea of the app:
public MainWindow()
{
try {
InitializeComponent();
Load(myFile);
} catch (IOException e) {
MessageBox.Show("Opening failure.");
Application.Current.Shutdown();
}
}
public void Load(string imgPath)
{
string tmpStr;
string[] tmp;
using (StreamReader sr = new StreamReader("myFile.txt", System.Text.Encoding.Default)) {
while ((tmpStr = sr.ReadLine()) != null) {
tmp = tmpStr.Split(' ');
...
}
}
}
private void Grid_Click(object sender, RoutedEventArgs e)
{
...
if (!File.Exists(myFile)) {
throw new IOException("File doesnt exist");
}
...
}
BUT, if I put try-catch block inside Grid_Click, it catches that exception.
The exception that is thrown in Grid-Click would not be caught by the catch statement in the MainWindow() method by design, since the method call to Grid_Click is not inside that try block.
The Grid_Click method is called when the click event is fired, which is at an asynchronous time. Only IOExceptions thrown inside the try block in MainWindow will be caught by that catch statement you have listed above.
What you call 'main loop' is just a constructor. The code does not execute under it's control.
Related
I am attempting to run an async output that will last a large amount of time, and then closing the application during execution which causes the textbox to dispose. I thought this would be handled by simply returning in a try-catch statement, but VS still says there is an "unhandled" exception. Here is the code:
public void AppendOutput(string text)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
try
{
synchronizationContext.Post(new SendOrPostCallback(o =>
{
Output.AppendText((string)o);
}), text);
}
catch(ObjectDisposedException e)
{
return;
}
previousTime = timeNow;
}
Here is what happens in debug:
I there a reason why this is considered unhandled? I thought that's what try-catch was for. My understanding was that I could simply return because there's no need to handle exceptions for a program that is attempting to write to a textbox that has been disposed; the program should be ending the thread on its own.
What is the correct way to deal with this problem?
According to your description, you want to solve the abnormal problem that occurs during debugging.
You can add the following code in the initialization component function, as shown below:
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
Result:
I have a problem with my VSTO add in. On some random machines I recieve an AccessViolationException trying to use PropertyAccessor.GetProperty.
I couldn't find an explenation on the internet for why this exception accures.
Also it seems there is no way to catch this exception, i use a try catch block that catces (System.Exception), but it doesn't catch it and outlook crashes.
Any ideas on what can be the problem?
Decorate your method with the attribute
[HandleProcessCorruptedStateExceptions]:
[HandleProcessCorruptedStateExceptions]
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
try
{
//Do something...
}
catch (Exception ex)
{
//This is catching ALL exception types
//even AccessViolationException
//or OutOfMemoryException
}
}
I already have the saving part down and I know it works, but when I click load button, it will not display anything that I have saved from the text boxes that go to the saying.txt file
using System;
using System.IO;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Grades : Form
{
private StreamWriter fil;
public Grades()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
fil = new StreamWriter("saying.txt"); //This is the txt file
}
catch (DirectoryNotFoundException exc)
{
lstBxDisplay.Text = "Nothing " +
exc.Message;
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
// saving the files to the saying.txt
private void btnSaveAs_Click(object sender, EventArgs e)
{
try
{
fil.WriteLine(txtBxLastName.Text);
txtBxLastName.Text = "";
txtBxLastName.Focus();
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
// next is the load button to load the files into the list/display box
private void btnLoad_Click(object sender, EventArgs e)
{
string inValue;
try
{
using (StreamReader infil =
new StreamReader("saying.txt"))
{
inValue = infil.ReadLine();
while (inValue != null)
{
inValue = infil.ReadLine();
if (inValue != null)
this.lstBxDisplay.Items.Add(inValue);
} // end of while
} // end of using
}
catch (System.IO.IOException exc)
{
lstBxDisplay.Text = exc.Message;
}
}
private void Grades_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
fil.Close();
}
catch
{
}
}
}
}
any reason why it is not loading into the list box? I have tried both label and text box to display the message and neither of them work. I debugged the program and it is executing fine
You have multiple issues here but I will point out two main issues that will get your code working.
You are not closing the stream when you try to save. If the stream stays open, you will never be able to read the values when you try to "Load" the file. You need to call fil.Close(); at the end of your btnSaveAs_Click method.
This is how the save should read.
fil.WriteLine(txtBxLastName.Text);
txtBxLastName.Text = "";
txtBxLastName.Focus();
fil.Close();
You're skipping the first line of the file in your "Load" method. You call infil.ReadLine(); then in the loop, you call it again before you add it to your listbox. You need to move your second ReadLine(). If you are only ever writing a single line to the file, your existing code will skip that first line and try to read it again which will be null (no second line). So, it will never add anything to your listbox.
This is how the reads should be ordered.
using (StreamReader infil = new StreamReader("saying.txt"))
{
inValue = infil.ReadLine();
while (inValue != null)
{
this.lstBxDisplay.Items.Add(inValue);
inValue = infil.ReadLine();
} // end of while
} // end of using
Those changes will get you working. Now to point out, you are going about reading and writing to a file all wrong. You should not be opening a stream in your form load and waiting for button clicks to be writing/reading from that stream. Unless you have a very good reason to do what you are doing, you should be opening your stream, performing the read/write operation, and closing your stream right away. For a simple file IO, I would even suggest using a different mechanism. Look at the MSDN for the System.IO.File Class for easier methods to read lines or write lines to a file.
It appears that exceptions that occur in a property's Set method do not bubble up to the Application's ThreadException event.
We use that event along with the AppDomain.CurrentDomain.UnhandledException event to catch any unexpected mishaps that occur in the application. The exception details are written to a log so our support and development team can better evaluate the issue. Sadly it looks like this Catch All falls short in this particular case.
There are several similar questions on StackOverflow, But no answer addresses the issue of the global exception handling not catching the exception. I already know we can fix it so no exception occurs. We could add a TryCatch block to every setter. We could add the BindingComplete event to each databinding and get the exception that way. But all of that defeats the purpose of having global exception handling which works perfectly in any other case.
How do I 'globally' catch exceptions thrown in object instances
Data Binding and throwing exception in setter
Neither Application.ThreadException nor AppDomain.CurrentDomain.UnhandledException are respected
To reproduce the issue, simply create a form with a text box, bind the text box to a property and throw an exception in the property's set method. Add the ThreadException and UnhandledException events to the program.cs. Run the program and type in the text box to trigger the exception. The debugger will break on the exception, press Continue (F5) the let the exception bubble up as it would outside of the debugger. Any normal exception would end up in those events, but this one does not.
Form1.cs
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", this, "TestValue", true, DataSourceUpdateMode.OnPropertyChanged);
}
private string _TestValue = "";
public string TestValue
{
get{return _TestValue;}
set
{
_TestValue = value;
throw new Exception("Something bad happened in here");
}
}
Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ThreadExceptionHandler;
AppDomain.CurrentDomain.UnhandledException += new System.UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Application.Run(new Form1());
}
private static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs args)
{
try
{
//RR.Common.ErrorLogRt.WriteError(args.Exception.StackTrace.ToString(), args.Exception.Message.ToString(), true);
MessageBox.Show(args.Exception.Message);
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
try
{
if (e != null)
{
Exception ex = e.ExceptionObject as Exception;
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
MessageBox.Show(ex.Message);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
if (e != null && e.Exception != null && e.Exception.InnerException != null)
{
//The unobserved exception is always the same, The actual exception that cause it will be the inner exception.
Exception ex = e.Exception.InnerException;
MessageBox.Show(e.Exception.Message);
//RR.Common.ErrorLogRt.WriteError(ex.StackTrace.ToString(), ex.Message.ToString(), true);
}
else
{
MessageBox.Show("Unhandled Error: " + e.ToString());
}
}
catch
{
MessageBox.Show("Error writing to exception log. This program will now terminate abnormally.");
Application.Exit();
}
}
}
Remarks from Binding.FormattingEnabled Property
Setting this property to true also enables error-handling behavior and
causes the BindingComplete event to be raised. The handler of this
event can take the appropriate action, based on the success, error, or
exceptions in the binding process, by examining the
BindingCompleteState property of the BindingCompleteEventArgs
parameter.
The code involved
internal bool PushData(bool force)
{
Exception ex = null;
if (!force && this.ControlUpdateMode == ControlUpdateMode.Never)
{
return false;
}
if (this.inPushOrPull && this.formattingEnabled)
{
return false;
}
this.inPushOrPull = true;
try
{
if (this.IsBinding)
{
object value = this.bindToObject.GetValue();
object propValue = this.FormatObject(value);
this.SetPropValue(propValue);
this.modified = false;
}
else
{
this.SetPropValue(null);
}
}
catch (Exception ex2)
{
ex = ex2;
if (!this.FormattingEnabled)
{
throw;
}
}
finally
{
this.inPushOrPull = false;
}
if (this.FormattingEnabled)
{
BindingCompleteEventArgs bindingCompleteEventArgs = this.CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, ex);
this.OnBindingComplete(bindingCompleteEventArgs);
return bindingCompleteEventArgs.Cancel;
}
return false;
}
As you can see, passing 4th parameter as true: DataBindings.Add("Text", this, "TestValue", true is responsible for catching the exception inside PushData and passing it to BindingComplete event. There is no other way (except AppDomain.CurrentDomain.FirstChanceException) to find the exception anywhere else than in BindingComplete if formatting is enabled.
I know a solution exists for WPF, but I could not make it work for winforms.
It seems like the exception is somehow trapped by the framework and I cant find the right trace to listen to.
What you could do though is handle first-chance exceptions (beware that this will probably make you catch way more than what you want).
This will show a message box with "Something bad happened in here" in your example:
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
//...
private static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs firstChanceExceptionEventArgs)
{
if(firstChanceExceptionEventArgs.Exception is TargetInvocationException)
{
if(firstChanceExceptionEventArgs.Exception.InnerException != null)
MessageBox.Show(firstChanceExceptionEventArgs.Exception.InnerException.Message);
else
MessageBox.Show(firstChanceExceptionEventArgs.Exception.Message);
}
}
If you are curious, this is the WPF solution I was talking about:
https://web.archive.org/web/20140809204919/https://www.tech.pro/tutorial/940/wpf-snippet-detecting-binding-errors
It looks like it has been reported as a defect to Microsoft, who have closed it as 'Won't Fix':
ReflectPropertyDescriptor.SetValue does not preserve stack trace
in the Microsoft Reference Source, the code for the SetValue method (lines 1085 to 1173) contains a block with a structure like this:
try // <--- This ...
{
try
{
// Code to invoke SetMethod.
}
catch(Exception)
{
// Code to rewind.
// Code to throw inner or rethrow.
}
}
finally // <--- ... and this consume the exception before you can handle it.
{
// Code to raise change notification.
}
The outer try ... finally block is consuming the (second chance) exception that is preventing you from handling it in your code. The dbugger can still catch the (first chance) exception, but it wont get out of the SetValue method.
I am maintaining a program with customer informations. It consists of many forms that each show some relevant info from the database. This error is in a single form after doing the following
Open the customer search form
View random customer A info in the customerinfo form
open the crm form and it automatically shows customer A. Then add a file to him via draganddrop.
Close the last two forms and select random customer B and do the same.
Close the last two forms and select customer A and add a new file. Error!!!
Here is the code that fails:
private void FireFileCountChanged() {
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2); // FAILS
"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
I tried adding the following:
private void FireFileCountChanged() {
if (FileCountChanged != null && this.Handle != null) // CHANGED AND FAILS.
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
But the this.handle gives:
'this.Handle' threw an exception of type 'System.ObjectDisposedException' and
"Cannot access a disposed object.\r\nObject name: 'AttachmentsControl'."
Then I added a timeout of 10 seconds as the first line in the method, but the handle is still not created. Has the handle somehow been disposed when one of the window were closed? And what can be done about this? Any help is appreciated. I'm kind of stuck.
private void FireFileCountChangedDeferred(int repostCount) {
if (FileCountChanged != null) {
if (repostCount > 0) {
//black magic is somehow involved in getting this event to fire *after* the filewatcher reports the change.
System.Threading.Thread.Sleep(10);
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), repostCount - 1);
} else
FileCountChanged(this, null);
}
}
private void CopyFiles(string[] files, bool reload) {
if (CreatePath()) {
foreach (string src in files) {
try {
string dest = MakeSafeFilename(src);
File.Copy(src, dest);
FireFileCountChanged();
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error while copying:{1}{0}", ex.Message, environment.NewLine);
}
}
}
}
private void Lstv_DragDrop(object sender, DragEventArgs ea) {
if (m_CanAdd) {
string[] files = GetDraggedFiles(ea);
if (files != null)
CopyFiles(files, true);
else if (OutlookDataObject.HoldsOutlookData(ea) && CreatePath()) {
try {
OutlookDataObject.CopyDroppedFiles(ea, m_Path, OutlookFilenameCallback);
} catch (Exception ex) {
//Util.Print("Copy ex: {0}", ex.Message);
ErrMsg("Error copying from Outlook:{1}{0}", ex.Message, Environment.NewLine);
}
}
}
}
Solution
private void FireFileCountChanged() {
while (!this.IsHandleCreated) // added
System.Threading.Thread.Sleep(100); //added
if (FileCountChanged != null)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
You need to check the IsHandleCreated property, not compare the Handle to null. Reading the Handle property is considered a UI operation itself.
private void FireFileCountChanged() {
if (FileCountChanged != null && this.IsHandleCreated)
BeginInvoke(new DeferEvent(FireFileCountChangedDeferred), 2);
}
However, based on the complex steps that you need to take to reproduce the bug, I suspect that there are some form instance re-use issues or other more complex issues at play here, and it isn't just a matter of making this call to BeginInvoke work.