Handling exception with BackgroundWorker - c#

In a WinForms application, an exception is occurring within a method that is run when a BackgroundWorker.DoWork event is fired.
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.DoWork += import_begin;
System.Objects arguments = new System.Object[] {filename, why};
worker.RunWorkerAsync(arguments);
private void import_begin(System.Object sender, System.ComponentModel.DoWorkEventArgs args)
{
// unpack the arguments
System.String filename = (System.String)arguments[0];
// exception is occurring here
Controller.Excel excel = new Controller.Excel(filename);
}
I have set breakpoints to pin down where the exception was thrown, and it was in the line of code as commented above. Even after handling the exception, there is a dialog box that shows up:
"Exception has been thrown by the target of an invocation".
Is it possible to prevent this dialog box?
By the way, the exception is of type InvalidDataException, due to an invalid file type attempted to be imported.
Edit: partial Controller.Excel code:
class Excel
{
protected OfficeOpenXml.ExcelPackage excel;
protected const int HEADER_ROW_OFFSET = 7;
System.Globalization.CultureInfo provider;
// ctor
public Excel(System.String filename)
{
excel = new OfficeOpenXml.ExcelPackage(new System.IO.FileInfo(filename));
excel.Compatibility.IsWorksheets1Based = false;
provider = System.Globalization.CultureInfo.InvariantCulture;
}
}

OP:
it is using the standard file dialog form. It is intended for the user to open an .xlsx file, and this exception occurs whenever they try to open any other file type
I sounds as though you just want to handle the condition gracefully when the user somehow picks a file which isn't an Excel file and/or corrupt.
Change your code to something like:
private void import_begin(System.Object sender, System.ComponentModel.DoWorkEventArgs args)
{
// unpack the arguments
System.String filename = (System.String)arguments[0];
// you probably should inspect the file extension in `filename` to see if it
// is at least .xls or .xlsx prior to using Controller
try
{
Controller.Excel excel = new Controller.Excel(filename);
...
}
catch (InvalidDataException ex)
{
// gracefully handle the error
...
}
catch (Exception ex)
{
// eat, don't want thread to unwind
}
}
Now if they pick any other file type or the file is a corrupted Excel file, the catch handler above will be invoked where you can handle the situation gracefully.
Normally you shouldn't update the UI from a worker but MessageBox has it's own message pump.
Tip
To keep it neat, you might want to inspect the file's extension prior to running the BackgroundWorker. It's a quick check for a quick win and keeps all UI-related activities in the main thread and not a child thread where things become more complex.

Related

Naudio: Encode in BackgroundWorker -> Empty target file created, Application is shut down and no exception is thrown

I´m having a problem with the MediaFoundationEncoder. When I execute the following code in a BackgroundWorkers DoWork method, I get an empty mp4 file, no exception is thrown and the Application is shut down immediately:
private void Mp4_DoWork(object sender, DoWorkEventArgs e)
{
try {
using (var reader = new MediaFoundationReader(_currentFilePath))
{
using (var encoder = new MediaFoundationEncoder(quality))
{
encoder.Encode(_mp4Path, reader);
}
}
} catch (Exception ex) {
Log.LogError("...", ex, true, false);
}
}
All file paths are valid and the _currentFilePath variable points to a wav file of about 50 MBs that is not being used and that I can successfully encode when above code is not run in a BackgroundWorker. Also the RunWorkerCompleted method is not called. When Debugging, I get to encoder.Encode(_mp4Path, reader); and that´s it. Any ideas?

System.Diagnostics.Process.HasExited always returns true and Process.Exited event handler always hits when process is still open

