Background worker still freezing ui - c#

Even tho I setup a background worker, I can't seem to make it not freeze the UI.
I looked around online but could not find a fix. I have tried creating basic threads and that worked but I need to also update UI stuff as it runs so thats why I switched to backgroundworkers, but if you could help me figure out how to use a basic thread and get that working that would be ok.
using MediaToolkit;
using MediaToolkit.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using VideoLibrary;
namespace ConsoleApp1
{
public partial class Form1 : Form
{
private List<VideoInfo> vids = new List<VideoInfo>();
private List<VideoInfo> failedVids = new List<VideoInfo>();
private int threads = 0;
BackgroundWorker worker;
private delegate void DELEGATE();
static void Main(string[] args)
{
Form1 form = new Form1();
form.ShowDialog();
}
public Form1()
{
InitializeComponent();
worker = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
if(threads == 0)
{
progressBar1.Maximum = vids.Count;
progressBar1.Value = 0;
failedVids.Clear();
worker.DoWork += doWork;
worker.RunWorkerAsync();
//downloadVid();
}else
{
Console.WriteLine("Waiting for threads" + threads);
}
/*Thread thread1 = new Thread(new ThreadStart(downloadVid));
thread1.Start();*/
}
private void button1_Click_1(object sender, EventArgs e)
{
VideoInfo vid = new VideoInfo(tbLink.Text, tbTitle.Text, tbArtist.Text);
vids.Add(vid);
tbLink.Clear();
tbTitle.Clear();
listView1.Items.Add(vid.getLink());
}
private int downloadThread(int i)
{
Console.WriteLine(i);
var source = #"C:\Users\derri\Downloads\DownloadTest\";
var youtube = YouTube.Default;
VideoInfo vidInfo = vids[(int) i];
var vid = youtube.GetVideo(vidInfo.getLink());
Console.WriteLine(vidInfo.getLink());
try
{
File.WriteAllBytes(source + vid.FullName, vid.GetBytes());
}
catch (Exception e)
{
failedVids.Add(vids[(int)i]);
Console.WriteLine(e);
goto End;
}
var inputFile = new MediaFile { Filename = source + vid.FullName };
var outputFile = new MediaFile { Filename = $"{source + vidInfo.getArtist() + " - " + vidInfo.getTitle()}.mp3" };
using (var engine = new Engine())
{
engine.GetMetadata(inputFile);
engine.Convert(inputFile, outputFile);
}
File.Exists(outputFile.Filename);
File.Delete(inputFile.Filename);
setTags(vidInfo.getArtist(), vidInfo.getTitle());
End:
threads--;
return 1;
}
private void doWork(object sender, DoWorkEventArgs e)
{
Delegate del = new DELEGATE(downloadVid);
this.Invoke(del);
}
private void downloadVid()
{
int prog = 0;
for (int i = 0; i < vids.Count; i++)
{
Console.WriteLine(i);
while ((threads > 5)) { }
Thread thread = new Thread(() => { prog += downloadThread(i); });
thread.Start();
threads++;
System.Threading.Thread.Sleep(1000);
//thread.Join();
/*ParameterizedThreadStart start = new ParameterizedThreadStart(downloadThread);
Thread t = new Thread(start);
t.Start(i);
progressBar1.Value++;
threads++;*/
}
while (threads > 0){}
foreach (VideoInfo failedVid in failedVids)
{
listView2.Items.Add(failedVid.getLink());
}
listView1.Clear();
vids.Clear();
}
private void setTags(string artist, string title)
{
TagLib.File file = TagLib.File.Create("C:\\Users\\derri\\Downloads\\DownloadTest\\" + artist + " - " + title + ".mp3");
file.Tag.Artists = (new String[] { artist });
file.Tag.Title = (title);
file.Save();
}
}
}

this.Invoke will run on the UI thread.
Look into how to use either ReportProgress or RunWorkerCompleted events of the BackgroundWorker class. The both allow you to pass data from the background thread to the UI thread.

Related

Why would my timer does not dynamically display the file size?

