12 Replies - 1405 Views - Last Post: 22 July 2013 - 10:14 AM Rate Topic: -----

#1 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Synchronous download with progress reporting

Posted 18 July 2013 - 04:04 PM

Hello,

I am new to C#. I have implemented a class, that works more or less like this:
  • Download a web page
  • Process contents
  • Download some data
  • Process the data
  • And so on... (repeat x 10)

This works great when flow of control is sequential - I just need to create a new object, and in its constuctor all these steps are being performed sequentially, so when the object is created, it contains all necessary data stored in it. Each step depends on the previous one and there is no need to introduce any concept of parallelism:)

But in order to make my app look professional:) I would like to have a progress bar, which reports progress of each of these steps to the user. And this is when I learned that I can't really download anything with progress reporting without use of asynchronous operations (i.e. Async methods of WebClient).

I tried quite a lot of different things to get a method, that simply returns the data and is able to report progress back to the GUI:
1. WebClient + WaitHandle - tried to suspend main thread after running WebClient Async method. In this case callback method is never called and the app just hangs forever.
2. WebClient + custom thread semaphore - the same
3. WebClient + BeginInvoke and waiting on IAsyncResult - this actually produces some decent result, but I don't know how to return data from it
4. I even tried to use a C-like synchronous socket read loop. This one actually does something, but I can only see the progress bar filling itself after download is completed. DoEvents(), Update()'s, even decreasing progress bar gauge didn't help.

My question is: does anyone met with that kind of problem and knows how to solve it? I don't want to end up redesigning my class to be a bunch of self-calling event handlers, when there is no need to do so:)

Thanks in advance!

Is This A Good Question/Topic? 0
  • +

Replies To: Synchronous download with progress reporting

#2 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5678
  • View blog
  • Posts: 12,217
  • Joined: 02-June 10

Re: Synchronous download with progress reporting

Posted 18 July 2013 - 06:04 PM

From my FAQ list:

Q:...do multi-threading? Having a problem with cross-threading...
A:

