0

First post here, sorry for starting with asking questions.

In my Windows Phone 7 app I have a working livetile that is beeing triggered by a background agent. But how can I modify the code so the httpwebrequest timeouts after 10 seconds?

Thanks in advance.

protected override void OnInvoke(ScheduledTask task)
    {
        //TODO: Add code to perform your task in background

        var request = (HttpWebRequest)WebRequest.Create(
        new Uri("site.com"));

        request.BeginGetResponse(r =>
        {
            var httpRequest = (HttpWebRequest)r.AsyncState;
            var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
            using (var reader = new StreamReader(httpResponse.GetResponseStream()))
            {
                var response = reader.ReadToEnd();

                Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    string strResult = response;


                    /// If application uses both PeriodicTask and ResourceIntensiveTask
                    if (task is PeriodicTask)
                    {
                        // Execute periodic task actions here.
                        ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
                        if (TileToFind != null)
                        {

                            StandardTileData NewTileData = new StandardTileData
                            {
                                BackgroundImage = new Uri("Pin-to-start.png", UriKind.Relative),
                                Title = strResult,
                                Count = null
                            };
                            TileToFind.Update(NewTileData);
                        }
                    }
                    else
                    {
                        // Execute resource-intensive task actions here.
                    }

                    NotifyComplete();
                }));
            }
        }, request);
    }
John
  • 681
  • 1
  • 8
  • 20

2 Answers2

3

Here is copy/paste from code that i use in one of my apps. It will abort the connection after 60 seconds.

    private static void DoWebRequest(string uri)
    {
        string id = "my_request";

        Timer t = null;
        int timeout = 60; // in seconds

        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            request.Accept = "*/*";
            request.AllowAutoRedirect = true;

            // disable caching.
            request.Headers["Cache-Control"] = "no-cache";
            request.Headers["Pragma"] = "no-cache";

            t = new Timer(
                state =>
                {
                    if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        logger.Write("Timeout reached for connection [{0}], aborting download.", id);

                        request.Abort();
                        t.Dispose();
                    }
                },
                id,
                timeout * 1000,
                0);

            request.BeginGetResponse(
                r =>
                {
                    try
                    {
                        if (t != null)
                        {
                            t.Dispose();
                        }

                        // your code for processing the results

                    }
                    catch
                    {
                        // error handling.
                    }
                },
                request);
        }
        catch
        {
        }
    }
invalidusername
  • 912
  • 9
  • 26
  • Thank you! Should I add all the parsing and also the tile stuff in the "request.BeginGetResponse(..."? – John Nov 02 '11 at 19:00
  • yes, you can just leave it there, but make sure you dispose of t (the timer) in the beginning of BeginGetResponse. – invalidusername Nov 02 '11 at 19:02
  • Okay, so I take your code and replace my code until the "request.BeginGetResponse"? I'm a beginner so sorry for the basic questions :) I tried doing this but I get the following errors: The name 'timeout' does not exist in the current context The name 't' does not exist in the current context – John Nov 02 '11 at 19:05
  • Ok i will post a more extensive example in a sec. hold on. – invalidusername Nov 02 '11 at 19:09
  • Thank you so much! I used your code as a sort of a wrapper, and then added all my old code after t.Dispose(). I'm testing now and I think it's working. I'll se after I have had my phone in flght mode overnight if the updating of the live tile continues. Where should I put the NotifyComplete() call? – John Nov 02 '11 at 19:25
  • I think NotifyComplete() can stay where it was, but i don't really know how your code looks like right now. Just look at this overview so you make sure you implement the rest successfully: http://msdn.microsoft.com/en-us/library/hh202942(v=vs.92).aspx especially: "When the agent has completed its task, it should call NotifyComplete() or Abort() to let the operating system know that it has completed. NotifyComplete should be used if the task was successful. If the agent is unable to perform its task – such as a needed server being unavailable - the agent should call Abort, ... " – invalidusername Nov 02 '11 at 19:36
2

But how can I modify the code so the httpwebrequest timeouts after 10 seconds?

You mean so it'll call NotifyComplete() regardless of timeouts?-) The catch is that after 15 seconds the task terminates, and gets disabled until it's re-launched by the user (inside your app).

I would recommend using TPL for Silverlight and utilizing the ability to use Tasks for setting a Timeout.

Something like:

protected override void OnInvoke(ScheduledTask task)
{
    var fetchTask = FetchData(TimeSpan.FromSeconds(10));
    fetchTask.ContinueWith(x =>
    {
        Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            string strResult = x.Result; // mind you, x.Result will be "null" when a timeout occours.

            ...

            NotifyComplete();
        }));
    });
}