So basically, I m making a program where 25 tasks write the same random task to one file until it reaches a certain size, and I would like to dynamically display the file size of the selected file in 0.5 seconds interval, so I made a timer hooked with Timer_Elapsed which should be executed every 0.5 seconds and display on UI which I specify on mainWindow.xaml on the textblock x:Name="fileSize", so I placed the createTimer function in to btnGo_Click function in mainwindow.xaml.cs so the event would extract the right fileInfo of the selectedFile. Any advice for my wrong would be appreciated. I'm also sharing the FileIO class in case it is needed, so they are full solutions. Even aside from the questions I asked, any general advice to better my code would be appreciated because I need to get a grasp of the good code example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.Threading;
namespace WriteFile
{
internal class FileIO
{
private object _lock = new object();
public volatile bool sizeReached = false;
private StreamWriter sw = null;
Mutex mut = null;
public FileIO()
{
if (!Mutex.TryOpenExisting("MyMutex", out mut))
{
mut = new Mutex(true, "MyMutex");
mut.ReleaseMutex();
}
}
internal void WriteFile(string FilePath)
{
while (!sizeReached)
{
mut.WaitOne();
try
{
using (sw = new StreamWriter(FilePath, true))
{
sw.WriteLine(Guid.NewGuid());
}
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
finally
{
if (sw != null)
{
sw.Close();
}
}
mut.ReleaseMutex();
}
}
internal void SizeMonitor(string FPath, int MaxSize, Task[] tasks)
{
FileInfo fi = null;
while (!sizeReached)
{
if (File.Exists(FPath))
{
fi = new FileInfo(FPath);
if (fi.Length >= MaxSize)
{
sizeReached = true;
}
}
if (sizeReached)
{
foreach (Task task in tasks)
{
task.Wait();
}
}
Thread.Sleep(1);
}
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
mainWindow.xaml
<Window x:Class="WriteFile.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WriteFile"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock x:Name="fileSize"/>
<TextBox Name ="TargetSize" VerticalAlignment="Center" FontSize="20">
</TextBox>
<Label Content="Target Size" HorizontalAlignment="Left" Margin="92,150,0,0" VerticalAlignment="Top"/>
<Button Name ="btnGo" Content="Write to file" HorizontalAlignment="Left" Margin="92,267,0,0" VerticalAlignment="Top" Width="100" Click="btnGo_Click"/>
</Grid>
</Window>
mainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using Microsoft.Win32;
using System.ComponentModel;
using System.Timers;
using System.Threading;
using System.Runtime.CompilerServices;
namespace WriteFile
{
public partial class MainWindow : Window
{
System.Timers.Timer timer = new System.Timers.Timer();
Task[] tasks;
Task MonitorTask;
static FileIO fio = new FileIO();
static string fPath;
static FileInfo fileInfo;
public MainWindow()
{
InitializeComponent();
CreateTimer();
}
public void CreateTimer()
{
var timer = new System.Timers.Timer(500); // fire every 0.5 second
timer.Enabled = true;
timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
fileSize.Text = fileInfo.Length.ToString();
}
private void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
Stream myStream;
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
if ((myStream = saveFileDialog.OpenFile()) != null)
{
StreamWriter sw = new StreamWriter(myStream);
sw.Write(" your text");
myStream.Close();
}
int NoOfTasks = 25;
int Target = Convert.ToInt32(TargetSize.Text);
fPath = saveFileDialog.FileName;
tasks = new Task[NoOfTasks];
fio.sizeReached = false;
fileInfo = new FileInfo(fPath);
for (int i = 0; i < NoOfTasks; i++)
{
tasks[i] = new Task(() => fio.WriteFile(fPath));
tasks[i].Start();
}
MonitorTask = new Task(() => fio.SizeMonitor(fPath, Target, tasks));
MonitorTask.Start();
}
}
}
As mentioned by others, FileInfo.Length value is pre-cached. If the associated file has changed, the FileInfo requires to refresh those cached values. To accomplish this, you must explicitly call the FileInfo.Refresh method. FileInfo does not monitor the associated file.
Furthermore, System.Threading.Timer executes the callback on a background thread. In WPF you can only reference a DispatcherObject (for example TextBlock) from a dispatcher thread (UI thread). For this reason you should use the DispatcherTimer. DispatcherTimer executes the callback on the dispatcher thread.
Additionally, you would want to stop the timer once the writing to the file is completed.
You should not use Task.Start. Instead of Task.Start and a Mutex you should simply await the Task. In your context, you can await a collection of Task objects using Task.WhenALl.
And instead of creating Task explicitly, you should use the async API of the StreamWriter.
You actually don't have concurrent code (neither would you benefit from it): the Mutex, declaration of a volatile field and the lock object are redundant. Also closing the StreamWriter explicitly in a finally block is not needed as you already use a using-block to handle the lifetime of the instance.
There are some more flaws in your code (some logical issues for instance). I have refactored your version to eliminate some of the issues for example by using the asynchronous StreamWriter API and async/await.
Because it is difficult to make any sense of your code (due to the lack of context) it is not possible to further improve it. For example, creating a single concatenated string (consisting of e.g. 25 values) would result in a single file write (and resource allocation), which would significantly improve the overall performance.
FileIO.cs
namespace WriteFile
{
internal class FileIO
{
public bool IsSizeReached { get; private set; }
public FileIO()
{
}
internal async Task WriteToFileAsync(string filePath)
{
if (this.IsSizeReached)
{
return;
}
using (var sw = new StreamWriter(filePath, true))
{
await sw.WriteLineAsync(Guid.NewGuid());
}
}
internal async Task SizeMonitorAsync(string fPath, int maxSize, IEnumerable<Task> tasks)
{
FileInfo fi = new FileInfo(fPath);
this.IsSizeReached = fi.Length >= maxSize;
if (this.IsSizeReached)
{
return;
}
await Task.WhenAll(tasks);
MessageBox.Show(fi.Length.ToString());
MessageBox.Show("Done");
}
}
}
MainWindow.xaml.cs
namespace WriteFile
{
public partial class MainWindow : Window
{
private DispatcherTimer Timer { get; }
private FileIO Fio { get; }
private FileInfo DestinationFileInfo { get; }
public MainWindow()
{
InitializeComponent();
this.Fio = new FileIO();
}
public void StartTimer()
{
this.Timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(500),
DispatcherPriority.Background,
OnTimerElapsed,
this.Dispatcher);
this.Timer.Start();
}
public void StopTimer() => this.Timer.Stop();
private void OnTimerElapsed(object sender, EventArgs e)
{
this.fileSize.Text = this.DestinationFileInfo.Length.ToString();
}
private async void btnGo_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.ShowDialog();
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
if (File.Exists(saveFileDialog.FileName))
{
File.Delete(saveFileDialog.FileName);
}
Stream myStream;
if ((myStream = saveFileDialog.OpenFile()) != null)
{
// Dispose StreamWriter and underlying Stream
using (var sw = new StreamWriter(myStream))
{
await sw.WriteAsync(" your text");
}
}
int noOfTasks = 25;
// TODO::You must validate the input to prevent invalid or unreasonable values.
// Currently, this line will throw and crash the application if the input is invalid (not numeric).
int target = Convert.ToInt32(TargetSize.Text);
string fPath = saveFileDialog.FileName;
var tasks = new List<Task>();
this.DestinationFileInfo = new FileInfo(fPath);
StartTimer();
for (int i = 0; i < noOfTasks; i++)
{
Task writeToFileTask = this.Fio.WriteToFileAsync(fPath);
tasks.Add(writeToFileTask);
}
await this.Fio.SizeMonitorAsync(fPath, target, tasks));
StopTimer();
}
}
}
Timers.Timer works asynchronously and it raises the Elapsed event on the pool thread if SynchronizingObject is not set.
And work with WPF UI elements can only be done on the thread of their Dispatcher (almost always this is the main UI thread of the application).
Two solutions out of many possible:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Asynchronously getting the right data
string text = fileInfo.Length.ToString();
// Using the Window's Dispatcher
// to setting ready-made data to UI elements.
Dispatcher.BeginInvoke(() => fileSize.Text = text);
}
The second option is without using a timer:
public MainWindow()
{
Initialized += RefreshAsync;
InitializeComponent();
}
public async void RefreshAsync(object? sender, EventArgs e)
{
while(true)
{
string text = await Task.Run(() => fileInfo.Length.ToString());
fileSize.Text = text;
await Task.Delay(500);
}
}