I am currently having issues with a piece of my code. This code works perfectly fine (even without some of the extra if statements) on my machine when I run it both through Visual Studio 2013 and when I publish using ClickOnce. On the client's machine, the event handler for the process exiting catches, the process ID is correct, and the process.hasExited returns true all while the process is still open. The process opens a pdf for them to sign and waits for them to save it and close it before proceeding. It uses Adobe Acrobat Reader DC.
The error message received is from the catch block of the event handler method:
System.IO.IOException: The process cannot access the file because it is being used by another process.
This occurs at the call for getReaders_NewHire(emp).
This is the method that opens the process:
private void pdfAndEmail(Employee employee, Requirements requirements)
{
try
{
PDFUtility pdfu = new PDFUtility();
pdfu.createPDFMG_NewHire(employee, requirements);
emp = employee;
process = new System.Diagnostics.Process();
string path = System.IO.Path.GetTempPath() + "\\ITResources\\Manager\\NewHire_" + employee.m_name.Replace(" ", "") + ".pdf";
Uri pdf = new Uri(path, UriKind.RelativeOrAbsolute);
process.StartInfo.FileName = pdf.LocalPath;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(process_Exited);
process.Start();
pid = process.Id;
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
and this is the event handling method:
private void process_Exited(object sender, EventArgs e)
{
try
{
if (process.Id == pid)
{
if (process.HasExited)
{
PDFUtility pdfu = new PDFUtility();
pdfu.getReaders_NewHire(emp);
Emailer send = new Emailer();
send.SendAfterMG_NewHire();
}
else
MessageBox.Show("Process has not exited.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error Message", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
The getReaders method in the event handler needs to read the file that the client has to sign so it must be closed beforehand. I cannot force a close because I will never know how long it will take them to sign and I can't have the email being sent out before they've signed and saved the pdf.
I've already tried using the WaitForExit() method and it skips completely (only on my client's computer). I am running Windows 10, but the client is running Windows 7. I have not been able to find any documentation about these methods not working on Win7.
Note: I understand that the if statements in the event handler method are a bit redundant, but I was desperate to find where it was catching.
Any help would be greatly appreciated.
FIXED: I ended up have a Message Box pop up after my PDF was signed to assist the WaitForExit() in the Background Worker thread. After this message box's OK button is pressed (because they pause the application and wait for a response), it then reads the pdf files.
It uses Adobe Acrobat Reader DC.
This is your advantage I think, and my approach assumes this. Consider the following:
private static void Main()
{
var process = Process.GetProcessesByName("AcroRd32").FirstOrDefault();
if (process != null)
{
Process.Start(#"C:\mvvm.pdf");
}
else
{
process = Process.Start(#"C:\mvvm.pdf");
}
process.EnableRaisingEvents = true;
process.Exited += (sender, args) =>
{
Console.WriteLine("Exited");
};
Console.ReadLine();
}
First we check if Adobe process is already running, if so we keep the reference and hook the event handler to that existing process. If not we do what you have already done. Some validation code is omitted.
I have tested with the process running and not running and it works.
NOTICE: It will not work as expected if the user has another application configured as a predefined PDF reader

Is it possible to have one Exception handler for multiple timers in a C# class?

I have a C# program which runs a system tray application - transferring / moving / creating / editing files in the background.
There is alot of exception handling and loop handling to prevent the program from crashing / getting stuck if the user manual deletes a file the program is working with.
Unfortunately one of the computer the program is running on is having the program crash. The computer is very hard to get, and cannot have debugging software loaded on (it is an embedded PC outside with no network access).
I have tried finding the cause of the crash (such as an unhandled exeption) however have not found anything and do not have access to the PC as it is in a remote location.
I was hoping to find a way to use AppDomain / an UnhandledExceptionEventHandler to catch all unhandled exceptions and log them for diagnosis.
However the exception I have (deliberately) created in the office inside the class "DoSomething.class", are crashing the WinForm application rather than logging the exception error.
My code is:
//Current domain for the unhandled exception handling
AppDomain currentDomain;
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlAppDomain)]
public MainForm()
{
InitializeComponent();
currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
CreateTimer.Interval = Settings.Default.CreateTimer;
CreateTimer.Start();
RunCopyProtocol();
ChangeFilesTimer.Interval = 10000;
ChangeFilesTimer.Start();
RunChangeFiles();
throw new Exception("ABC");
}
public void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
try
{
Exception ex = (Exception)args.ExceptionObject;
SetLabel(ex);
}
finally { }
}
private string SetLabel(Exception ex)
{
String str;
str = ex.Message;
Label1.Text = str;
return str;
}
private void ChangeFilesTimer_Tick(object sender, EventArgs e)
{
RunChangeFiles();
throw new Exception("2");
}
Why doesn't the throw new exception call the unhandled exception error?
How would I use AppDomain to get the unhandled exception handler to handle this exception / exceptions in the RunChangeFiles?
Code on AppDomain is based of MSDN examples
If your timer is a System.Timers.Timer the reason is documented by MSDN here:
The Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event.
Take a look at this similar question:
How do I get the Exception that happens in Timer Elapsed event?
You'll have to catch the exceptions that are thrown in the elapsed handler, and rethrow them on a ThreadPool thread.
Using your code above and extending the answer from the referenced question:
private void ChangeFilesTimer_Tick(object sender, EventArgs e)
{
try
{
RunChangeFiles();
}
catch (Exception ex)
{
ThreadPool.QueueUserWorkItem(
_ => { throw new Exception("Exception on timer thread.", ex); });
}
}
If your timer is a System.Windows.Forms.Timer then you will need to hook into the Application.ThreadException event to handle unhandled exceptions.
Subscribe to this event prior to calling Application.Run().
You can also handle logging of the Exception in a local exception handling block before rethrowing the exception.
try
{
/// ...
}
catch (Exception ex)
{
if (ex is ArgumentException)
{
/// handle known or specific exceptions here
}
else
{
/// log then rethrow unhandled exceptions here
logExceptions(ex);
throw;
}
}

Handling AppDomain.CurrentDomain.UnhandledException in windows service

I have window service which acts as a sync software. I want to add unhanded exception logging on my service, so I modified my program.cs like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main()
{
// Register Unhandled Exception Handler
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
// Run Service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service()
};
ServiceBase.Run(ServicesToRun);
}
static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
// Get Exception
Exception ex = (Exception)args.ExceptionObject;
// Generate Error
string ErrorMessage = String.Format(
"Error: {0}\r\n" +
"Runtime Terminating: {1}\r\n----- ----- ----- ----- ----- -----\r\n\r\n" +
"{2}\r\n\r\n####################################\r\n",
ex.Message,
args.IsTerminating,
ex.StackTrace.Trim());
// Write Error To File
try
{
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
sw.WriteLine(errorMessage);
}
catch { }
}
}
Then on my Service.cs file, in the OnStart method, I added a throw new Exception("test"); to see if unhanded exceptions are being logged to file as expected.
When I start my service, it stops immediately as expected; however it doesn't seem to be logging the exception to the specified file.
Any idea what I am doing wrong here? Thanks in advance for any help.
Before you ask, my service runs as Local Service and the directory where my service .exe runs from (c:\mysync) already has Local Service added in the security tab with full read/write access.
OnStart is called in Service base class inside try-catch block. If an exception happens on this stage it catches it and just set a status 1 as a result and do not throw it further:
string[] args = (string[]) state;
try
{
this.OnStart(args);
.....
}
catch (Exception ex)
{
this.WriteEventLogEntry(Res.GetString("StartFailed", new object[1]
{
(object) ((object) ex).ToString()
}), EventLogEntryType.Error);
this.status.currentState = 1;
}
As a result you can find a record in EventLogs, but you can't catch it as an unhanded domain exception, as there is no such exception.
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
It is forever a really bad idea to not use full path names for files (like c:\foo\bar.log). Especially in a service, you have very little control over the default directory for your service. Because it is started by the service control manager, not by the user from the command prompt or a desktop shortcut.
So high odds that you are just looking at the wrong file. The real one probably ended up being written to c:\windows\system32 (or syswow64). The operating system directories are normally write protected but that doesn't work for a service, they run with a highly privileged account so can litter the hard drive anywhere.
Always use full path names. Using the EventLog instead is highly recommended.

Why thread is stopped when start new process, while application (c#) is still running?

Is there any problem which i have to do carefully when starting new process in multiple thread application?
I tried this in a simple project:
static void Main(string[] args)
{
Process.Start(#"D:\System\Desktop\a.txt");
MessageBox.Show("Success");
}
And it runs perfectly. But when i do it in my big project which use multiple thread, it's thread is stopped working ("a.txt" is opened but "Success" is not shown) while my application (other thread) do well.
What is the problem in this situation?
If you have a Windows.Forms application and you try to show a message-box from a thread that is not the main user-interface thread, the behavior of the message-box is undefined. Meaning, it may or may not show, be inconsistent, or some other problem.
For instance, displaying a message-box from the BackgroundWorker's DoWork event may or may not work. In one case, the message-box-result was always cancel regardless of what button was clicked.
Therefore, if you are using a message-box just for debugging purposes, use a different technique. If you have to show a message-box, call it from the main user-interface thread.
A console-application should normally not have problems displaying message-boxes. Yet, I have had cases where I would have to sleep the thread for 100ms before the message-box call.
Note, as TomTom pointed out, the main user-interface thread is the application's Windows message loop. Which reminds me, I once had to create a Form in a Console application in order to create a Windows message loop, so my application could respond to Windows messages.
This isn't the answer - I can't put all this code in a comment...
This works for me. Tell me how your code differs from this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace Test
{
class Program
{
const string OutputFile = #"E:\Output.txt";
object _lock = new object();
static void Main(string[] args)
{
Program program = new Program();
Thread thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\Test.txt");
thread = new Thread(program.ThreadMethod);
thread.Start(#"E:\DoesntExist.txt");
Console.ReadKey();
}
void ThreadMethod(object filename)
{
String result = RunNormal(filename as string);
lock (_lock)
{
FileInfo fi = new FileInfo(OutputFile);
if (!fi.Exists)
{
try
{
fi.Create().Close();
}
catch (System.Security.SecurityException secEx)
{
Console.WriteLine("An exception has occured: {0}", secEx.Message);
return;
}
}
StreamWriter sw = fi.AppendText();
sw.WriteLine(result);
sw.Close();
}
}
string RunNormal(string fullfilename)
{
try
{
Process.Start(fullfilename);
return fullfilename + "|Success";
}
catch (Exception e)
{
return fullfilename + "|" + e.ToString();
}
}
}
}
The output in Output.txt is:
E:\DoesntExist.txt|System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(String fileName)
at Test.Program.RunNormal(String fullfilename) in E:\Projekti\VS2010\Test\Test\Program.cs:line 59
E:\Test.txt|Success
How much different is your code? Do you call some other methods? How do you process the results?
Make sure Process.Start works. Passing a filename is not good enough in some cases. In you sample code, you would have to set the use-shell property; otherwise, you would have to use cmd start <filename> or equivalent.
Therefore, just start NotePad.exe to make sure Process.Start works. If it does then your problem is the process command and command line.

Categories