I'm trying to build an app that pulls back all the directories and files and builds a Treeview. I have some code below that does this, however it seams to cause my UI to freeze as it is building.
I'm unsure if this a threading issue? I think that the code that builds the tree view is on a separate thread to the UI although this is one bit i'm unsure of.
I've also read that i may need to look into UI Virtualisation / data Virtualisation. If anyone could point me into the correct direction that would be very helpful.
private void btnSearch_Click(object sender, System.EventArgs e)
{
btnSearch.Text = "Searching...";
this.Cursor = Cursors.WaitCursor;
Application.DoEvents();
Thread treeThread = new Thread(() => ListDirectory(lstTreeView, "C:\\"));
treeThread.Start();
btnSearch.Text = "Search";
this.Cursor = Cursors.Default;
}
private void ListDirectory(TreeView treeView, string path)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
this.Invoke((MethodInvoker)(() => treeView.Nodes.Clear()));
var rootDirectoryInfo = new DirectoryInfo(path);
this.Invoke((MethodInvoker)(() => treeView.Nodes.Add(CreatedirNode(rootDirectoryInfo))));
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
this.Invoke((MethodInvoker)(() => lblCount.Items.Add("Time taken in Sec " + (elapsedMs)/1000)));
}
I think this maybe the reason as I think the UI is freezing as directoryNode.Nodes.Add(new TreeNode(file.Name)); looks to be writing on the UI thread?
private static TreeNode CreatedirNode(DirectoryInfo DirI) {
var directoryNode = new TreeNode(DirI.Name);
try
{
foreach (var directory in DirI.GetDirectories())
directoryNode.Nodes.Add(CreatedirNode(directory));
foreach (var file in DirI.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name));
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
return directoryNode;
}
Related
I detect faces in image files inside a folder using Emgu CV. I'm using a foreach loop for this. However, the form does not respond until the whole process is finished.
I use threads to avoid this. However, the thread is not working as I expected. The loop ends before the rendering of the images is finished.
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
Thread th = new Thread(() => ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath));
th.Start();
}
Even if I do it this way, it works like it was before using threads.
th.Start();
th.Join();
The simplest thing is to change from Thread to Task (and correctly dispose of your image) like this:
foreach (var item in files)
{
using (Image img = Image.FromFile(item))
{
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
await Task.Run(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath));
}
}
That MUST be done in a async Task method (or async void event handler) to allow the use of the await keyword.
This approach will run each imageprocessmulti one after the other without blocking the UI.
If you want to run all of the imageprocessmulti in parallel, then you're best off making a list of tasks and awaiting them all at once. Like this:
List<Task> tasks =
(
from item in files
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
select Task.Run(() =>
{
using (Image img = Image.FromFile(item))
{
ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath);
}
})
).ToList();
await Task.WhenAll(tasks);
My preferred approach is to use Microsoft's Reactive Framework - NuGet System.Reactive - and then do this:
IObservable<Unit> query =
from item in files.ToObservable(Scheduler.Default)
let savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file))
from unit in Observable.Using(
() => Image.FromFile(item),
img => Observable.Start(() => ModernUI.FaceDetect.imageprocessmulti(img, savefile, savepath)))
select unit;
await query.ToArray();
All of these approaches should work for you.
Here's one way to approach it using async/await, for example, with a button click event:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
}
});
// ...more code here to run after all images have been processed...
MessageBox.Show("Done!");
}
Here's an alternate version to show progress, not sure what you tried:
private async void button1_Click_1(object sender, EventArgs e)
{
progressBar1.Visible = true;
progressBar1.Value = 0;
int counter = 0;
int total = files.Length;
await Task.Run(() => {
foreach (var item in files)
{
Img = Image.FromFile(item);
string savefile = Path.Combine(path, "eo", dirname, Path.GetFileName(file));
ModernUI.FaceDetect.imageprocessmulti(Img, savefile, savepath);
counter++;
int p = (int)((double)counter / total * 100);
progressBar1.Invoke((MethodInvoker) delegate {
progressBar1.Value = p;
});
}
});
// ...more code here to run after all images have been processed...
progressBar1.Visible = false;
MessageBox.Show("Done!");
}
Basically I have a WPF application Where the user Write A process Name ,
Then a new Thread start where it keeps scanning if the process is Opened yet or not , The Thread will be Alive untill the process is Found .. So i can Get the handle and write The Memory !
private void scanBtn_Click (object sender, RoutedEventArgs e)
{
Thread s = new Thread(( ) => {
this.Dispatcher.Invoke((Action)(( ) =>
{
scanner(pName.Text);
}));
});
try
{
if (pName.Text != string.Empty)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
s.Start();
pName.IsEnabled = false;
if (!s.IsAlive)
{
pName.IsEnabled = true;
InfoTxt.Text = "[ FOUND ]";
Process p = Process.GetProcessesByName(pName.Text)[0];
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private static void scanner ( string procName)
{
while (true)
{
Process s = SeekProcName(procName);
if (s != null) break;
}
}
private static Process SeekProcName(string pName)
{
Process[] procs = Process.GetProcesses().Where(p => p.MainWindowHandle != (IntPtr)0).ToArray();
Process f = null;
foreach (var item in procs)
{
if (item.ProcessName.ToLower() == pName.ToLower())
{
f = item;
break;
}
}
return f;
}
The s thread is trying to run a delegate on the UI thread, which means it's no different than calling SeekProcName(procName) in the event handler directly.
It would be better to use Task.Run and async/await to run the check in a background thread. When await returns, execution resumes in the UI thread which means the UI can be updated without requiring Invoke or BeginInvoke
private async void scanBtn_Click (object sender, RoutedEventArgs e)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
//Read the textbox contets *before* starting the task
var name=pName.Text;
var p=await Task.Run(()=>SeekProcName(name));
if (p!=null)
{
InfoTxt.Text = "[ FOUND ]";
}
}
This can be called in a loop, with a small delay between iterations, without blocking the UI thread :
private async void scanBtn_Click (object sender, RoutedEventArgs e)
{
while(someCondition)
{
InfoTxt.Text = "[ WAITING FOR PROCESS TO OPEN ]";
var name=pName.Text;
var p=await Task.Run(()=>SeekProcName(name));
if (p!=null)
{
InfoTxt.Text = "[ FOUND ]";
}
await Task.Delay(1000);
}
}
I'm trying to get my head around threads, in my current application threads would be the best way to deal with it, what i have:
public void threadsTest() {
Invoke(new MethodInvoker(() => {
// loop over the listview getting the first value
foreach (ListViewItem item in listViewBacklinks.Items)
{
// try...
try
{
var mainURL = item.Text;
using (WebClient wc = new WebClient())
{
try
{
var pageHtml = wc.DownloadString(mainURL);
if (pageHtml.Contains(Text))
{
var subitems = item.SubItems.Add("anchor text");
item.BackColor = Color.Green;
}
else
{
item.BackColor = Color.Red;
}
} catch (Exception)
{
item.BackColor = Color.Red;
}
//Helpers.returnMessage("Work done!");
}
} catch (Exception ex) {
Helpers.returnMessage(ex.ToString());
}
}}));
}
private void btnAnalyzeAnchorText_Click(object sender, EventArgs e)
{
// attempting threads
var t = new Thread(threadsTest);
t.Start();
}
I thought like the backgroundWorker it would not freeze up the GUI but it did so i put in the invoke to access GUI elements, i don't think it looks right, the way it is now the GUI is still unresponsive until the work is done, i have viewed some tutorials but it's not totally sinking in, any help/tips on cracking threads would be great.
This is what you need to do:
public void threadsTest(string[] urls)
{
var results = new Dictionary<string, string>();
foreach (string mainURL in urls)
{
using (WebClient wc = new WebClient())
{
var pageHtml = wc.DownloadString(mainURL);
results[mainUrl] = pageHtml;
}
}
Invoke(new MethodInvoker(() => ProcessResults(results)));
}
There are no UI elements in this code, just the heavy lifting of the WebClient.
Then in the new ProcessResults you're back on the UI thread so then you can set your results.
The call to the thread needs to pass through an array of urls (strings) but you get that before creating the thread so you're still in the UI thread and thus it is safe to do.
I'm currently working on a program that converts a list of files from .ps (PostScript) to .png.
Originally, this was done in a batch file, one file at a time. I am working on code that uses the Ghostscript.NET dll to process these files asynchronously. By splitting these up into tasks, I have cut down the processing time from 30 minutes to about 6 minutes.
I want to be able to show the user some sort of progress on this, so that it doesn't just look like my program is frozen.
I know just enough about threading to frustrate myself, so any suggestions on the best way to do this is greatly appreciated. The code below has a BackgroundWorker implemented to try to show the progress. I have used BGWorker before to show progress, but not on multiple tasks like this. In fact, this is my first time multi-threading without just using BGWorker.
I feel that BGWorker is probably not what I need to be using, but I wanted to try to take a stab at it myself before I asked.
Here is the code that I have so far:
public partial class ProcessStatusForm : Form
{
public string[] testList;
public string wordPath;
public string StatusText;
public GhostscriptVersionInfo _gs_version_info;
public DirectoryInfo dInfo;
public List<Task> tasks;
public float NumberOfTasks;
public bool PS2PNGRunning;
public int ProgressPct;
public float dPercent;
public decimal decPercent;
public ProcessStatusForm(string wordDoc, List<string> runList)
{
InitializeComponent();
this.wordPath = wordDoc;
this.testList = runList.ToArray();
this.StatusText = string.Empty;
this._gs_version_info = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL |
GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
this.dInfo = new DirectoryInfo(SettingsClass.PSFolder);
this.PS2PNGRunning = false;
this.ProgressPct = 0;
this.NumberOfTasks = runList.Count;
}
private void ProcessStatusForm_Shown(object sender, EventArgs e)
{
//Spawn tasks for each of the .ps files in the PS_FILES folder
tasks = new List<Task>(dInfo.GetFiles("*.ps").Length);
//Start the BackgroundWorker
this.PS2PNGRunning = true;
BackgroundWorker.RunWorkerAsync();
foreach (var file in dInfo.GetFiles("*.ps"))
{
//Get fileName to pass fo the ConvertPS2PNG
string inputFile = file.Name;
//Create the Task
var task = Task.Factory.StartNew(() => ConvertPS2PNG(inputFile));
tasks.Add(task);
}
//Wait until all tasks have completed
Task.WaitAll(tasks.ToArray());
PS2PNGRunning = false;
}
private void ConvertPS2PNG(string input)
{
string output = input.Replace(".ps", "_01.png");
input = SettingsClass.PSFolder + input;
output = SettingsClass.PNGFolder + output;
GhostscriptProcessor processor = new GhostscriptProcessor(_gs_version_info, true);
processor.Process(CreateGSArgs(input, output), new ConsoleStdIO(true, true, true));
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
ProgressPct = 0;
while (PS2PNGRunning)
{
Thread.Sleep(1000);
float TasksCompleted = 0;
foreach (var tsk in tasks)
{
if (tsk.Status == TaskStatus.RanToCompletion)
{
TasksCompleted++;
}
}
StatusText = TasksCompleted + " of " + NumberOfTasks + " converted...";
dPercent = TasksCompleted / NumberOfTasks;
dPercent *= 100;
decPercent = (decimal)dPercent;
decPercent = Math.Round(decPercent);
ProgressPct = (int)decPercent;
BackgroundWorker.ReportProgress(ProgressPct);
}
BackgroundWorker.ReportProgress(100);
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ProgressLabel.Text = this.StatusText;
this.progressBar.Style = ProgressBarStyle.Continuous;
this.progressBar.Value = e.ProgressPercentage;
}
public string[] CreateGSArgs(string inPath, string outPath)
{
List<string> gsArgs = new List<string>();
gsArgs.Add("-dBATCH");
gsArgs.Add("-dNOPAUSE");
gsArgs.Add("-sDEVICE=png16m");
gsArgs.Add("-dQUIET");
gsArgs.Add("-sPAPERSIZE=letter");
gsArgs.Add("-r800");
gsArgs.Add("-sOutputFile=" + outPath);
gsArgs.Add(inPath);
return gsArgs.ToArray();
}
}
When I put breaks in the code of BackgroundWorker_DoWork, everything seems to be coming out right, but when it gets to the BackgroundWorker.ReportProgress(), it never makes it to the BackgroundWorker_ProgressChanged() method.
At the very least, I could live with just having a progressBar.Style as marquee while this is running so that the user can see that the program is working, but reporting the actual progress would be ideal.
As I said before, I haven't done a ton of work with threading, and all of my knowledge on the subject pretty much comes from Google and StackOverflow. If there is a completely different way to do this, I am open to all criticism.
Was the name BackgroundWorker given to the object when you dragged it from the designer screen? If not change your code to use the appropriate name it was given (default should have been backgroundWorker1).
Or...
Try casting the sender object to a BackgroundWorker object in your DoWork method and call ReportProgress() from there.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
bw.ReportProgress(25);
}
}
I have a thread that calls a static method to update file properties using WindowsAPICodePack ShellPropertyWriter and BackgroundWorker. The thread calls the method below for each file in a folder of 1000+ files and hangs on the ShellPropertyWriter.close() after the 700th update or so.
Nothing to do with the file itself, tried using different files that successfully updated before.
public static bool ShellPropertyUpdate(VideoEntry mediaEntry)
{
try
{
ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath);
ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
pw.Close();
}
catch (Exception ex)
{
return false;
}
return true;
}
private void mnuWriteMetadataToFiles_Click(object sender, EventArgs ev)
{
this.WorkerThread = new BackgroundWorker();
this.WorkerThread.DoWork += new DoWorkEventHandler(WorkerThread_WriteMetadataToFiles);
this.WorkerThread.ProgressChanged += new ProgressChangedEventHandler(WorkerThread_ProgressChanged);
this.WorkerThread.RunWorkerCompleted += (s, e) => WorkerThread_Completed("Writing metadata to files", s, e);
this.WorkerThread.WorkerReportsProgress = true;
this.WorkerThread.WorkerSupportsCancellation = true;
this.WorkerThread.RunWorkerAsync(WMPlayer);
}
private void WorkerThread_WriteMetadataToFiles(object sender, DoWorkEventArgs e)
{
int counter = 0;
BackgroundWorker worker = (BackgroundWorker)sender;
MediaPlayer wmp = (MediaPlayer)e.Argument;
// ... Loop with the foreach video in the library and write it to file.
foreach (VideoEntry entry in wmp.Videos)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
worker.ReportProgress(counter, "Updating '" + entry.Filename + "'" + Environment.NewLine + "Processing file");
if (VideoToFile.ShellPropertyUpdate(entry))
{
result &= true;
}
counter++;
}
}
e.Result = result;
}
Never heard of this assembly before, but it smells like handle exhaustion to me. Try this instead:
using (ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath))
{
ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
pw.Close();
}
Here every file handle is closed immediately, instead of at garbage collector's discretion. ShellFile must implement IDisposable for this to work, otherwise this code will not compile. I'm fairly certain that ShellFile implements it.
Apparently it does have something to do with the files themselves. I took out few problem files and the Thread continued processing until the next problem file. I have no clue what's wrong with the file, however I'm willing to pass on updating problem files. Is there a way to stop/kill the thread? I can't use DoWorkEventArgs.cancel() since the thread is hanging and not coming back.