This post has been edited by tlhIn`toq: 18 July 2013 - 06:05 PM

Was This Post Helpful? 0
  • +
  • -

#3 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3667
  • View blog
  • Posts: 11,499
  • Joined: 05-May 12

Re: Synchronous download with progress reporting

Posted 20 July 2013 - 10:30 AM

What's wrong with using the DownloadProgressChanged event? http://msdn.microsof...esschanged.aspx
Was This Post Helpful? 0
  • +
  • -

#4 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 20 July 2013 - 11:22 AM

Hi,

The problem is like this: my class is designed for just one "flow", i.e. everything is happening sequentially. If I decide to use this event, I will have to split the class to numerous event handlers, and I don't want to do that.

So I need a method which will basically call an asynchronous operation (like DownloadStringAsync), but will wait untill this operation completes and then return the result (in this case the string). Then I could just subscribe to DownloadProgressChanged and DownloadCompleted events.

I tried to suspend the thread and resume it in DownloadCompleted event, but it simply doesn't work - the thread is never resumed.
I also tried to read the tutorials, but like I said before, I am just beginning my adventure with C# so it's hard to understand some of them, and I can't find anything that matches my pattern.

Thanks for help, anyway:)
Was This Post Helpful? 0
  • +
  • -

#5 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3667
  • View blog
  • Posts: 11,499
  • Joined: 05-May 12

Re: Synchronous download with progress reporting

Posted 20 July 2013 - 11:30 AM

Why do you need to have multiple classes and multiple event handlers? If the current code works sequentially, and all you need to do is update a progress bar, all you need to do is keep using the same event handler to advance the progress bar. All the event handler should do is just update the progress bar. Take away registering for events, and your code works like before when you didn't have a progress bar. Add in registering for events, then you get the "bling" of a progress bar, but your code is still the same old sequential program.
Was This Post Helpful? 0
  • +
  • -

#6 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 04:26 AM

Maybe I am just stupid, but I don't see the solution for my problem in your post...

First, I am not using any event handler now, I am trying to implement it.

Second, I have a code like this in the constructor:

Ctor(Uri url)
{
  string html = Network.DownloadString(url);
  string newUrl = this.ParseHtml(html);
  string newHtml = Network.DownloadString(newUrl);
  
  string newUrl2 = this.ParseHtml2(newHtml);
  byte [] data = Network.DownloadData(newUrl2);

  // etc. repeat

}



Network.DownloadString() is a helper class which just calls WebClient.DownloadString() with appropriate headers.

So, main problem: if I would like to use progress reporting, I would have to change the call to WebClient.DownloadStringAsync(). Leaving alone event subscription, I can't simply return data from this method because DownloadStringAsync() is not returning any data and the call returns immediately. So in order to get this working I would have to move call to this.ParseHtml() to an event handler executed when first download is complete; the same goes for all other (download & parse) tuples.

So you can see it yourself: I can't really use the same sequential code structure in the constructor, but instead I have to change it to a bunch of chained events. And even if I do this, I won't be able to rely on one useful "feature" of my code, that is the object being completely initialized after class constructor returns.

I don't really know how to handle this situation...
Was This Post Helpful? 0
  • +
  • -

#7 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3667
  • View blog
  • Posts: 11,499
  • Joined: 05-May 12

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 06:42 AM

Change from:
class Network
{
    :
    byte [] DownloadData(string uri)
    {
        :
        var stringData = webClient.DownloadString(uri);
        return ConvertToByteData(stringData);
    }
}


to:
class Network
{
    class DownloadPoco
    {
        public string StringData;
        public EventWaitHandle Waiter;
    }

    :
    void WebClient_DownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        var download = e.UserState as DownloadPoco();

        : // other code here to check for cancel or failure

        download.StringData = e.Result;
        download.Waiter.Set();
    }

    :
    string DownloadData(string uri)
    {
        var download = new DownloadPoco(); 
        download.Waiter = new ManualResetEvent(false)
        :
        webClient.DownloadStringCompleted += WebClient_DownloadCompleted;
        webClient.DownloadStringAsync(uri, download);
        :
        download.Waiter.WaitOne();
        return ConvertStringDataToByteData(download.StringData);
    }
}


Was This Post Helpful? 0
  • +
  • -

#8 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 07:18 AM

Hi,

Thanks for help, but I have already tried this approach (I mentioned it in my first post). The problem with this code is that the main thread is never resumed, i.e. it hangs forever while waiting for semaphore to be set.
Was This Post Helpful? 0
  • +
  • -

#9 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3667
  • View blog
  • Posts: 11,499
  • Joined: 05-May 12

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 10:59 AM

unless you hooked up the event handler incorrectly, I doubt that there is a hang, setting a breakpoint on the handler will show whether the handler is being called or not.

What I suspect to be a more likely scenario us that Windows is claiming your program is not responding because the message pump is not running while waiting for the event to be signaled.

Any which way, post your code.

Sent from my T-Mobile G2 using Tapatalk 2
Was This Post Helpful? 0
  • +
  • -

#10 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 01:34 PM

Okay:)

The Network class:
class Network
{
    class Result
    {
        public string data;
        public EventWaitHandle waiter;
    }

    public string DownloadAsString(Uri url)
    {
        Result r = new Result();
        r.waiter = new ManualResetEvent(false);
        WebClient webClient = new WebClient();
        webClient.DownloadStringCompleted += DownloadCompletedEvent;
        webClient.DownloadStringAsync(url, r);
        r.waiter.WaitOne();
        return r.data;
    }

    void DownloadCompletedEvent(object sender, DownloadStringCompletedEventArgs e)
    {
        Result r = e.UserState as Result;
        Console.WriteLine("Download completed event raised!");//1
        r.data = e.Result;
        r.waiter.Set();
    }
}


Form1 class:
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Network network = new Network();
        string html = network.DownloadAsString(new Uri("http://www.google.com"));
        Console.WriteLine(html);//2
    }
}


This is everything that I changed in the project. When executing this code, I can see neither //1 nor //2 printout in the console, and GUI remains not responsive, Although I'm getting Console output about some thread finished execution:)

Please tell me if there is something wrong with my code. Thanks!:)
Was This Post Helpful? 0
  • +
  • -

#11 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 21 July 2013 - 01:53 PM

I couldn't find the Edit button...

But to answer your other questions: I put the breakpoint in DownloadCompletedEvent method, and it's never hit. When I remove the thread waiting, the method is executed.
Was This Post Helpful? 0
  • +
  • -

#12 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3667
  • View blog
  • Posts: 11,499
  • Joined: 05-May 12

Re: Synchronous download with progress reporting

Posted 22 July 2013 - 05:39 AM

As far as I know, WebClient doesn't use the message pump, but for grins try changing:
webClient.DownloadStringAsync(url, r);
r.waiter.WaitOne();


to
webClient.DownloadStringAsync(url, r);
MessageBox.Show("Test");     // popup a message box to get a message pump running
r.waiter.WaitOne();


Was This Post Helpful? 0
  • +
  • -

#13 Beju  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 18-July 13

Re: Synchronous download with progress reporting

Posted 22 July 2013 - 10:14 AM

Hi,

that did the trick:) What do you think is the reason of such behavior, and what should I replace the MessageBox with? :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1