private Task<string> FetchData(TimeSpan timeout)
{ 
    var tcs = new TaskCompletionSource<string>();
    var request = (HttpWebRequest)WebRequest.Create(new Uri("site.com"));

    Timer timer = null;
    timer = new Timer(sender =>
    {
        tcs.TrySetResult(null);
        timer.Dispose();
    }, null, (int)timeout.TotalMilliseconds, Timeout.Infinite);

    request.BeginGetResponse(r =>
    {
        var httpRequest = (HttpWebRequest)r.AsyncState;
        var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
        using (var reader = new StreamReader(httpResponse.GetResponseStream()))
        {
            var response = reader.ReadToEnd();
            tcs.TrySetResult(response);
        }
    });

    return tcs.Task;
}
Claus Jørgensen
  • 25,882
  • 9
  • 87
  • 150
  • another great reply from Claus. Beats my comment/answer. Thanks! TPL is pretty awesome. didn't think about using that. – invalidusername Nov 03 '11 at 14:25
  • Claus, is that a better solution? I used the first answer in this thread and added NotifyComplete in the part of the code for the timeout, and also added NotifyComplete where the tile is beeing set. This way NotifyComplete should be called reardless of if the httpwebrequest timesout or not? I had my phone i flightmode all night and this morning the live tile started updating again. – John Nov 03 '11 at 17:58
  • Claus answer is mainly better because he is using the existing TPL framework to achieve the same thing I was doing but with a lot less code, and therefore less error prone. But my implementation works just as well as his. – invalidusername Nov 03 '11 at 18:04
  • Alright, cool! Thank you both! It's so wonderful to get some help when you get stuck. – John Nov 03 '11 at 18:31
  • Well, it's a *different* solution. I think invalidusername provided a nice solution as well. I was just bored at work when I wrote this ;-) – Claus Jørgensen Nov 03 '11 at 20:00
  • Claus, haha thats funny :) Thanks for helping us uout when you're bored. Invalidusername >> In you example, is the timeout set to 60*1000 = 60000 seconds? I need maximum 15 seconds so I guess specifying 15 adn then skipping the multiplication would work? Just a bit strange that it worked over night when my phone was in glight mode without breaking the scheduled task agent? – John Nov 03 '11 at 20:22
  • Probably because it didn't timeout? Anyway, I'll recommend using `TimeSpan.FromSeconds(15).TotalMilliseconds` rather than doing *math*. – Claus Jørgensen Nov 03 '11 at 20:25
  • Alright, but since I had my phone in flightmode all night and the tile updating continued this morning I'm a bit confused. But it's working so I'm happy :) – John Nov 03 '11 at 20:32
  • It wouldn't timeout with flightmode. It would just instantly fail. – Claus Jørgensen Nov 03 '11 at 20:34
  • Okay, that explains it. Last question before I leave you two alone :) My live tiles are usually only updated once every hour, aren't they supposed to update every 30 minutes? I'm using a periodic task and I'm printing the current time to the tile when it's beeing updated. Any ideas why this is so? – John Nov 03 '11 at 21:35
  • If I recall correctly, it depends on how you defined the tile task in the first place? You can set the re-occurrence interval manually, perhaps you did? – Claus Jørgensen Nov 03 '11 at 22:42
  • I don't define any interval at all. But I'm not alone with this problem: http://forums.create.msdn.com/forums/p/93299/565948.aspx – John Nov 04 '11 at 07:48
  • Claus, I decided to give your solution a try since I'm having problems with the other one (one the code times out the background task is disabled). In your solution I'm having problem with the private Task FetchData(TimeSpan timeout). Shopuld it really look like that? My error is: The type or namespace name 'Task' could not be found (are you missing a using directive or an assembly reference?) – John Nov 15 '11 at 11:00
  • You need TPL for Silverlight, as I linked in the post. – Claus Jørgensen Nov 15 '11 at 12:54
  • Yeah, downloaded it and installed it but can't find Extension Manager (using Visual Studio Express). Once it's installed, how do I move on? Sorry for beginners questions but my live tile is working just fine and everything is good to go except for that it never runs again after the httpwebrequest has timed out so I'm really eager finding a solution that is working. – John Nov 15 '11 at 13:06
  • See http://docs.nuget.org/docs/start-here/managing-nuget-packages-using-the-dialog – Claus Jørgensen Nov 15 '11 at 13:52
  • Thanks Claus. I'll try and if I fail I'll just have to make the other code sample work even when it times out. – John Nov 15 '11 at 14:17