Thread.Abort causing deadlock in catch{} statement - c#

I have a set of threaded classes that print different types of documents. The classes use inheritance to share common code. The class constructor requires file name and printer name arguments. A Print() method creates a new worker thread, waits for the worker thread to complete using Thread.Join(timeout) and calls Thread.Abort() on the worker thread if the Join times out. The worker thread starts an application that can open the specified file, causes the file to be sent to printer synchronously (usually using application's Print method) and exits. The worker thread's code is wrapped in a try{} ... catch{} block to deal with any unforeseen crashes of the external application. The catch block contains minimal cleanup and logging.
internal static FilePackage TryPrintDocumentToPdf(string Filename)
{
.....
Logging.Log("Printing this file using PowerPoint.", Logging.LogLevel.Debug);
printableFormat = true;
fc = new FileCollector(Email2Pdf.Settings.Printer.PdfAttachmentCollectDirectoryObj, FileCollector.CollectMethods.FileCount | FileCollector.CollectMethods.FilesNotInUse | FileCollector.CollectMethods.ProcessExit);
fc.FileCount = 1;
fc.ProcessNames = new string[] { OfficePowerPointExe, Email2Pdf.Settings.Printer.PrinterExe };
fc.Prepare();
using (PowerPointPrinter printer = new PowerPointPrinter(Filename, Email2Pdf.Settings.Printer.PdfAttachmentPrinter))
{
printer.KillApplicationOnClose = true;
printer.Print();
printOk = printer.PrintOk;
}
.....
}
internal abstract class ApplicationPrinter : IDisposable
{
protected abstract string applicationName { get; }
protected string filename;
protected string printer;
protected bool workerPrintOk;
protected bool printOk;
public bool PrintOk { get { return printOk; } }
public bool KillApplicationOnClose { get; set; }
public void Print()
{
System.Threading.Thread worker = new System.Threading.Thread(printWorker);
DateTime time = DateTime.Now;
worker.Start();
if (worker.Join(new TimeSpan(0, Email2Pdf.Settings.Printer.FileGenerateTimeOutMins, 0)))
{
printOk = workerPrintOk;
}
else
{
worker.Abort();
printOk = false;
Logging.Log("Timed out waiting for " + applicationName + " file " + filename + " to print.", Logging.LogLevel.Error);
}
}
protected abstract void Close();
protected abstract void printWorker();
public virtual void Dispose() { Close(); }
}
internal class PowerPointPrinter : ApplicationPrinter
{
private const string appName = "PowerPoint";
protected override string applicationName { get { return appName; } }
private Microsoft.Office.Interop.PowerPoint.Application officePowerPoint = null;
public PowerPointPrinter(string Filename, string Printer)
{
filename = Filename;
printer = Printer;
this.Dispose();
}
protected override void printWorker()
{
try
{
officePowerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
officePowerPoint.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
Microsoft.Office.Interop.PowerPoint.Presentation doc = null;
doc = officePowerPoint.Presentations.Open(
filename,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
doc.PrintOptions.ActivePrinter = printer;
doc.PrintOptions.PrintInBackground = Microsoft.Office.Core.MsoTriState.msoFalse;
doc.PrintOptions.OutputType = Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides;
doc.PrintOut();
System.Threading.Thread.Sleep(500);
doc.Close();
//Marshal.FinalReleaseComObject(doc);
doc = null;
workerPrintOk = true;
}
catch (System.Exception ex)
{
Logging.Log("Unable to print PowerPoint file " + filename + ". Exception: " + ex.Message, Logging.LogLevel.Error);
Close();
workerPrintOk = false;
}
}
protected override void Close()
{
try
{
if (officePowerPoint != null)
officePowerPoint.Quit();
Marshal.FinalReleaseComObject(officePowerPoint);
officePowerPoint = null;
if (KillApplicationOnClose)
Utility.KillProcessesByName(OfficePowerPointExe);
}
catch { }
}
}
I found my application non-responsive, with the main thread in a Sleep/Wait/Join at the Thread.Abort() line. I do not recall the status of the worker thread, but the logging that was supposed to be performed in the catch{} block did not take place. (I Attached to my process with VS2010 after I found it non-responsive).
I refer to the following Note from the Thread.Abort Method:
The thread that calls Abort might block if the thread that is being
aborted is in a protected region of code, such as a catch block,
finally block, or constrained execution region. If the thread that
calls Abort holds a lock that the aborted thread requires, a deadlock
can occur.
I believe I have a dead-locking issue because (1) it does not always happen, and (2) because of the Note on MSDN (above).
The Note appears to suggest that using try{} ... catch{} is NEVER safe inside a thread if the thread can be Abort()'ed. Is this true?
I do not see how I can avoid using Abort() in my scenario. Will using Thread.Interrupt() instead make any difference?
How to I fix the dead-locking issue I have?
BackgroundWorker does not work for me because I do not need progress reporting and, more importantly, it is possible that my worker thread will block indefinitely when it executes third party applications. For the same reason, I cannot ask my thread to terminate, but have one option only - to ruthlessly Abort() the worker thread.

Your mechanism using Thread.Abort() is not a good one. In fact, calling Thread.Abort() should be avoided.
The thread that calls Abort might block if the thread that is being
aborted is in a protected region of code, such as a catch block,
finally block, or constrained execution region. If the thread that
calls Abort holds a lock that the aborted thread requires, a deadlock
can occur. Ref.
Instead, use a BackgroundWorker which supports cancellation, progress reporting (and auto marshalling onto UI thread in completed event).

It looks to me like you are basically remote-controlling the PowerPoint application in order to print a PowerPoint document. Thus you could be subject to any dialog boxes that the application put up (or tried to put up) on the screen. If this whole thing is being run in the background (e.g. on a server), there may not be a user to dismiss any such dialogs, so that could explain part of the issue. My recommendation would be to look into third-party libraries that would allow you to load a PPT file and print it (or convert it to PDF and print that) without having to rely on the PowerPoint application. Then you wouldn't have to wait on an external app outside your control and you woudln't have to resort to forcefully aborting threads.

I think I found a solution by making the following changes:
Do not call Thread.Abort() if we know that the worker thread is executing a catch{} block (see protected volatile bool isPrinting below).
Use a separate thread to call Thread.Abort() and encourage a context switch with Sleep(0) (see private void AbortPrintWorker() below).
internal abstract class ApplicationPrinter : IDisposable
{
protected abstract string applicationName { get; }
protected string filename;
protected string printer;
protected bool workerPrintOk;
protected bool printOk;
public bool PrintOk { get { return printOk; } }
public bool KillApplicationOnClose { get; set; }
protected System.Threading.Thread worker;
protected volatile bool isPrinting;
public void Print()
{
worker = new System.Threading.Thread(printWorker);
DateTime time = DateTime.Now;
worker.Start();
if (worker.Join(new TimeSpan(0, Email2Pdf.Settings.Printer.FileGenerateTimeOutMins, 0)))
{
printOk = workerPrintOk;
}
else
{
AbortPrintWorker();
printOk = false;
Logging.Log("Timed out waiting for " + applicationName + " file " + filename + " to print.", Logging.LogLevel.Error);
}
}
protected abstract void printWorker();
public abstract void Dispose();
private void AbortPrintWorker()
{
System.Threading.Thread abortThread = new System.Threading.Thread(abortWorker);
if (isPrinting)
{
abortThread.Start();
System.Threading.Thread.Sleep(0);
abortThread.Join();
}
else
{
worker.Join();
}
}
private void abortWorker()
{
worker.Abort();
worker.Join();
}
}
internal class PowerPointPrinter : ApplicationPrinter
{
private const string appName = "PowerPoint";
protected override string applicationName { get { return appName; } }
private Microsoft.Office.Interop.PowerPoint.Application officePowerPoint = null;
public PowerPointPrinter(string Filename, string Printer)
{
filename = Filename;
printer = Printer;
this.Dispose();
}
protected override void printWorker()
{
try
{
isPrinting = true;
officePowerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
officePowerPoint.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
Microsoft.Office.Interop.PowerPoint.Presentation doc = null;
doc = officePowerPoint.Presentations.Open(
filename,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
doc.PrintOptions.ActivePrinter = printer;
doc.PrintOptions.PrintInBackground = Microsoft.Office.Core.MsoTriState.msoFalse;
doc.PrintOptions.OutputType = Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides;
doc.PrintOut();
System.Threading.Thread.Sleep(500);
doc.Close();
Marshal.FinalReleaseComObject(doc);
doc = null;
workerPrintOk = true;
isPrinting = true;
}
catch (System.Exception ex)
{
isPrinting = false;
Logging.Log("Unable to print PowerPoint file " + filename + ". Exception: " + ex.Message, Logging.LogLevel.Error);
workerPrintOk = false;
}
}
public override void Dispose()
{
try
{
if (officePowerPoint != null)
officePowerPoint.Quit();
Marshal.FinalReleaseComObject(officePowerPoint);
officePowerPoint = null;
if (KillApplicationOnClose)
Utility.KillProcessesByName(OfficePowerPointExe);
}
catch { }
}
}
AbortPrintWorker() creates a separate thread to call Abort() on the worker thread. I believe this deals with the issue highlighted in the Note on Abort():
The thread that calls Abort might block if the thread that is being
aborted is in a protected region of code, such as a catch block,
finally block, or constrained execution region. If the thread that
calls Abort holds a lock that the aborted thread requires, a deadlock
can occur.
Is this correct?

Related

Non-blocking dispatcher thread with Control and System.Threading.Thread

The problem is that dispatcher blocks the MainWindow thread until it is executed all of the queued AddOutputLine()s. Can these calls be non-blocking for a wpf control?
//TaskPage.xaml.cs
[MTAThread]
public void AddOutputLine(string line = "")
{
try {
Session.TaskPage.Dispatcher.BeginInvoke(new Action(delegate
{
txtOutput.Text += "[" + DateTime.Now + "] " + line + Environment.NewLine;
txtOutput.ScrollToEnd();
}));
} catch (Exception ex) {
Program.SetCurrentStatus("Error occured : "+ex.Message);
}
}
In this class that invokes the Action parameter:
//BackgroundUploader.cs
private Action<string> _log;
public BackgroundUploader(Client client, Action<string> log = null)
{
_log = log;
_client = client;
}
while doing this:
//BackgroundUploader.cs
public void RunThreadProc()
{
try
{
_log?.Invoke("Timeout interval set for BackGround Task.");
while (!Stop)
{
//the problematic blocking of WPF Control starts here
var inOutFiles = CreateXML(_sqlStrings, _outputNames, _outputDirectory);
Send(inOutFiles, _outputDirectory);
LastRunTime = DateTime.Now;
_log?.Invoke($"Waiting for {_waitMinutes} minutes...");
//the Control returns to processing messages
System.Threading.Thread.Sleep((int)TimeSpan.FromMinutes(_waitMinutes).TotalMilliseconds);
}
_log?.Invoke("BackGroundTask canceled.");
}
catch (Exception ex)
{
_log?.Invoke($"Error while executing background task: {ex.Message}");
}
}
The RunThreadProc() is called from Control like this:
//TaskPage.xaml.cs
_backgroundThread = new System.Threading.Thread(bU.RunThreadProc);
_backGroundThread.Start();
I also cannot use Bcl.Async because it's failing on Task result with method that uses async modifier on Windows Server 2003 target OS.
edit: I encountered the problem inside of the background class, its methods run synchronously, even though they are in a different thread.

Logging best approaches

I'm preparing to start developing a new application which has to connect to different API's of different machines which will make the application fairly complicated.
What approaches are there to implement logging into my application, and what are their respective advantages and disadvantages? To be more precise, I'm looking into the best way to export the logs to an external logfile.
Details:
Currently I'm using a system where I add a logentry to a list of logs which I then process line by line through a backgroundworker.
My idea behind this is that by putting the logsprocessing on a different thread I will not disturb the main UI-thread or any other threads for that matter.
However, I think that I'm still using unnecessary resources in my UI-thread because I'm compiling and adding my logentry on the UI-thread. Should I put the actual entry as well on a different thread?
Here is the code that I currently use:
List<LoggingClass> Logs;
BackgroundWorker bgLogs;
public void startLogging()
{
// INITIALIZE THE BACKGROUND WORKER
bgLogs = new BackgroundWorker();
// SET UP THE BACKGROUND WORKER
bgLogs.WorkerSupportsCancellation = true;
bgLogs.WorkerReportsProgress = false;
bgLogs.DoWork += bgLogs_DoWork;
// START THE BACKGROUND WORKER
bgLogs.RunWorkerAsync();
}
private void bgLogs_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (worker.IsBusy == true)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
if (Logs != null && Logs.Count > 0)
{
processLogs();
}
Thread.Sleep(1000);
}
}
}
public void log(DateTime logTimeStamp, LogType logType, string logContent)
{
// CHECK IF THE LIST OF LOGS EXISTS
if (Logs == null)
{
Logs = new List<LoggingClass>();
}
// CREATE A TEMPORARY LOG ENTRY
LoggingClass tmpLog = new LoggingClass();
tmpLog.TimeStamp = logTimeStamp;
tmpLog.Type = logType;
tmpLog.Content = logContent;
// ADD THE LOG TO THE LIST OF LOGS
Logs.Add(tmpLog);
// CLEAR THE TEMPRARY LOG
tmpLog = null;
}
private void processLogs()
{
string logOutput = string.Empty;
do
{
// CREATE A TEMPORARY LOG ENTRY
LoggingClass tmpLog = Logs[0];
// APPEND THE LOG DATA INTO THE TEMPORARY STRING
logOutput += string.Format("[{0}]\t{1}\t{2}", tmpLog.TimeStamp.ToString("HH:mm:ss.fff"), tmpLog.Type.ToString(), tmpLog.Content) + Environment.NewLine;
// REMOVE THE FIRST LOG ENTRY
Logs.RemoveAt(0);
} while (Logs.Count > 0);
// GET THE FILENAME FOR THE LOGGING FILE
DateTime currentDate = DateTime.Now;
string logFile = Path.GetFullPath(Path.Combine(Application.ExecutablePath, #"..\LOGS\" + currentDate.ToString("yyyyMMdd") + "_log.log"));
// CHECK AND CREATE IF NEEDED THE LOGS DIRECTORY
Directory.CreateDirectory(Path.GetDirectoryName(logFile));
// ADD THE LOGGINGCONTENT TO THE LOG FILE
File.AppendAllText(logFile, logOutput, Encoding.UTF8);
}
class LoggingClass
{
public DateTime TimeStamp { get; set; }
public LogType Type { get; set; }
public string Content { get; set; }
}
Are there any issues with using this method?

Windows Service: The calling thread cannot access this object because a different thread owns it

Trying to convert XML files using XSL and printing the output. However, receiving the following message: The calling thread cannot access this object because a different thread owns it.
To set an interval for checking files, added a timer to the OnStart.
if (findPrinter() > 0)
{
System.Timers.Timer printNetterCheck = new System.Timers.Timer();
printNetterCheck.Elapsed += new ElapsedEventHandler(OnTimedEvent);
printNetterCheck.Interval = 30000;
printNetterCheck.Enabled = true;
}
The OnTimedEvent:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
getFiles();
}
If any files available, call print:
foreach (string file in files)
{
try
{
StringWriter xslTransformResult = new StringWriter();
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xslPath);
xslt.Transform(file, null, xslTransformResult);
if (print(xslTransformResult) == 1)
{
//do some things
The print function:
private int print(StringWriter transformedXML)
{
//assume OK
int rc = 1;
try
{
StringReader printNetterStreamReader = new StringReader(transformedXML.ToString());
PrintSystemJobInfo printNetterJob = printer.AddJob("PrintNetterPrint");
Stream printNetterStream = printNetterJob.JobStream;
Byte[] printNetterByteBuffer = UnicodeEncoding.Unicode.GetBytes(printNetterStreamReader.ReadToEnd());
printNetterStream.Write(printNetterByteBuffer, 0, printNetterByteBuffer.Length);
printNetterStream.Close();
}
catch (Exception e)
{
//return fail
rc = -1;
eventLog.WriteEntry("Error printing: " + e.Message, EventLogEntryType.Error);
}
return rc;
}
When calling print I receive the thread error. Found some stuff about Dispatchers etc.. but those are not available when using services.
Check PrintQueue.AddJob.
The method makes a COM call which requires the application be running in a single apartment (STA). The easiest way to fix that is to add STAThreadAttribute to Main which will force the application to run in a single thread. If you need multithreading in your application then you will need to implement the necessary plumbing to run the PrintQueue separately on an STA Thread.
// Create a factory to hold your printer configuration
// So that it can be retrieved on demand
// You might need to move the findPrinter() logic
public class PrintQueueFactory
{
private static PrintQueue _instance = new PrinterQueue(/* Details */);
public static PrintQueue PrintQueue { get { return _instance; } }
}
private int print(StringWriter transformedXML)
{
//assume OK
int rc = 1;
try
{
var printer = PrintQueueFactory.PrintQueue;
var staThread = new Thread(() => Print(printer, transformedXML.ToString()));
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();
}
catch (Exception e)
{
//return fail
rc = -1;
eventLog.WriteEntry("Error printing: " + e.Message, EventLogEntryType.Error);
}
return rc;
}
private static void Print(PrintQueue printer, string lines)
{
using(var printNetterJob = printer.AddJob("PrintNetterPrint"))
using(var printNetterStreamReader = new StringReader(lines))
using(var printNetterStream = printNetterJob.JobStream)
{
Byte[] printNetterByteBuffer = UnicodeEncoding.Unicode.GetBytes(printNetterStreamReader.ReadToEnd());
printNetterStream.Write(printNetterByteBuffer, 0, printNetterByteBuffer.Length);
}
}
maybe, as you are using a Timer control, it is related with multi-threading, maybe you should check if an Invoke is Required (InvokeRequired) in the Timer.Elapsed event handler.
If so, you should create a delegate to call this function, so it can be executed in the right thread.
Check this Invoke-Required question

Control.BeginInvoke does not call delegate when UI function is idle

I am modifying a windows desktop application that works with some external hardware. When the user activates the hardware from the application a progress (UI) form is started. This form creates a thread that performs all of the work with the hardware. The problem comes when I try to report progress back to the UI thread. It appears that the first of my Control.BeginInvoke ("Negotiating message") works fine. However, the second one (first adjustment to progressbar) never seems to call it's delegate and as a result the application locks up on the subsequent endinvoke. I believe the issue is that the GUI is now in an idle state, but I am not sure how to fix the situation. Any help would be appreciated. Code found below:
In the UI Load Method Thread:
private void frmTwainAquire_Load(object sender, EventArgs e)
{
try
{
//Show the GUI
this.Visible = showGUI;
pbScanningProgress.Value = 0;
btnCancel.Enabled = false;
btnCancel.Visible = false;
// Set the delegates.
SetScanMessageDelegate = new SetScanMessage(this.SetScanMessageMethod);
SetRegistrationMessageDelegate = new SetRegistrationMessage(this.SetRegistrationMessageMethod);
AddScanProgressDelegate = new AddScanProgress(this.AddScanProgressMethod);
AddRecogProgressDelegate = new AddRecogProgress(this.AddRecogProgressMethod);
// Set progress bars.
pbScanningProgress.Value = 0;
pbRecognition.Value = 0;
abortScan = false;
// Create thread here!
twainInstance = new rScan.Twain();
rScanning = new rScanThread(this, twainInstance);
// Start the thread.
rScanning.tScan = new Thread(rScanning.Scan);
rScanning.tScan.Start();
}
catch (Exception ex)
{
// Error checking here.
}
}
Delegate Methods:
public void SetScanMessageMethod(string scanMessage)
{
this.lblScanMessage.Text = scanMessage;
}
public void SetRegistrationMessageMethod(string recogMessage)
{
this.lblRecognition.Text = recogMessage;
}
public void AddScanProgressMethod(int progress)
{
this.pbScanningProgress.Value += progress;
}
public void AddRecogProgressMethod(int progress)
{
this.pbRecognition.Value += progress;
}
Thread method that is giving the problem. Please note that the thread is in a different class then the previous two code blocks (both are in the UI class):
public class rScanThread : IMessageFilter
public void Scan()
{
// Set progress bar message.
IAsyncResult result;
if (frmTwainAquireInstance.lblScanMessage.IsHandleCreated && frmTwainAquireInstance.lblScanMessage.InvokeRequired)
{
result = frmTwainAquireInstance.lblScanMessage.BeginInvoke(frmTwainAquireInstance.SetScanMessageDelegate, "Negotiating Capabilities with Scanner.");
frmTwainAquireInstance.lblScanMessage.EndInvoke(result);
}
else
{
frmTwainAquireInstance.lblScanMessage.Text = "Negotiating Capabilities with Scanner.";
}
// Start the intialization of the rScan process.
bool intializeSuccess = twainInstance.Initialize(frmTwainAquireInstance.Handle);
// If the process could not be started then quit.
if (!intializeSuccess)
{
frmTwainAquireInstance.Close();
return;
}
if (frmTwainAquireInstance.pbScanningProgress.IsHandleCreated && frmTwainAquireInstance.pbScanningProgress.InvokeRequired)
{
result = frmTwainAquireInstance.pbScanningProgress.BeginInvoke(frmTwainAquireInstance.AddScanProgressDelegate, 33);
frmTwainAquireInstance.pbScanningProgress.EndInvoke(result); // Lock up here.
}
else
{
frmTwainAquireInstance.pbScanningProgress.Value += 33;
}
// Do more work after. The code never makes it this far.
} // End of rScanThread.Scan()

C# Making a program finish processes before advancing

In C#, how do you make a program only process one thing at a time? I've been working on a patching system, and I think I have the coding all correct but can't test it because a lot of the functions are trying to process all at once when they need to be processing in an order. The program doesn't even let the display shown up before it starts trying to process everything. Because none of them return a value other then the main function all the functions are set to void. I thought about maybe using a return value inside of a loop to make sure the program is finished with that step first before moving on but it still leaves the problem of the program not even showing up until everything is done processing which its suppose to show the progress of everything. Any suggestions of tips?
Edit: I don't know what to post in the code, so im posting all the main functions:
public void DSP_Load(object sender, EventArgs e)
{
if (v1 >= v2)
{
File_Progress_Title.Text = "100%";
Update_Status.Text = "Divine Shadows is currently up to date.";
Application.DoEvents();
Process.Start("Divine Shadows.exe");
Close();
}
else
{
Update_Status.Text = "Checking For Updates...";
Application.DoEvents();
if (!Directory.Exists(tempFilePath))
{
Directory.CreateDirectory(tempFilePath);
}
using (SqlCon = new MySqlConnection(connString))
{
SqlCon.Open();
string command = "SELECT * FROM version where version > '" + v1 + "' ORDER BY version LIMIT 1";
MySqlCommand GetLatestVersion = new MySqlCommand(command, SqlCon);
using (MySqlDataReader DR = GetLatestVersion.ExecuteReader())
{
while(DR.Read())
{
do
{
string LatestVersion = Convert.ToString(DR.GetValue(1));
string WebURL = Convert.ToString(DR.GetValue(2));
update.DownloadFileAsync(new Uri(WebURL), tempFilePath + "patch" + LatestVersion + ".zip");
update.DownloadProgressChanged += new DownloadProgressChangedEventHandler(download);
update.DownloadFileCompleted += new AsyncCompletedEventHandler(extration);
Application.Restart();
}
while (v1 < v2);
Process.Start("Divine Shadows.exe");
Close();
}
}
}
}
}
public void download(object sender, DownloadProgressChangedEventArgs e)
{
if (v1 >= v2)
{
File_Progress_Title.Text = "100%";
Update_Status.Text = "Divine Shadows is currently up to date.";
Application.DoEvents();
Process.Start("Divine Shadows.exe");
Close();
}
else
{
Update_Status.Text = "Downloading Updates...";
Application.DoEvents();
File_Progress_Display.Value = e.ProgressPercentage;
File_Progress_Title.Text = Convert.ToString(e.ProgressPercentage) + "%";
}
}
public void extration(object sender, AsyncCompletedEventArgs e)
{
if (v1 >= v2)
{
File_Progress_Title.Text = "100%";
Update_Status.Text = "Divine Shadows is currently up to date.";
Application.DoEvents();
Process.Start("Divine Shadows.exe");
Close();
}
else
{
Update_Status.Text = "Installing Updates, Please Wait...";
Application.DoEvents();
UnzipFile(extactFile, extractLocation);
}
}
public static void UnzipFile(string extactFile, string extractLocation)
{
try
{
FastZip fastZip = new FastZip();
fastZip.CreateEmptyDirectories = false;
fastZip.ExtractZip(extactFile, extractLocation, FastZip.Overwrite.Always, null, null, null, false);
}
catch (Exception ex)
{
throw new Exception("Error unzipping file \"" + extactFile + "\"", ex);
}
File.Delete(extactFile);
}
Your problem is not WebClient() specific, its about how your application is working with threads.
In general, winforms applications have one GUI Thread. This thread is used to executed your methods and also updating the user interface. If you start a long term process, the gui thread gets locked till the operation is finished. Thats the reason why your display is not shown.
You can solve that problem by implementing the BackgroundWorker. On that website you can also find an example how to implement it. Let the BackgroundWorker do your patching process and use events inside the BackgroundWorker.RunWorkerAsync() method to update your GUI.
If you are using c#4 or newer you can use the Task Parallel Library to perform tasks asynchronously, thus leaving your UI response while thing are being downloaded. First of all you need a reference:
using System.Threading.Tasks;
And some code:
public void YourMainFunction()
{
var urls = new List<string>();
urls.Add("http://google.com");
urls.Add("http://yahoo.com");
foreach(var url in urls)
{
Task.Factory.StartNew<DownloadResult>(() =>
DownloadIt(url))
.ContinueWith(WorkDone, TaskScheduler.FromCurrentSynchronizationContext());
}
}
private class DownloadResult
{
public string Url {get; set;}
public string Result {get; set;}
}
private DownloadResult DownloadIt(string url)
{
var downloadResult = new DownloadResult{ Url = url };
var client = new WebClient();
downloadResult.Result = client.DownloadString(url);
return downloadResult;
}
private void WorkDone(Task<DownloadResult> task)
{
if(task.IsFaulted)
{
//An exception was thrown
MessageBox.Show(task.Exception.ToString());
return;
}
//Everything went well
var downloadResult = task.Result;
//Here you can update your UI to reflect progress.
MessageBox.Show(downloadResult.Result);
}

Categories

Resources