in my application i want to add files into my list box.
if my file isn't pcap extension i want to send the file path to my class and convet it to pcap extension and then add this file to my Listbox.
in case i am choose to add namy files the GUI not responding until my application finish to add or convert this file and i wonder how to add the option to do all this via threads.
private void btnAddfiles_Click(object sender, EventArgs e)
{
System.IO.Stream stream;
OpenFileDialog thisDialog = new OpenFileDialog();
thisDialog.InitialDirectory = (lastPath.Length > 0 ? lastPath : "c:\\");
thisDialog.Filter = "(*.snoop, *.pcap, *.cap, *.net, *.pcapng, *.5vw, *.bfr, *.erf, *.tr1)" +
"|*.snoop; *.pcap; *.cap; *.net; *.pcapng; *.5vw; *.bfr; *.erf; *.tr1|" + "All files (*.*)|*.*";
thisDialog.FilterIndex = 1;
thisDialog.RestoreDirectory = false;
thisDialog.Multiselect = true;
thisDialog.Title = "Please Select Source File";
if (thisDialog.ShowDialog() == DialogResult.OK)
{
if (thisDialog.FileNames.Length > 0)
{
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
foreach (String file in thisDialog.FileNames)
{
try
{
if ((stream = thisDialog.OpenFile()) != null)
{
using (stream)
{
string fileToAdd = string.Empty;
Editcap editcap = new Editcap();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += new DoWorkEventHandler(
(s3, e3) =>
{
if (!editcap.isLibpcapFormat(file))
{
fileToAdd = editcap.getNewFileName(file);
}
else
{
listBoxFiles.Items.Add(file);
}
});
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
(s3, e3) =>
{
listBoxFiles.Items.Add(fileToAdd);
});
backgroundWorker.RunWorkerAsync();
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
}
}
Your application is freezing because you're doing a lot of work in the UI thread. You need to move the long running tasks to a background thread and then just update the UI in the UI thread.
The first thing that you need to do, in order to do that, is seperate out your long running task from your UI manipulation. Currently you're intermingliing the two, which is what's causing your confusion as to how to map it to a BackgroundWorker.
As long as you don't need to be updating the listbox iteratively and it's okay to just add all of the items at the end all at once (that's what I would expect out of a listbox) you can simply do your file IO in one place, adding the results into a collection of some sort (List is likely appropriate here) and then, separately, you can add all of the items in the list to your ListBox (or use data binding).
Once you make that change the move to using something like a BackgroundWorker is quite easy. The IO work that populates the List goes in the DoWork, runs in the background, and then sets the Result. The RunWorkerCompleted event then takes that lists and adds the items to the ListBox.
If you have a compelling need to add the items to the listbox as you go, so you see one item, then the next, etc. over time, then just think of it as "reporting progress" and use the relevant progress reporting functionality built into BackgroundWorker. Update the progress inside of the loop, and in the progress reported event handler take the value given to you and put it in the ListBox.
Here is an implementation:
private void btnAddfiles_Click(object sender, EventArgs e)
{
System.IO.Stream stream;
OpenFileDialog thisDialog = new OpenFileDialog();
thisDialog.InitialDirectory = (lastPath.Length > 0 ? lastPath : "c:\\");
thisDialog.Filter = "(*.snoop, *.pcap, *.cap, *.net, *.pcapng, *.5vw, *.bfr, *.erf, *.tr1)" +
"|*.snoop; *.pcap; *.cap; *.net; *.pcapng; *.5vw; *.bfr; *.erf; *.tr1|" + "All files (*.*)|*.*";
thisDialog.FilterIndex = 1;
thisDialog.RestoreDirectory = false;
thisDialog.Multiselect = true;
thisDialog.Title = "Please Select Source File";
if (thisDialog.ShowDialog() == DialogResult.OK)
{
if (thisDialog.FileNames.Length > 0)
{
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork +=
(s3, e3) =>
{
//TODO consider moving everything inside of the `DoWork` handler to another method
//it's a bit long for an anonymous method
foreach (String file in thisDialog.FileNames)
{
try
{
if ((stream = thisDialog.OpenFile()) != null)
{
using (stream)
{
Editcap editcap = new Editcap();
if (!editcap.isLibpcapFormat(file))
{
string fileToAdd = editcap.getNewFileName(file);
backgroundWorker.ReportProgress(0, fileToAdd);
}
else
{
backgroundWorker.ReportProgress(0, file);
}
lastPath = Path.GetDirectoryName(thisDialog.FileNames[0]);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
};
backgroundWorker.ProgressChanged +=
(s3, arguments) =>
{
listBoxFiles.Items.Add(arguments.UserState);
};
backgroundWorker.RunWorkerAsync();
}
}
You can do it with BackgroundWorker:
Add a backgroundWorker to your form via the Toolbox.
Start it with:
backgroundWorker.RunWorkerAsync(new string[] {parm1, parm2});
Add a events to backgroundWorker (Properties window)
Use DoWork to do your calculations. Then use RunWorkerCompleted to apply the settings.
Related
Hello i created a program which reads serial data from a scoreboard, then depending on the string the program separates the data into different boxes on the form and then to different txt files.The purpose of this, is to use those txt files for livestreaming purposes in basketball games.
It's the first time i work with serial data and i am not a very experienced programmer.
My problem, as the title of this post suggests is that every now and then without any reason i am loosing some packages. This is happening randomly , for example in 10 second period i could 1 package while the next one none or 4.
private void ReadData()
{
Thread MyThread = null;
{
try
{
ThreadStart ThreadMethod = new ThreadStart(ReadFromPort);
MyThread = new Thread(ThreadMethod);
}
catch (Exception e)
{
Console.WriteLine("Failed to create thread! " + e.Message);
return;
}
try
{
MyThread.Start();
}
catch (Exception e)
{
Console.WriteLine("The thread failed to start! " + e.Message);
}
}
}
//Recieves data and write them on textbox (optionally on a txt)
private void ReadFromPort()
{
while (Receiver == true)
{
try
{
int count = ComPort.BytesToRead;
System.Windows.Forms.Application.DoEvents();
byte[] data = new byte[count];
ComPort.Read(data, 0, data.Length);
currentMessage = Combine(currentMessage, data);
ReceivedData = (BitConverter.ToString(data));
if (count > 0)
{
if (chBoxUpdate.Checked)
{
DataType = count;
tempData = ReceivedData;
this.Invoke(new EventHandler(DisplayText));
if (chboxTxt.Checked)
{
this.Invoke(new EventHandler(ExportData));
}
}
else if (chBoxPrevious.Checked)
{
DataType = count;
tempData = ReceivedData;
this.Invoke(new EventHandler(ClearData));
this.Invoke(new EventHandler(DisplayText));
if (chboxTxt.Checked)
{
this.Invoke(new EventHandler(ExportData));
}
}
}
}
catch (Exception e)
{
}
}
}
//Displays Text
private void DisplayText(object sender, EventArgs e)
{
string temp;
Console.WriteLine(tempData+ " (" + tempData.Length.ToString() + ")");
try
{
if (tempData.Length == 38)// && ReceivedData.Substring(12, 5) == "03-02")
{
if (tempData.Substring(12, 5) == "03-02")
{
DataText.AppendText(tempData.Substring(24, 8));
DataText.AppendText("\n");
Blink.Text = "Reading...";
timer1.Start();
timer1.Enabled = true;
}
}
else
if (tempData.Length == 35)
{
if (tempData.Substring(12, 5) == "45-02")
{
AttackTime.AppendText(tempData.Substring(24, 5));
Blink.Text = "Reading...";
AttackTime.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
}
else
if (tempData.Length == 29)
{
if (tempData.Substring(12, 5) == "03-36")
{
HomeScore.AppendText(tempData.Substring(21, 2));
Blink.Text = "Reading...";
HomeScore.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
else
if (tempData.Substring(12, 5) == "03-46")
{
AwayScore.AppendText(tempData.Substring(21, 2));
Blink.Text = "Reading...";
AwayScore.AppendText("\n");
timer1.Start();
timer1.Enabled = true;
}
}
}
catch (ArgumentOutOfRangeException)
{
}
}
Keep in mind that tempData and ReceivedData are the same here and as the programs appears now there's not any particular reason to set tempData=ReceivedData.This is a part of a different,older code i used in the begining which i never changed but it doesn't effect the program.
At first i am using a thread to enable the ReadFromPort then if the program finds that there are available data in line it displays them with the DisplayText and it's using the ExportData to export the data to a txt.I think the problem is somewhere there but as i am not very experienced i can't tell where.
Are there any suggestions on how to improve my code? If you more details or information i can provide them.
You are using Invoke, this blocks the caller thread until the event has been processed, and since you are updating the UI this can take some time. This probably causes some buffer to overflow and data to be discarded. So you should do as little work as possible on the reading thread.
There are a few alternatives
Put the data on a concurrentQueue, and let the UI thread have a timer that periodically triggers a method that reads from the queue and updates the UI.
Put the data on a concurrentQueue, wrapped in a blocking collection, have another background thread iterate over the blocking collection, using GetConsumingEnumerable, and post the result to the main thread once done. This would be suitable if there is significant processing to be done.
Use BeginInvoke to post the received data to the main-thread, this does not wait for the call to complete, but probably have slightly higher overhead that the previous alternatives. I would recommend not accessing any UI properties from the background thread, since by default no property of a UI class is threadsafe. Even if you might get away with it if you are only reading, I would not take the chance.
it's my first question I'm asking here, so please be gentle with me ;)
So I've actually got two WinForms in my C# application I'm writing at the moment (I'm quite new to C#).
This window has a button, which saves photos from an usb device you selected before in a list box to another folder.
After clicking on this button my main thread is of course busy with copying, so I decided to create another WinForm which contains my ProgressBar.
Foreach completed copy, I want to increment my ProgressBar accordingly.
So I count the number of copies I have to do and give it the progressbar as maximum. But my problem at the moment is, that I really don't know how to increment the ProgressBar without getting a Thread Unsafe Exception.
Here's my ProgressWindow code:
public partial class ProgressWindow : Form
{
BackgroundWorker updateProgressBarThread = new BackgroundWorker();
private Boolean _isThreadRunning = false;
public Boolean IsThreadRunning
{
get { return _isThreadRunning; }
set { _isThreadRunning = value; }
}
private int _progressbarLength;
public int ProgressbarLength
{
get { return _progressbarLength; }
set { _progressbarLength = value; }
}
private int progress = 1;
public ProgressWindow()
{
Show();
InitializeComponent();
}
private void StartUpdateThread(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Reports progress to the ProgressChangedEvent function. (Thread Safe)
}
private void FinishProgressThread(object sender, RunWorkerCompletedEventArgs e)
{
if (!_isThreadRunning)
{
MessageBox.Show("Erfolgreich kopiert");
Close();
}
}
private void ProgressChangedEvent(object sender, ProgressChangedEventArgs e)
{
this.copyProgressbar.Value = e.ProgressPercentage;
this.progressStatus.Text = e.ProgressPercentage.ToString();
}
public void CallUpdateThread()
{
updateProgressBarThread.WorkerReportsProgress = true;
updateProgressBarThread.DoWork += new DoWorkEventHandler(StartUpdateThread);
updateProgressBarThread.ProgressChanged += new ProgressChangedEventHandler(ProgressChangedEvent);
updateProgressBarThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FinishProgressThread);
updateProgressBarThread.RunWorkerAsync();
}
}
I want to increment my ProgressBar with 1 after each succesful copy.
How do I do this from my main thread?
This is the function which actually handles the copy process
private void SaveFile(System.IO.DirectoryInfo root)
{
try
{
IEnumerable<DirectoryInfo> directoriesNames = root.EnumerateDirectories();
// New instance of thread ProgressWindow.
ProgressWindow progress = new ProgressWindow();
progress.CallUpdateThread();
foreach (DirectoryInfo element in directoriesNames)
{
// Query all subdirectories and count everything with the in the configuration made settings.
if (!element.Attributes.ToString().Contains("System"))
{
// Now we insert the configuration we applied.
String fileExtension = null;
if (Properties.Settings.Default._configPhoto)
{
fileExtension = "*.jpg";
}
if (Properties.Settings.Default._configWordDocument)
{
fileExtension = "*.odt";
}
FileInfo[] jpgList = element.GetFiles(fileExtension, SearchOption.AllDirectories);
// set the size of the progress bar
progress.ProgressbarLength = jpgList.Count();
// Now we go through all our results and save them to our backup folder.
foreach (FileInfo tmp in jpgList)
{
string sourceFilePath = tmp.FullName;
string destFilePath = PATHTOBACKUP + "\\" + tmp.Name;
progress.IsThreadRunning = true;
try
{
System.IO.File.Copy(sourceFilePath, destFilePath, true);
}
catch (IOException ioe)
{
MessageBox.Show(ioe.Message);
}
}
}
}
// progress.IsThreadRunning = false;
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show(e.Message);
}
}
It's pretty obvious that I have to do this after this function
System.IO.File.Copy(sourceFilePath, destFilePath, true);
But how do I report this to my ProgressWindow?
I really hope I explained it well enough, not sure if I'm missing something important.
Thanks in advance guys
Here is a compact example of the key components:
Clicking button starts new thread worker
Progress is done by file lengths, not by number of files
BeginInvoke used to update the progress bar (avoid cross Thread exception)
ProgressBar pb = new ProgressBar() { Minimum = 0, Maximum = 100 };
Button btn = new Button();
btn.Click += delegate {
Thread t = new Thread(() => {
DirectoryInfo dir = new DirectoryInfo("C:\\temp\\");
var files = dir.GetFiles("*.txt");
long totalLength = files.Sum(f => f.Length);
long length = 0;
foreach (var f in files) {
length += f.Length;
int percent = (int) Math.Round(100.0 * length / totalLength);
pb.BeginInvoke((Action) delegate {
pb.Value = percent;
});
File.Copy(f.FullName, "...");
}
});
t.IsBackground = true;
t.Start();
};
I am trying to update the components on my form with blocking its thread.
My program uses DotNetZip to add files into an archive and I am trying to update the progress bars to illustrate the progress made.
The SaveProgress method is called when the Save() starts. Before and after each entry has been written and when the Save() is finished.
At the moment the labels are not being updated and the progressBar1 does not update?
private void buttonCompress_Click(object sender, EventArgs e)
{
if ((folderBrowserDialog1.ShowDialog() == DialogResult.OK) && (saveFileDialog1.ShowDialog() == DialogResult.OK))
{
buttonCompress.Enabled = false;
String DirectoryToZip = folderBrowserDialog1.SelectedPath;
String ZipFileToCreate = saveFileDialog1.FileName;
using (ZipFile zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
zip.SaveProgress += SaveProgress;
zip.StatusMessageTextWriter = System.Console.Out;
zip.AddDirectory(DirectoryToZip); // recurses subdirectories
zip.Save(ZipFileToCreate);
}
}
}
Compression is very CPU-intensive, of course it would freeze your UI thread, use a background thread for it instead:
private void buttonCompress_Click(object sender, EventArgs e)
{
if ((folderBrowserDialog1.ShowDialog() == DialogResult.OK) && (saveFileDialog1.ShowDialog() == DialogResult.OK))
{
buttonCompress.Enabled = false;
String DirectoryToZip = folderBrowserDialog1.SelectedPath;
String ZipFileToCreate = saveFileDialog1.FileName;
// fire off zipping job in a background thread
Task.Factory.StartNew(() => StartZipping(DirectoryToZip, ZipFileToCreate), TaskCreationOptions.LongRunning);
}
}
private object StartZipping(string DirectoryToZip, string ZipFileToCreate)
{
using (ZipFile zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
zip.SaveProgress += SaveProgress;
zip.StatusMessageTextWriter = System.Console.Out;
zip.AddDirectory(DirectoryToZip); // recurses subdirectories
zip.Save(ZipFileToCreate);
}
}
Also since the SaveProgress event handler will now be called from the background thread, you have to change it to marshall UI updates to the UI thread.
I am currently writing a simple WPF 3.5 application that utilizes the SharePoint COM to make calls to SharePoint sites and generate Group and User information. Since this process takes awhile I want to show a ProgressBar while the groups are being generated. The desired process is as follows:
User enters url and clicks button to fetch site data.
ProgressBar begins animation
Groups are generated and names are added to a ListView
Upon completion ProgressBar animation ends
The problem I am running into is that the UI is never updated. Neither the ProgressBar or the ListView makes any changes. If anyone has any ideas to help with the code below it would be greatly appreciated.
private void GetGroupsAndUsersButton_Click(object sender, RoutedEventArgs e)
{
siteUrl = "";
if (SiteURLTextBox.Text.Length > 0)
{
FetchDataProgressBar.IsIndeterminate = true;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
}
else
{
System.Windows.MessageBox.Show("Please enter a URL for the SharePoint site you wish to retrieve data");
}
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
siteUrl = SiteURLTextBox.Text;
GroupListView.ItemsSource = null;
try
{
using (SPSite site = new SPSite(siteUrl))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if (GroupNames == null)
GroupNames = new List<string>();
foreach (SPGroup oGroup in collGroups)
{
GroupListView.Items.Add(new ListViewItem() { Content = oGroup.Name });
}
foreach (ListViewItem item in GroupListView.Items)
{
item.MouseLeftButtonUp += item_MouseLeftButtonUp;
}
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
FetchDataProgressBar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
FetchDataProgressBar.IsIndeterminate = false;
}
));
}
At first you need to support ProgressChanged events.
Update your BackgroundWorker initialization to:
GroupListView.ItemSource = null;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged += OnProgressChanged;
mWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync(SiteURLTextBox.Text);
After that you have to add a OnProgressChanged handler:
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
FetchDataProgressBar.Value = e.ProgressPercentage;
ListViewItem toAdd = (ListViewItem)e.UserState;
toAdd.MouseLeftButtonUp += item_MouseLeftButtonUp;
GroupListView.Items.Add(toAdd);
}
Therefore you have to change your DoWork:
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
try
{
using (SPSite site = new SPSite((String)e.Argument))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if(GroupNames == null)
GroupNames = new List<string>();
int added = 0;
foreach(SPGroup oGroup in collGroups)
{
added++;
ListViewItem tmp = new ListViewItem() {
Content = oGroup.Name
};
worker.ReportProgress((added * 100)/collGroups.Count,tmp);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
That's because you're not allowed to change GUI on DoWork.
After that, each ListViewItem is added separately to your ListView. I would also recommend, that your URL is passed as an argument to RunWorkerAsync.
Edit: Add percentage to OnProgressChanged.
In your DoWork method, you are manipulating WPF controls in code on a background thread, which you are not supposed to do. Actually, you should receive errors like "Cannot access control from other thread". Probably those exceptions are caught by your catch-all error handler, and maybe even the MessageBox doesn't work from the background thread.
As a quick fix, you would have to make siteURL and collGroups class fields, move everything before the using block to your GetGroupsAndUsersButton_Click method, and everything starting with the first foreach loop to the RunworkerCompleted event, so that all code which accesses controls runs on the UI thread.
Another thing you should change is that you should not create ListViewItems in code, but use a DataTemplate instead... this is not connected to your problem, though.
You'll need:
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
Then in your DoWork you'll need to call:
var worker = (BackgroundWorker)sender;
worker.ReportProgress(progressAmount);
Good worked example here: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
I have a simple Winforms app that allows users to select multiple videos (files) simultaneously and runs background workers threads to loop through each of the videos in the BW. Have pasted code below, I get a NullReferenceException as "Unable to create capture from ..." at this line
Capture _capture = new Capture(videoFileName)
in processVideo method.
N.B: The same code work fine if I select a single video. So some issue with the multiple instances of Capture class.
I would expect the ProcessVideo method to have new instance of Capture and open it separately. Any ideas on what I might be doing wrong?
private void openVideoToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Video | *.AVI;*.MPEG;*.WMV;*.MP4;*.MOV;*.MPG;*.MPEG;*.MTS;*.FLV";
ofd.Multiselect = true;
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] videos = ofd.FileNames;
if (videos != null)
{
BackgroundWorker[] bw = new BackgroundWorker[videos.GetLength(0)];
int n = 0;
foreach (string video in videos)
{
bw[n] = new BackgroundWorker();
bw[n].DoWork += new DoWorkEventHandler(bw_DoWork);
bw[n++].RunWorkerAsync(video);
}
}
}
}
catch (NullReferenceException excpt)
{
MessageBox.Show(excpt.Message);
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
string filename = (string)e.Argument;
ProcessVideo(filename);
}
private void ProcessVideo(string videoFileName)
{
Capture _capture = new Capture(videoFileName);
UInt64 TOTAL_FRAMES = Convert.ToUInt64(_capture.GetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_COUNT));
for (UInt64 n = 0; n < TOTAL_FRAMES; n++)
{
using (Image<Bgr, Byte> img1 = _capture.QueryFrame())
{
//do something with the frame
}
}
}
I suggest you to update Sourcesafe service pack
it may help you
[I think you code is perfect there is
nothing wrong in it.
You got an error while creating object it clearly saw that
there may be chance that file format is not supported
or may be internal error problem.]
Let me know that after doing updation it works or not.
Regards Red