Click here to Skip to main content
15,882,063 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
Here I'm stuck in a problem with threading.

I have created one function which clicks button on web page which launches the download dialog box (inside browser control). I ran into a most frustrating issue with IE and the Download dialog. It seems that the actual "Download File" dialog is launched in a new thread by the web browser. While this would normally be fine, it turns out that the new thread launched for the the download is launched synchronously, so the current thread will not continue until a button on the download dialog is clicked, or the dialog closes (finished or cancelled).

When I use this code the main thread halts till the newly created threads stop. the new thread tries to find dialog window which is where it fails and it stops. After this main thread resumes and the dialog box appears.

I'm not able to synchronize this threads, any help greatly appreciated

C#
public void DownloadFile()
        {
            HtmlElementCollection links = this.webBrowser1.Document.GetElementsByTagName("input");
            int btn = links.Count - 1;
            //links[btn].InvokeMember("Click"); 
            object btn1 = links[btn].DomElement;
            btn1.GetType().InvokeMember("click",System.Reflection.BindingFlags.InvokeMethod, null, btn1, null);
            Thread workThread = new Thread(delegate() { FindDownloadDialogWindow("#32770", "File Download", 25, @"D:\abc.zip"); });
            //wait until the dialog is found and file saved.
            workThread.Start();
            while (workThread.ThreadState != System.Threading.ThreadState.Stopped)
            {
                Thread.Sleep(1000);
                Application.DoEvents();
            } 
        }
Posted
Updated 16-Feb-11 3:53am
v4
Comments
shrikant003 16-Feb-11 9:31am    
code is working fine when i call this function from buton click

private void button6_Click(object sender, EventArgs e)
{
DownloadFile();
}

Very, very bad code!

You start thread; and thread are designed to avoid spin-wait, and then you use spin-wait to wait for the thread! Wow, what an abuse!

1) Don't spin-wait for anything, ever.

2) As a rule of thumb, never use DoEvents, use a thread instead.

3) Use Thread.Join to for a blocking wait for the end of the thread.

4) Don not created threads which you expect to finish. Use System.ComponentModel.BackgroundWorker.

5) Do not update UI through non-UI thread; instead, use System.Windows.Forms.Control.Invove; for WPF and Forms, use System.Threading.Dispatcher.Invoke.

Your code is beyond repair, so I cannot tell you right code. To get this, you should explain your ultimate purpose more accurately. Most likely, you will be able to solve your problem yourself using BackgroundWorker.

Important! Do not post Answer, use "Improve Question" instead. In this way, you could put formatted code. For other purposes, use "Add Comment".

—SA
 
Share this answer
 
Comments
Espen Harlinn 16-Feb-11 14:44pm    
Good answer, my 5
Sergey Alexandrovich Kryukov 16-Feb-11 15:30pm    
Thank you, hope it can help.
--SA
shrikant003 17-Feb-11 5:25am    
Thanks alot SAKryukov I got it
shrikant003 17-Feb-11 10:20am    
I have commented the while loop its working fine now, but what if i need to execute same function "DownloadFile()" multiple times
Sergey Alexandrovich Kryukov 17-Feb-11 12:28pm    
Then the solution should be very different. I'll give the answer later if you want.
--SA
yes dear, once you want to do things on separate threads, the better option is to use background worker. Because its easy to use and does most of the job itself.

There are three events of it, as described below

C#
//This Event _DoWork Occurs on separate thread
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                 //implemen your download logic here

                 backgroundWorker1.ReportProgress(1);   //this is to report progress of your work
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message,"Download File");
            }
        }

// here you can access controls of main form or thread
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //you might want to show the download progres
            progressBar1.Increment(e.ProgressPercentage);
        }

// this event occours once your job is finished
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (checkBox_close.Checked)
            {                
                this.Close();
                this.Dispose();
            }
            label_size3.Text += " COMPLETED";
        }

//the Do work event is called from main thread or form like this

            backgroundWorker1.RunWorkerAsync();
 
Share this answer
 
As I promised a while ago, here is my answer to follow-up question.

If the method DownloadFile should be repeated many times, approach should be very different.

You should create a separate thread and start if from the very beginning (more exactly, when the application main form is shown, use the event Shown). Use regular System.Threading.Thread. The thread should be kept sleeping and wake up only when their input data is fed to them. It means you need to use some synchronization primitives (I would suggest System.Threading.EventWaitHandle, System.Threading.AutoResetEvent or System.Threading.ManualResetEvent). The thread is waiting at the call to EventWaitHandle.WaitOne and awakened by calling EventWaitHandle.Set from another thread (for example, UI thread) when new task is ready, complete full cycle (possibly with exception due to some failure) and comes back, repeating download cycle in infinite loop. Be sure to use locking for exchange of task data between thread.

For this particular task more specialized tool should be used: a blocking message queue. Your application makes a queue of download task (some type encapsulating URL, output file name and other parameters) which is fed to the downloading thread; the queue should be blocking, to avoid spin-wait.

You can find complete well tested generic implementation and usage sample in my Tips/Trick article: Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^]. In the queue-based implementation, locking should not be used: the queue already provides propert synchronization.

The thread should notify the UI not directly (to show download status, etc.), but using Control.Invoke. This mechanism makes sure that UI properties/methods are not called by the non-UI thread but put to the queue, so actually calls are done in UI thread. By the way, my article referenced above provides pretty good explanation of how this invocation mechanism works.

Good luck,
—SA
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900