Why when downloading files using webclient downloadfileasync I'm getting exception on the progressBar value that it's negative value?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
namespace DownloadFilesFromSite
{
public partial class Form1 : Form
{
private Queue<string> _downloadUrls = new Queue<string>();
private List<string> urls = new List<string>();
private List<string> sources = new List<string>();
private List<string> links = new List<string>();
public Form1()
{
InitializeComponent();
Sources();
}
private void Sources()
{
string link = "https://www.documentingreality.com/forum/f10/several-different-dead-chinese-women-176102/";
for (int i = 2; i < 141; i++)
{
sources.Add(link + "index" + i + ".html");
}
}
private void ReadSourcePage(string fn)
{
var lines = File.ReadAllLines(fn).ToList();
string contains = "https://www.documentingreality.com/forum/attachments/f10/";
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].Contains(contains))
{
int index = lines[i].IndexOf("f10/") + 4;
int index1 = lines[i].IndexOf(".jpg") - index;
string result = lines[i].Substring(index, index1);
links.Add(contains + result + ".jpg");
}
}
}
private void downloadFiles(IEnumerable<string> urls)
{
foreach (var url in urls)
{
_downloadUrls.Enqueue(url);
}
// Starts the download
button1.Text = "Downloading...";
button1.Enabled = false;
progressBar1.Visible = true;
label1.Visible = true;
DownloadFile();
}
private void DownloadFile()
{
if (_downloadUrls.Any())
{
WebClient client = new WebClient();
client.Headers.Add("User-Agent: Other");
client.DownloadProgressChanged += client_DownloadProgressChanged;
client.DownloadFileCompleted += client_DownloadFileCompleted;
var url = _downloadUrls.Dequeue();
string FileName = url.Substring(url.LastIndexOf("/") + 1,
(url.Length - url.LastIndexOf("/") - 1));
client.DownloadFileAsync(new Uri(url), #"E:\dr\htmlsources\" + FileName);
label1.Text = url;
return;
}
// End of the download
button1.Text = "Download Complete";
}
private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// handle error scenario
throw e.Error;
}
if (e.Cancelled)
{
// handle cancelled scenario
}
DownloadFile();
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
downloadFiles(sources);
}
}
}
In the DownloadFile on the line :
client.DownloadFileAsync(new Uri(url), #"E:\dr\htmlsources\" + FileName);
If I copy the url address to the chrome it will show me the source of the page and I can save and download the source page it will be about 700KB
But when clicking the button1 when it start downloading this sources it will throw exception :
ArgumentOutOfRangeException: Value of '-154300' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'.
Parameter name: Value
And if I will not use the progressBar1 at all for testing all the downloaded sources files will be about 25KB instead about 700KB.
I tried to add the line :
client.Headers.Add("User-Agent: Other");
But not seems to fix the exception.
DownloadFileAsync will return immediately and then 'client' will also go out of scope. Read up on how to use async/await functions.

Asynchronous Call to Timer

I am trying to create an application that shows the system statistics but it also executes a timer each second to update the current CPU clock speed. I have tried numerous solutions but none seem to work for me. I will attach my code below. I am using the System.Management DLL to call the stats I am wanting. Thank you!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
namespace ezCPU
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = this.Text + " - v" + Application.ProductVersion;
getCPUInfo();
}
public string convertClockSpeed(string s)
{
double clockSpeed = Convert.ToDouble(s);
double newClockSpeed = (clockSpeed / 1000);
return Math.Round(newClockSpeed, 2).ToString() + " GHz";
}
public void getCPUInfo()
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach (ManagementObject obj in myProcessorObject.Get())
{
string cpuName = obj["Name"].ToString();
txtCPUName.Text = cpuName;
string cpuManufacturer = obj["Manufacturer"].ToString();
if (cpuManufacturer == "GenuineIntel")
{
txtCPUManufacturer.Text = "Genuine Intel";
}
else
{
txtCPUManufacturer.Text = cpuManufacturer;
}
string cpuCores = obj["NumberOfCores"].ToString();
txtCores.Text = cpuCores;
string cpuThreads = obj["ThreadCount"].ToString();
txtThreads.Text = cpuThreads;
string cpuMaxSpeed = obj["MaxClockSpeed"].ToString();
txtClockSpeed.Text = convertClockSpeed(cpuMaxSpeed);
}
}
private void cpuTimer_Tick(object sender, EventArgs e)
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach(ManagementObject obj in myProcessorObject.Get())
{
string currentClockSpeed = obj["CurrentClockSpeed"].ToString();
txtCPUSpeed.Text = convertClockSpeed(currentClockSpeed);
}
}
}
}
This is the result of the code. However, I am unable to move the form around while it is still updating.
Have you tried the windows forms timer? It has the elapsed event that even spins up a background thread to do the work, keeping your ui responsive:
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.elapsed?redirectedfrom=MSDN&view=netframework-4.8
public class Form1: Form
{
public System.Windows.Forms.Timer aTimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
// Create a timer and set a two second interval.
aTimer.Interval = 2000;
// Hook up the tick event for the timer.
aTimer.Tick += OnTimedEvent;
// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;
// Start the timer
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}
private void OnTimedEvent(object sender, EventArgs e)
{
// update your statistics here
}
}
If you want to update the ui there, then the tick event is the way to go: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer.tick?view=netframework-4.8

How to fix a problem with threads which are trying work at the same time?

I have a problem. I made a service which monitoring printing jobs in real time. It didn't worked perfect, but I had no big problem. Now I need to change service into Windows Forms program. And I've got a problem with threads.
Error:
System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'
This error appears on string "PrintQueue.Refresh()".
I can't find where the other thread tries to run.
I tried to set the other thread and start it with MonitoringJobs() procedure but it doesn't work.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Collections.Concurrent;
using System.Data.SqlClient;
using System.Diagnostics;
using System.ServiceProcess;
using System.Management;
using System.Windows;
using System.Printing;
using System.Configuration;
using System.Collections.Specialized;
using System.Threading;
namespace MonitoringPrintJobs
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
timer.Elapsed += GetJobs;
timer.AutoReset = true;
timer.Enabled = true;
}
System.Timers.Timer timer = new System.Timers.Timer(5);
int writeInterval = 1000;
int DefaultWriteInterval = 1000;
bool Logging;
SelectPrinter fSelectPrinter = new SelectPrinter();
ConcurrentDictionary<string, string> QueueJobs = new ConcurrentDictionary<string, string>();//declaration printers jobs dictionary
ConcurrentDictionary<string, DateTime> PrintedJobs = new ConcurrentDictionary<string, DateTime>();
PrintServer printServer = null;
PrintQueueCollection QueuesOnLocalServer = null;
List<PrintQueue> queues = new List<PrintQueue>();
public void MonitoringJobs()
{
//if(queues != null)
foreach (var PrintQueue in queues)
{
PrintQueue.Refresh();
using (var jobs = PrintQueue.GetPrintJobInfoCollection())//wait until collection updates!!!
foreach (var job in jobs)
{
if (!QueueJobs.ContainsKey(job.Name))//if list with printer jobs doesn't contain found job (name)
{//then put name in list with printer jobs
QueueJobs.TryAdd(job.Name, job.JobStatus.ToString());
if (Logging == true)
{
File.AppendAllText(#"D:\Logs\Logging.txt", String.Format("{0} - {1} - {2}{3}", DateTime.Now, job.Name, job.JobStatus, Environment.NewLine));
}
}
else//if list with printer jobs contains found job name
{
if (QueueJobs[job.Name] != job.JobStatus.ToString() && !QueueJobs[job.Name].ToLower().Contains("error"))//if status for this job doesn't exist
{
QueueJobs[job.Name] = job.JobStatus.ToString();//replace job's status
if (Logging == true)
{
File.AppendAllText(#"D:\Logs\Logging.txt", String.Format("{0} - {1} - {2}{3}", DateTime.Now, job.Name, job.JobStatus, Environment.NewLine));
}
}
if (job.JobStatus.ToString().ToLower().Contains("error") && PrintedJobs.ContainsKey(job.Name))
{
var someVar = new DateTime();
PrintedJobs.TryRemove(job.Name, out someVar);
}
}
if (QueueJobs[job.Name].ToLower().Contains("print") && !QueueJobs[job.Name].ToLower().Contains("error"))//if successfully printed
{
PrintedJobs.TryAdd(job.Name, DateTime.Now);
}
}
}
}
private void GetJobs(Object source, System.EventArgs e)
{
writeInterval--;
MonitoringJobs();
if (writeInterval <= 0)
{
writeInterval = DefaultWriteInterval;
PrintedJobs.Clear();
QueueJobs.Clear();
}
}
protected void OnStart()
{
QueuesOnLocalServer = printServer.GetPrintQueues();
writeInterval = 120000;
foreach (var item in fSelectPrinter.SelectetPrinters)
Logging = true;
foreach (var printer in fSelectPrinter.SelectetPrinters)
{
if (string.IsNullOrEmpty(printer))
{
timer.Stop();
Environment.Exit(0);
}
var queue = QueuesOnLocalServer.FirstOrDefault(o => o.FullName.ToUpper() == printer.ToUpper());
if (queue == null)
{
timer.Stop();
Environment.Exit(0);
}
queues.Add(queue);
}
timer.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fSelectPrinter.ShowDialog();
}
private void Form1_Load(object sender, EventArgs e)
{
printServer = new PrintServer();
foreach (PrintQueue pq in printServer.GetPrintQueues())
fSelectPrinter.listBox1.Items.Add(pq.Name);
}
private void button1_Click_1(object sender, EventArgs e)
{
bool StartPrinting = button2.Enabled = false;//turn of select printers form button
if (StartPrinting == false)//StartPrinting == monitoring == true
{
OnStart();
}
else
{
StartPrinting = true;//StartPrinting == monitoring == false
timer.Stop();
}
}
}
}
In this program I tried to get printing jobs statuses and output them in listbox1 and write results with string.Format in file.
On Form1_Load event you are adding things to your fSelectedPrinter.Listbox.
If you items you are adding are coming from a different thread, that will cause an error. Only UI thread can update objects on a form without using SynchornizationContext.
private readonly SynchronizationContext synchronizationContext;
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
Here is an example:
private async void btnListFiles1_Click(object sender, EventArgs e)
{
if (txtDirectory1.Text == "")
{
MessageBox.Show(InfoDialog.SELECT_DIRECTORY,PROGRAM_NAME);
return;
}
if (!Directory.Exists(txtDirectory1.Text))
{
MessageBox.Show(InfoDialog.DIRECTORY_NOT_EXIST, PROGRAM_NAME);
return;
}
try
{
string fileTypes = (txtFileTypes1.Text == "") ? "" : txtFileTypes1.Text;
string[] files = Directory.GetFiles(txtDirectory1.Text.TrimEnd(),
(fileTypes == "") ? "*.*" : fileTypes,
(chkSub1.Checked) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
listBoxFiles.Items.Clear();
progressBar.Step = 1;
progressBar.Value = 1;
progressBar.Maximum = files.Length + 1;
listBoxFiles.BeginUpdate();
if (txtSearchPattern1.Text != "")
{
string searchPattern = txtSearchPattern1.Text.ToLower();
await System.Threading.Tasks.Task.Run(() =>
{
foreach (string file in files)
{
if (file.ToLower().Contains(searchPattern))
{
AddToListBox(file);
}
}
});
}
else
{
await System.Threading.Tasks.Task.Run(() =>
{
foreach(string file in files)
{
AddToListBox(file);
}
});
}
listBoxFiles.EndUpdate();
progressBar.Value = 0;
}
private void AddToListBox(string item)
{
synchronizationContext.Send(new SendOrPostCallback(o =>
{
listBoxFiles.Items.Add((string)o);
progressBar.Value++;
}), item);
}

InvalidOperationException using PFX w/ Windows Forms

I have two exceptions here. Not sure why they occur because I use Form.Invoke to run UI updates on the UI thread. So first,
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Xml;
using System.Windows.Forms;
namespace Toplr
{
using System.Collections.Specialized;
using System.Xml.XPath;
using System.Xml.Linq;
using System.Text;
using System.ServiceModel.Web;
using System.ServiceModel.Syndication;
using System.Net;
using System.Web;
using System.Xml.Schema;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public partial class ToplrForm : Form
{
private readonly Uri SearchBase = new Uri(#"http://www.twine.com/feed/atom/entries/");
private readonly UriTemplate SearchTemplate = new UriTemplate(#"search?type={type}&author={author}");
public ToplrForm()
{
InitializeComponent();
Exiting = false;
TaskContext = new TaskManager();
Items = new AsyncBindingList<Twine>(this);
twineBindingSource.DataSource = Items;
}
private void ToplrForm_Load(object sender, EventArgs e)
{
}
private readonly TaskManager TaskContext;
private readonly AsyncBindingList<Twine> Items;
private bool Exiting;
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("Close()");
Close();
}
private void ToplrForm_FormClosing(object sender, FormClosingEventArgs e)
{
MessageBox.Show("Exiting = tru");
Exiting = true;
//TaskContext.Dispose();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
var sfd = new SaveFileDialog()
{
ValidateNames = true
};
if (sfd.ShowDialog() == DialogResult.OK)
{
using (var xtw = new XmlTextWriter(sfd.FileName, Encoding.UTF8))
{
var xw = XmlWriter.Create(xtw);
xw.WriteStartDocument();
xw.WriteStartElement("opml");
xw.WriteAttributeString("version", "1.1");
xw.WriteStartElement("head");
xw.WriteElementString("title", userNameComboBox.Text);
xw.WriteEndElement();
xw.WriteStartElement("body");
foreach (var row in twineDataGridView.SelectedRows)
{
var twine = (Twine)((DataGridViewRow)row).DataBoundItem;
if (twine != null)
{
xw.WriteStartElement("outline");
xw.WriteAttributeString("text", twine.Title);
xw.WriteAttributeString("type", "link");
xw.WriteAttributeString("url", twine.HtmlAddress);
xw.WriteStartElement("outline");
xw.WriteAttributeString("text", twine.Title);
xw.WriteAttributeString("type", "atom");
xw.WriteAttributeString("url", twine.AtomAddress);
xw.WriteEndElement();
xw.WriteEndElement();
}
}
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndDocument();
xw.Close();
}
}
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("Copyright (C) 2009 Bent Rasmussen");
}
private void accessButton_Click(object sender, EventArgs e)
{
var user = userNameComboBox.Text;
Task.Create(x => ProcessAccount(user));
}
public void ProcessAccount(string user)
{
this.Invoke((Action)(() =>
{
userNameComboBox.Enabled = false;
accessButton.Enabled = false;
toolStripStatusLabel1.Text = "Processing...";
}));
var param = new NameValueCollection();
param.Add("type", "Twine");
param.Add("author", user);
var source = SearchTemplate.BindByName(SearchBase, param);
var wc = new WebClient();
using (var feedStream = wc.OpenRead(source))
{
var reader = XmlReader.Create(feedStream);
var feed = SyndicationFeed.Load(reader);
int c = 0, i = 0;
foreach (var item in feed.Items)
{
this.Invoke((Action)(() =>
{
toolStripProgressBar1.Increment(1);
toolStripStatusLabel1.Text = "Processing...";
}));
if (item.Links.Count != 0)
{
//try
{
ProcessTwine(item);
i++;
}
//catch (Exception)
{
c++;
}
}
if (Exiting)
break;
}
}
this.Invoke((Action)(() =>
{
userNameComboBox.Enabled = true;
accessButton.Enabled = true;
}));
}
private Twine ProcessTwine(SyndicationItem item)
{
var result = new Twine();
result.Title = item.Title.Text;
result.HtmlAddress = item.Links[0].Uri.ToString();
result.AtomAddress = "";
var wc = new WebClient();
var data = wc.DownloadData(result.HtmlAddress);
var stream = new MemoryStream(data);
var readerSettings = new XmlReaderSettings()
{
ProhibitDtd = false,
ValidationType = ValidationType.None,
ValidationFlags = XmlSchemaValidationFlags.None,
};
var reader = XmlReader.Create(stream, readerSettings);
var doc = XDocument.Load(reader);
var htmlNs = (XNamespace)"http://www.w3.org/1999/xhtml";
var root = doc.Root;
var atom = from r in root.Descendants(htmlNs + "head").Descendants(htmlNs + "link")
where r.Attribute("rel").Value == "alternate" && r.Attribute("type").Value == "application/atom+xml"
select r.Attribute("href");
foreach (var e in atom)
{
if (e.Value != "")
{
result.AtomAddress = e.Value;
this.BeginInvoke((Action)(() =>
{
Items.Add(result);
toolStripProgressBar1.Increment(1);
}));
}
break;
}
return result;
}
}
}
This triggers the exception "Cannot access a disposed object" on this fragment
this.Invoke((Action)(() =>
{
toolStripProgressBar1.Increment(1);
toolStripStatusLabel1.Text = "Processing...";
}));
If this fragment is commented out, I run into the next problem - a TargetInvocationException on Program level.
The inner exception of this is an InvalidOperationException.
The code is quite simple, so it should not be hard to implement this, I just new a few hints to move on.
Visual Studio project files.
If the user hits the exit button, the Close() method is called, and the UI starts getting torn down. However, your worker code keeps running and attempts to update the UI, which it can no longer do.
If you centralise all those invoke calls:
public void UpdateUI(Action action) {
if(!Exiting) this.Invoke(action);
}
you can call:
UpdateUI(() =>
{
toolStripProgressBar1.Increment(1);
toolStripStatusLabel1.Text = "Processing...";
});
(etc - all the this.Invoke calls should use UpdateUI instead)
and it should work. Also, make Exiting volatile.

Categories

Resources