Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

[wpdev] Trace page views with Application Insights

A few weeks ago, I’ve decided to test Application InsightsApplication Insights with one of my Windows Phone apps. Application Insights is a library provided by Microsoft that allows you to easily collect usage and performance traces from your app and display them on your Visual Studio Online portal.

The integration in a Windows Phone app is quite easy: reference the NuGet package, and call a method in your application constructor to initialize the component:

ClientAnalyticsSession.Default.Start("your Application Insights ID");

If you’re using Visual Studio 2013, you can also install the Application Insights Tools for Visual Studio extension, that will take care of those steps for you.

A lot of useful indicators are logged, such as the number of unique users, the version of their OS, their screen resolution, and so on. However, I quickly noticed that the page views weren’t logged, even though there’s a section dedicated to this in the Visual Studio Online portal. Fortunately, the API allows you to manually trace page views, and so I did by hooking the PhoneApplicationFrame.Navigated event:

// Make sure to call this line *after* the call to InitializePhoneApplication()
RootFrame.Navigated += RootFrameNavigated;
 
Then in the event handler, call the LogPageView with the URL of your page:
 
private void RootFrameNavigated(object sender, NavigationEventArgs e)
{
    ClientAnalyticsChannel.Default.LogPageView(e.Uri.ToString().TrimStart('/'));
}

Make sure you call the TrimStart method to remove any slash at the beginning of your page URI. For some reason, those URI don’t appear in the “Page views” screen of Application Insights.

After a few minutes/hours, you should be able to see the reports. You may notice a few navigations events to a page called “app://external”. They occur when the user navigates away from your app (either when exciting or when a launcher is triggered).

Posté le par KooKiz | 0 commentaire(s)
Classé sous : ,

[wpdev] Pushing Telerik controls a bit further: keeping the RadWindow open when navigating to another page

The RadWindow is a nice control for Windows Phone provided by Telerik. It allows you to display a popup, along with some neat helpers, especially for the popup placement. Unfortunately, I just ran along one of the limitations of this control.

My application displays a list of pictures. When tapping on a picture, I use the RadWindow to display some detailed information on the picture. In the popup, there’s also a “fullscreen” button. When pressed, the button navigates to a new page, where the user can view the picture in full screen, and zoom to his convenience. So far, so good.

That’s when I noticed an issue: when navigating to the fullscreen page, then back to the main page, the RadWindow is closed. After double-checking my code, I came to the conclusion that this behavior is built directly inside the RadWindow. It does make sense, but it is a problem in my case because when going back to the main page, the user will expect to find it in the exact same state he left it.

My first solution has been to re-open the RadWindow in the Loaded event of the page. It worked, but there was a slight delay between the loading of the page and the opening of the popup (a few hundred of milliseconds at most, but still noticeable). Even disabling the opening animation didn’t fix that. Is there a better workaround?

I tried to subscribe to the WindowClosing event of the RadWindow, then setting e.Cancel to “true” to cancel the closing. But somehow, the “cancel” instruction is ignored in this specific scenario. Digging into the RadWindow source code explains why:

if (this.OnWindowClosing() && !this.pageUnloaded)

There is a built-in check to prevent the user from cancelling the closing in this situation. Clearly, the RadWindow has been designed to forbid from doing what I’m trying to do. It’s time to use dirty hacks to bend the rules!

My bet here is to change is to act on the “pageUnloaded” field to trick the RadWindow into thinking the page isn’t being unloaded. My first try has been to directly set the “pageUnloaded” field to “false” in the WindowClosing event, but it led to weird side effects (the “close on back button press” feature wasn’t working anymore). If overwriting the value doesn’t work, what about preventing it to be set at all?

The only time this value is set is in the “OnPageUnloaded” method of the RadWindow. As its name indicates, this method is a handler for the “Unloaded” event of the page. Let’s just forcefully unsubscribe the event handler. The handler is set when opening the RadWindow. The control has a “UnsubscribeFromPageUnloaded” method, which makes our work a tad easier. The method is private, so we have to use reflection to call it every time we open the popup:

this.PopupDetail.IsOpen = true;
 
var unsubscribe = this.PopupDetail.GetType().GetMethod(
    "UnsubscribeFromPageUnloaded",
    System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
 
unsubscribe.Invoke(this.PopupDetail, null);

(PopupDetail is the name of my RadWindow)

Once this code has been added, the “pageUnloaded” field is never set, and we can cancel the WindowClosing event even if the page is being unloaded. The only subtlety left is: how to cancel the event only when the page is being unloaded, and not when we’re trying to legitimately close the popup? There’s many ways to do that, but I noticed that the RadWindow disables the closing animation on this specific situation, so we may as well use that to our advantage:

private void PopupDetail_WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
    var window = (RadWindow)sender;
 
    if (window.CloseAnimation == RadAnimation.Empty)
    {
        e.Cancel = true;
    }
}

(of course, it won’t work if you manually disabled the closing animation)

Once those few lines of code have been added, the RadWindow acts in the desired way: it doesn’t close automatically when the page is being unloaded, but it closes properly in all other situations.

 

Please note that the RadWindow wasn’t built to be used this way. It seems to work fine on my side, but it may have unwanted side-effects in some corner cases. Use this hack at your own risk.

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , ,

[wpdev] Give that memory back!

A weird behavior (one more) I noticed on Windows Phone, thanks to a question on StackOverflow (once more).

First, let’s create a simple Windows Phone application. The first page will contain three buttons: the first one navigates to another page, the second one displays the amount of memory used (by calling Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage), and the last one will forcefully call the garbage collector, wait for the finalizers to execute, and collect the memory freed by those finalizers:

private void ButtonNavigate_Click(object sender, RoutedEventArgs e)
{
    this.NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
 
private void ButtonShowMemory_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show(Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage.ToString());
}
 
private void ButtonCollectMemory_Click(object sender, RoutedEventArgs e)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}

In the second page, we simply create a few objects and bind them to a grid. The sole purpose is to simulate a page that would use a large amount of memory:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    this.ListBox.ItemsSource = Enumerable.Range(0, 50).Select(i => new
    {
        Title = i.ToString(),
        Payload = new byte[1024 * 512]
    });
}

Then, the test can start. Start the application, and display the amount of used memory. The tests are done with the application compiled in release mode and no debugger attached, to simulate exactly what’ll happen on a published application:

1

At that point, the memory used is 9.6 MB. Just in case, we tap the “Collect Memory” button, then look again at the memory usage. A small amount is freed, and the memory usage is now 9.5 MB. Nothing surprising so far.

Then, we navigate to the second page, and back to the first one (using the back button of the phone). We expect the memory usage to jump, and indeed it does:

2

38 MB of memory used. We’ve created 50 objects, each with a payload of 512 KB, so about 25 MB, plus the memory used by the page. So far, the memory usage is coherent. Now, we try to collect the memory, and the surprises begin:

3

Still 38 MB! Almost no memory has been freed. The page isn’t in the backstack anymore, it should have been collected, and yet…

Navigating once more to page 2, then back to page 1,and displaying the memory:

4

66 MB. We now have two instances of our second page kept into memory. What will happen this time if we collect the memory ?

5

44 MB. Apparently, one of the page has been collected. What’s happening? Why haven’t the two pages been collected at the same time, since none of them are in the backstack? Why has a page been collected even though it wasn't collected the first time?

Pressing the collect button once more, and…

6

17 MB. We’re not back exactly with the same amount of memory we started (probably because some libraries has been loaded at some point), but we allocate 25 MB of memory in the second page so we can say for sure it has been freed.

Trying once more. Navigating to page 2, back to page 1, and displaying the amount of memory: 40 MB. Pressing the collect button, then displaying the amount of memory: still 40 MB. Pressing the collect button once more, then displaying the amount of memory: 17 MB. Apparently, we have to collect the memory two times for the page to be freed.

Then, can we just chain the calls to the garbage collector? Let’s change the code of the collect button to simulate what happens when we tap two times on it:

private void ButtonCollectMemory_Click(object sender, RoutedEventArgs e)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
 
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}

Compiling the app, launching it, and displaying the amount of memory: 9.6 MB. Pressing the collect button: 9.6 MB. So far, the results are consistent with the first experience. Navigating to the second page, back to the first page, and displaying the memory: 38 MB. Pressing the collect button, and displaying the memory again:

7

Still 38 MB! Collecting again, and displaying the memory: 12 MB… Why do we still need to press the button two times even though we’re calling the garbage collector twice as much? Well, anyone who knows how the garbage collector works must have figured out that it would change nothing. Still, what happens when we press the button two times?

This time, we’re going to let the UI thread run between our two calls to the garbage collector:

private void ButtonCollectMemory_Click(object sender, RoutedEventArgs e)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
 
    this.Dispatcher.BeginInvoke(() =>
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    });
}

Then we do the test again. Initial memory: 9.6 MB. Navigating to second page and back to the first page: 38 MB. Pressing the collect button: 11.7 MB. We finally managed to free the unused memory!

Summing it up

What has our test showed? After leaving a page, calling the garbage collector once isn’t enough to free the memory used. You have to call it twice, letting the UI thread between the two calls.

What does it change? The memory limit on Windows Phone is really tight, especially on 512 MB devices. In normal situations, you shouldn’t have to call the garbage collector yourself: you can trust it to run automatically when the memory is running low. However, we just saw that calling it once doesn’t free the memory. It means that you can’t rely on that mechanism anymore: after leaving a page that use huge amounts of memory, even if the garbage collector is triggered automatically, it won’t be able to free the memory and you may run into an OutOfMemory exception, even though those objects aren’t used anymore!

In most situations, you shouldn’t have to worry about it. But if your application tends to crash because of lack of memory, you may want to consider calling manually the garbage collector after leaving a page that allocates large amounts of memory. And don’t forget to let the UI thread run between the two calls!

Oh, but it gets even better when you try to do that in the “OnNavigatedTo” event of the first page. You don’t have to call the garbage collector once, or twice, but thrice!

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    this.Dispatcher.BeginInvoke(() =>
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
 
        this.Dispatcher.BeginInvoke(() =>
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
 
            this.Dispatcher.BeginInvoke(() =>
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            });
        });
    });
}

And trust me, it works.

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , ,

[wpdev] Pushing Telerik controls a bit further: pull to refresh

While working on a new version of my ImageBoard Browser app (warning: shameless self-placement), I decided to try and use the pull-to-refresh feature integrated to the Telerik’s RadDataBoundListBox control. The pull-to-refresh is about automatically refreshing a list when the user scrolls to its end. It’s a feature a bit lacking in terms of discoverability, and it’s therefore recommended to add a few UI hints to help the user understand he can still scroll after reaching the end of the list. However, it’s a gesture that feels great at usage, bends into the UI flow, and saves a few precious pixels of screen space.

Enabling the feature on the RadDataBoundListBox is straightforward: just set the “IsPullToRefreshEnabled” property to True, and subscribe to the “RefreshRequested” event. When the event is triggered, the control automatically display a neat loading animation. When you’re done loading the new data, simply call the “StopPullToRefreshLoading” method to stop the animation.

1

So far, so good. The feature works flawlessly, and I was about to call it a day. That’s when I noticed a major flaw: when the list is empty (either because the query yielded no result, or because of a network error), the pull-to-refresh feature doesn’t work. Apparently, Telerik is aware of that behavior. Maybe it will be changed in an upcoming version, but for now, how to work around this issue?

The objective is to allow the user to refresh the list when there is no element. Displaying a refresh button is the straightforward solution, but I do not want the UI to be cluttered by an additional button. Therefore, that button must be visible only when the list is empty. Chance: this scenario is actually supported by the RadDataBoundListBox: just add the template of the refresh button in the “EmptyContent” property of the list, and done!

… Almost. Unfortunately, it brings an UI inconsistency. When the user presses the refresh button, I need to display a loading animation. But the RadDataBoundListBox has no property or method to force the apparition of the pull-to-refresh loading animation! I therefore have to make my own loading animation. And since the whole point is to make the UI neat and clean, I need to perfectly mimic the ListBox’s built-in animation. How to do that? Thankfully, Telerik publishes their source code along with the controls, so we can dig in and use the exact same template.

From there, it’s pretty straightforward. I use a “IsLoading” boolean property in the ViewModel to indicate when the data is loaded. I use it along with a BooleanToVisibilityConverter (the one provided with the Telerik controls) to alternatively display the refresh button or the loading animation. Everything is wrapped in the “EmptyContent” property of the RadDataBoundListBox to be displayed only when the list is empty:

<telerikPrimitives:RadDataBoundListBox.EmptyContent>
    <Grid Margin="{StaticResource PhoneMargin}"
    VerticalAlignment="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
 
        <telerikPrimitives:RadBusyIndicator
    AnimationStyle="AnimationStyle7"
    ContentPosition="Right"
    Foreground="{StaticResource PhoneForegroundBrush}"
    HorizontalAlignment="Left"
    VerticalAlignment="Stretch"
    VerticalContentAlignment="Top"
    Content="{x:Null}"
    IsRunning="{Binding Path=IsLoading}" />
 
        <TextBlock Grid.Column="1"
                FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                FontSize="{StaticResource PhoneFontSizeMediumLarge}"
                Margin="30 0 0 0"
                Visibility="{Binding Path=IsLoading, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}"
                Text="loading" />
 
        <Button x:Name="ButtonRefresh"
    Grid.Column="0"
    Grid.ColumnSpan="2"
    Click="ButtonRefreshClick"
    Visibility="{Binding Path=IsLoading, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}, ConverterParameter=True}"
    Content="Refresh" />
    </Grid>
</telerikPrimitives:RadDataBoundListBox.EmptyContent>

And that’s it! Our loading animation look identical to the pull-to-refresh one, thus avoiding the inconsistency.

2

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , ,

[wpdev] Storage bug in MediaLibrary.SavePicture

I’ve received many reports lately of users complaining of abnormal storage usage from one of my apps, Imageboard Browser.The application was in some cases storing hundreds of megabytes! I thought at first that it was just the "other storage” issue, but I did some further investigation just in case.

After some trial and error and a bit of luck, I’ve discovered that the bug lies in the methods MediaLibrary.SavePicture and MediaLibrary.SavePictureToCameraRoll. As their name indicate, they are used to save a picture to the phone’s picture hub, and they do their job pretty well. Except that ever time you use them, a copy of your picture is stored in the isolated storage!

To confirm this, I’ve written a small application with two buttons. It’s as simple as it can get: the “download” button downloads a picture, and saves it to the picture hub. The “storage” button displays the contents of the isolated storage:

   1: private void ButtonDownload_Click(object sender, RoutedEventArgs e)
   2: {
   3:     var webClient = new WebClient();
   4:  
   5:     webClient.OpenReadCompleted += WebClient_OpenReadCompleted;
   6:  
   7:     webClient.OpenReadAsync(new Uri("http://i.s-microsoft.com/global/ImageStore/PublishingImages/FY13/Asset/highlights/WPbuy_BG_0225_420x210_FR_FR.jpg"));
   8: }
   9:  
  10: private void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
  11: {
  12:     using (var mediaLibrary = new MediaLibrary())
  13:     {
  14:         mediaLibrary.SavePicture("1", e.Result);
  15:     }
  16:  
  17:     MessageBox.Show("Done");
  18: }
  19:  
  20: private void ButtonStorage_Click(object sender, RoutedEventArgs e)
  21: {
  22:     using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
  23:     {
  24:         var files = isolatedStorage.GetFileNames();
  25:  
  26:         MessageBox.Show(string.Join("\r\n", files));
  27:     }
  28: }

When running the application for the first time, the “storage” button shows that the isolated storage is empty:

cap1

 

Then, after using the “download” button to download a picture and save it to the picture hub, the “storage” button finds one file!

cap2

 

Since there’s no built-in tool in Windows Phone to explore the isolated storage, it’s a bug that can be easily overlooked.

I’ve been doing a few more tests about this bug, and here are my findings so far:

- I could not reproduce the bug on Windows Phone 7, only Windows Phone 8 seems affected.

- I could reproduce the issue on various devices (Lumia 822, Lumia 920, and the emulator). So it’s unlikely that the bug is hardware-specific.

- The bug occurs only when saving JPG pictures. Nothing is stored on the isolated storage when saving a PNG picture.

- If I anticipate and create beforehand a file on the isolated storage with the same name, it will be overwritten. If I lock it by keeping the stream open, the MediaLibrary.SavePicture method throws an exception.

- The MediaLibrary.SavePicture has an overload that expect a byte array rather than a stream. It suffers from the same bug.

- The name of the file created in the isolated storage is the same name as the one provided to the MediaLibrary.SavePicture, with “.jpg” appended to the end (even if the name already contained “.jpg”), and dots ‘.’ replaced by underscores ‘_’. Knowing this, it’s possible to write code to automatically delete the file.

- On Nokia’s storage tool, those files are reported in the application’s dedicated storage. Not in the temp files or the “others” storage.

In a nutshell, if you’re saving pictures to the picture hub, make sure you’re deleting the temporary files in the isolated storage afterwards. I hope Microsoft will be able to provide a fix quickly, as it can quickly become an issue on devices with low storage.

Also, I only have been able to test on the latest version of the WP8 OS (8.0.10211.204). If you still have a device with an older version of the OS, could you please try to reproduce the bug on it? I’m eager to know if the bug was introduced on the latest version of WP8 or if it has always been standing there.

Posté le par KooKiz | 0 commentaire(s)

[wpdev] Memory leak with BitmapImage

There’s a memory leak that has been bothering me for a while in my Imageboard Browser app. The scenario is simple: a slideshow feature, where a new picture is loaded from an url every few seconds. After a while, the app crashes with an OutOfMemory exception. I’ve never been able to find the source of the leak, so I settled for a dirty workaround consisting in loading a fake picture in the BitmapImage instance to force it to release the memory:

   1: private void DisposeImage(BitmapImage image)
   2: {
   3:     if (image != null)
   4:     {
   5:         try
   6:         {
   7:             using (var ms = new MemoryStream(new byte[] { 0x0 }))
   8:             {
   9:                 image.SetSource(ms);
  10:             }
  11:         }
  12:         catch (Exception)
  13:         {
  14:         }
  15:     }
  16: }

It fixed the issue, but I didn’t dig much further. However, over the last few weeks I’ve seen many similar leaks reported on StackOverflow, so I’ve finally decided to get to the bottom of the problem.

Setup

To diagnose a memory issue, the first thing to do is to have a repro case. It must be as simple as possible and have no random components, to be able to focus on the issue.

First, I needed a list of pictures. I browsed the Internet a bit, found a wallpaper website with predictable urls, and set up a small program:

   1: public partial class MainPage : PhoneApplicationPage
   2: {
   3:     public MainPage()
   4:     {
   5:         this.CurrentIndex = 100;
   6:  
   7:         this.InitializeComponent();
   8:     }
   9:  
  10:     protected int CurrentIndex { get; set; }
  11:  
  12:     protected override void OnNavigatedTo(NavigationEventArgs e)
  13:     {
  14:         base.OnNavigatedTo(e);
  15:  
  16:         this.LoadNextPicture();
  17:     }
  18:  
  19:     void LoadNextPicture()
  20:     {
  21:         GC.Collect();
  22:         GC.WaitForPendingFinalizers();
  23:  
  24:         Debug.WriteLine(Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage);
  25:  
  26:         Thread.Sleep(1000);
  27:  
  28:         var bitmap = new BitmapImage();
  29:         bitmap.ImageOpened += bitmap_ImageOpened;
  30:         bitmap.ImageFailed += bitmap_ImageFailed;
  31:  
  32:         bitmap.UriSource = new Uri(string.Format("http://www.maximumwallhd.com/fonds-ecran/3d/abstrait/fond-ecran-3d-abstrait-{0}.jpg", this.CurrentIndex));
  33:  
  34:         this.MainImage.Source = bitmap;
  35:  
  36:         this.CurrentIndex++;
  37:     }
  38:  
  39:     void bitmap_ImageFailed(object sender, ExceptionRoutedEventArgs e)
  40:     {
  41:         Debug.WriteLine("Failed: " + (this.CurrentIndex - 1) + " - " + e.ErrorException);
  42:     }
  43:  
  44:     void bitmap_ImageOpened(object sender, RoutedEventArgs e)
  45:     {
  46:         this.LoadNextPicture();
  47:     }
  48: }


Basically, it calls the garbage collector, display the total memory usage, wait a while (to avoid triggering any anti-leech protection from the website), load the picture, then back to step one. The XAML part just contains an Image control called “MainImage”.

Start the app, and sure enough we’re leaking:

image

 

After only nine pictures, the app crashes with an OutOfMemory exception. Good! Now let’s try to find out what’s going on.

The workarounds

First of all, the workarounds. The two I know are : loading a dummy picture (as explained in the introduction), or setting the UriSource to null. Let’s try both.

Loading a dummy picture:

image

Setting UriSource to null:

image

The curves are really similar, in both case we’ve stopped leaking memory (at least: large amounts of memory). Now the problem is: we obviously shouldn’t have to use workarounds. Is there another way?

Finding the cause

After a bit of trial and error, I found something that I really wasn’t expecting. Removing the ImageOpened and ImageFailed event handler fixes the leak!

By simply changing the ImageOpened method to this:

   1: void bitmap_ImageOpened(object sender, RoutedEventArgs e)
   2: {
   3:     var bitmap = (BitmapImage)sender;
   4:  
   5:     bitmap.ImageOpened -= bitmap_ImageOpened;
   6:     bitmap.ImageFailed -= bitmap_ImageFailed;
   7:  
   8:     this.LoadNextPicture();
   9: }

The memory usage is higher than when using the workaround so I let the test run longer, but it looks stable:

image

Why the increase in memory usage? We’re calling LoadNextPicture from the ImageOpened event handler. Therefore, we’re still in the picture loading callstack, and the garbage collector has nothing to clean yet when it’s called. To test this theory, I replaced the call to LoadNextPicture by “Dispatcher.BeginInvoke(() => this.LoadNextPicture());”. Also, we’re calling GC.Collect before setting the new image source. Thus it isn’t available yet for collection, and we actually have two pictures at the same time in memory. After adding a GC.Collect after the image source assignment, we have the same memory curve as when using the workarounds:

image

 

Digging further

Now, we know that the leak occurs when not removing the ImageOpened and ImageFailed event handlers. The problem is: it shouldn’t happen.

As a quick reminder, the garbage collector works by keeping track of what is called “GC roots”. Those are references that can’t be collected: for instance, static variables. Then, the garbage collectors browses the objects referenced by those roots, and the objects referenced by those references, and so on. After recursively browsing the reference tree, all objects that weren’t found are freed from the memory.

In our case, the PhoneApplicationPage object is used by the application, and therefore won’t be freed. Therefore, when an object is referenced by the page, it won’t be freed either.

On top of that, we’ve got event handlers. Event handlers are a frequent source of memory leaks. Imagine we’re creating an object called “ChildObject”. We assign a method of this object, called “SomeMethod”, to the “Loaded” event of our page. Then, even though we’re not referencing this object any further in the code, the object is still referenced by the page (through the event handler) and will be kept alive as long as the page is alive!

Is that our problem here? No, because we’re assigning a method of our page to the ImageOpened and ImageFailed events. The BitmapImage is keeping a reference to our page, but not the other way around. So the BitmapImage is keeping the page alive, but the page shouldn’t keep the BitmapImage alive. The cause of our leak must be elsewhere.

Just to make sure, I reverted to the leaky version of the program, and created the described “ChildObject” class. It implements a finalizer, to know when it’s freed by the garbage collector, and an empty Bitmap_ImageOpened method:

 

   1: public class ChildObject
   2: {
   3:     ~ChildObject()
   4:     {
   5:         Debug.WriteLine("Finalizer");
   6:     }
   7:  
   8:     public void Bitmap_ImageOpened(object sender, RoutedEventArgs e)
   9:     {
  10:     }
  11: }

Then, I assign this “Bitmap_ImageOpened” method to the ImageOpened handler of the BitmapImage. Our LoadNextPicture method is now:

   1: void LoadNextPicture()
   2: {
   3:     GC.Collect();
   4:     GC.WaitForPendingFinalizers();
   5:  
   6:     Debug.WriteLine(Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage);
   7:  
   8:     Thread.Sleep(1000);
   9:  
  10:     var bitmap = new BitmapImage();
  11:  
  12:     var test = new ChildObject();
  13:  
  14:     bitmap.ImageOpened += test.Bitmap_ImageOpened;
  15:  
  16:     bitmap.UriSource = new Uri(string.Format("http://www.maximumwallhd.com/fonds-ecran/3d/abstrait/fond-ecran-3d-abstrait-{0}.jpg", this.CurrentIndex));
  17:  
  18:     this.MainImage.Source = bitmap;
  19:  
  20:     Debug.WriteLine(Microsoft.Phone.Info.DeviceStatus.ApplicationCurrentMemoryUsage);
  21:  
  22:     this.CurrentIndex++;
  23: }

When executing the application, the console shows:

5070848
26812416
Finalizer
37093376
Finalizer
53968896
Finalizer
70389760
Finalizer
79781888
Finalizer
96452608
Finalizer
113881088
Finalizer
129703936
Finalizer
146341888

The finalizer is called. So even though we’re leaking memory, the ChildObject is freed. Which means that the BitmapImage is freed as well!

To be sure, I tried to voluntarily keep the BitmapImage alive. I’ve done so by creating a property of type “List<object>” and storing the BitmapImage instances inside. Executing the app then provides the following output:

4993024
27533312
37752832
53293056
70389760
79060992
95039488
113156096
129724416
146386944

The finalizer isn’t called anymore. We’ve proven that the BitmapImage is indeed cleaned by the garbage collector. So what are we actually leaking? Maybe the memory profiler can tell us?

Loading the first snapshot, I tried seeing what was consuming the memory:

image

… Nothing. Which means we’re probably leaking native memory (the profiler only shows managed memory, .NET objects that is).

One more step

There’s nothing more we can do with Visual Studio. It’s time to start Reflector and decompile the framework’s assemblies to try to understand what’s happening.

We know the leak is centered around the event handler, so we can directly go and see what’s going on there.

Nothing interesting in the code of the ImageOpened event:

   1: public event EventHandler<RoutedEventArgs> ImageOpened
   2: {
   3:     add
   4:     {
   5:         base.AddEventListener(DependencyProperty.RegisterCoreProperty(0x659e, null), value);
   6:     }
   7:     remove
   8:     {
   9:         base.RemoveEventListener(DependencyProperty.RegisterCoreProperty(0x659e, null), value);
  10:     }
  11: }
  12:  

Removing the event handler fixes the leak, so let’s see what is in this “RemoveEventListener” method.

   1: internal void RemoveEventListener(DependencyProperty property, Delegate handler)
   2: {
   3:     this._coreTypeEventHelper.RemoveEventListener(this, property, handler);
   4: }

Meh, just a wrapper. Let’s see the _coreTypeEventHelper.RemoveEventListener method:

   1: internal void RemoveEventListener(IManagedPeer obj, DependencyProperty property, Delegate handler)
   2: {
   3:     foreach (KeyValuePair<int, EventAndDelegate> pair in this.EventAndDelegateTable)
   4:     {
   5:         if (!pair.Value.WrappedDelegate.Equals(handler) || ((!pair.Value.WrappedEvent.Equals(property) && ((!(pair.Value.WrappedEvent is CoreDependencyProperty) || !(property is CoreDependencyProperty)) || (((CoreDependencyProperty) pair.Value.WrappedEvent).m_nKnownId != ((CoreDependencyProperty) property).m_nKnownId))) && !QuirksMode.SkipEventComparisonDuringRemoveEventListener()))
   6:         {
   7:             continue;
   8:         }
   9:         string eventName = "M@" + pair.Key.ToString();
  10:         XcpImports.RemoveEventListener(obj, property, eventName);
  11:         this.EventAndDelegateTable.Remove(pair.Key);
  12:         break;
  13:     }
  14: }
  15:  

The method checks a few things, then calls the XcpImports.RemoveEventListener method, and finally removes something from the “EventAndDelegateTable” dictionary. Now that’s interesting, maybe the memory is used by the dictionary?

The dictionary stores instances of an internal class called “EventAndDelegate”. It only contains a reference to the dependency property and to the delegate used as event handler (in our case: the bitmap_ImageOpened method). Nothing big enough to explain the leak.

Then maybe the XcpImports.RemoveEventListener method?

   1: [SecuritySafeCritical]
   2: internal static unsafe void RemoveEventListener(IManagedPeerBase obj, DependencyProperty property, string eventName)
   3: {
   4:     CheckThread();
   5:     CValue outval = new CValue();
   6:     outval.SetCountAndType(eventName.Length, VType.valueString);
   7:     fixed (char* str = ((char*) eventName))
   8:     {
   9:         char* chPtr = str;
  10:         outval.m_pchValue = chPtr;
  11:         CheckHResult(RemoveEventListenerNative(obj.NativeObject, property.m_nKnownId, ref outval));
  12:         GC.KeepAlive(obj);
  13:     }
  14: }
  15:  
  16:  

It’s getting really low-level here. But the interesting part is the line:

   1: CheckHResult(RemoveEventListenerNative(obj.NativeObject, property.m_nKnownId, ref outval));

We’re calling a method called “RemoveEventListenerNative”. As its name indicates, it’s a native method, so we can’t use Reflector to see what it’s doing. But the method receives a parameter called “obj.NativeObject”, making it a nice candidate for our leak.

Stepping back and digging into the “add” part of the event, we reach a similar AddEventListener method, passing the “obj.NativeObject” parameter to a method called “AddEventListenerNative”:

   1: CheckHResult(AddEventListenerNative(obj.NativeObject, property.m_nKnownId, ref outval, handledEventsToo));

How to know whether it’s the source of the leak or not? By calling it ourselves!

Back in Visual Studio, with the leaky version of the app. Let’s set a breakpoint in the bitmap_ImageOpened method, then explore the BitmapImage object with the QuickWatch window:

image

Here we find our “EventAndDelegateTable” dictionary, with only one entry: our ImageOpened event handler (I removed the ImageFailed event handler to have as little noise as possible).

The XcpImports.RemoveEventListener takes three parameters: the instance of the BitmapImage class, the dependency property, and an “eventName” string. This string is the concatenation of the constant “M@” and the key of the entry in the EventAndDelegateTable table. In our case, we see in the QuickWatch window that the key is “7”. Therefore, we execute this in the “Immediate” window of Visual Studio:

MS.Internal.XcpImports.RemoveEventListener(bitmap, DependencyProperty.RegisterCoreProperty(13409, null), "M@7");

Then we resume the code execution by pressing F5, wait for the breakpoint to be hit again, and do the whole process once more. This time, the key is “8”, looks like it’s incremented by 1 every time.

After doing that a few times, we can look at the memory usage in the Output window:

28971008
37257216
37691392
44126208
37838848
38096896
45613056
45699072
45240320

The memory usage stays below 50MB. When compared to the previous values, it becomes obvious that we’ve found the source of the leak.

Conclusion

Up to this point, what have we found?

- When assigning an event handler to a BitmapImage (ImageOpened or ImageFailed, I haven’t tested with DownloadProgress but it’s probably the same), the runtime internally keeps a reference to the picture’s native object.

- When removing the event handler, the native object is freed as expected. If the event handler isn’t removed, the runtime keeps the reference to the native object, therefore leaking huge amounts of memory.

This is a bug. Our code follows the .NET guidelines, and we’ve proven that the managed instance of the BitmapImage object is released by the garbage collector. So we’re not keeping stray references anywhere.

What should the framework developers do?

It’s hard to tell, since I don’t know how the reference is used by the native code. But they should at least consider making the BitmapImage implement the IDisposeable interface, and clean the event handlers in the Dispose method. The IDisposeable interface is the .NET way to tell the developer “be extra careful or you may be leaking memory”. In the current implementation of the BitmapImage class, there is no way to suspect that a leak will occur.

What should third-party Windows Phone developers do?

When using a BitmapImage, ensure that you’re removing the event handlers in every possible code path. Use Visual Studio’s Performance Analyzer and test your code with very large pictures to make sure you’re not leaking memory. And set the UriSource of the BitmapImage to null, as an extra precaution.

[WP8] Debugging and fixing a bug in Windows Phone sync tool

Since a few days, I had this annoying bug in the application used to sync my Windows Phone with my desktop computer: every time I tried to synchronize podcasts, the application just crashed. After a while, I decided it would be a nice debugging exercise to dig into the issue myself.

First step: reproducing the issue. It was easy enough, the app automatically crashed when clicking the “Sync button”. And sure enough, this time again, it did crash:

5

 

I suspected that the application was written in .NET (most Windows Phone related apps are), so I clicked on Debug and selected Visual Studio 2012. VS directly showed me that an InvalidCastException was the issue, and that the method was called in ITunesMusicSyncSource.GetLocationForReverseTransfer.

1

It’s precise enough. From there, I started Reflector, loaded the Microsoft.WPSync.Sync.Source.iTunes assembly, and navigated to the specified method:

6

At first, I couldn’t find the problem. There’s sure a few casts in there, but none of them seemed nasty at first glance. I still had my Visual Studio open, so I started manually trying each cast in the QuickWatch window until I found the wrong one:

2

Indeed, according to the signature, the “item.Properties.ObjectForKey” method returns an object. If the property is not found, the default value (passed as the second parameter) is returned. So in this case, if the ZMEDIAITEM_ATTRIBUTE_BOOKMARK property isn’t found, the value 0 is returned. Then, the return value is casted to long. Except that 0 is an int! A cast from int to long is valid, but we first need to unbox the value by explicitly casting it to long.

Basically:

   1: long l = 0L;
   2: object obj = (object)l;
   3: int a = (int)obj; // Invalid
   4: int a = (int)(long)obj; // Valid

That’s an easy-to-make mistake, so I’m not really surprised it wasn’t detected in the released product.

Anyway, now that we know what the bug is, can we fix it? Sure. By using a Reflector add-in called ReflexIL, we can rewrite the bogus code.

3

It’s for those cases that knowing a bit of CIL can be quite useful. The “0” integer value is loaded on the stack by calling “ldc.i4.0”, then boxed to a System.Int32. We simply replace the instruction to box to a System.Int64, and add a “conv.i8” right before to convert the value to long. Then right-click on the assembly, and click on “Save as” to export the modified assembly. Since the original assembly is signed, ReflexIL asks what if we want to sign the new assembly:

4

We don’t have the signature file, so we can’t possibly re-sign it. So I’ve just selected “Register it for verification skipping (on this computer)” to allows the .NET framework to load the unsigned assembly. Since I don’t intend to publish the file, it shouldn’t be a problem.

Once the new assembly is generated, just replace the old one in the application’s folder (don’t forget to make a backup!)

Now start the application again, try to sync the podcast, and... Sure enough, it works!

7

Posté le par KooKiz | 0 commentaire(s)

[WP8] Programmatically terminate a Silverlight app

Exiting programmatically as always been an issue for Silverlight applications on Windows Phone 7. A few workaround existed, from throwing an exception to referencing a XNA assembly and using the ‘Game.Exit()’ method.

Windows Phone 8 brings a new API, that can be used from Silverlight applications: ‘Application.Terminate()’

Using it is really straightforward:

   1: Application.Current.Terminate();

A word of advice though: calling this method will immediately kill your app. It means that the ‘Application.Closing’ event won’t be triggered, and the contents of the ‘IsolatedStorageSettings.ApplicationSettings’ dictionary won’t be automatically saved to the isolated storage. Therefore, if needed, don’t forget to save that dictionary before calling the ‘Terminate’ method:

   1: IsolatedStorageSettings.ApplicationSettings.Save();
   2: Application.Current.Terminate();
Posté le par KooKiz | 0 commentaire(s)

[WP8] Programmatically change the lock screen picture

Windows Phone 8 introduces a new API to allow apps to change the background picture displayed on the lock screen. The displayed picture can be picked from the application’s resources or from the isolated storage.

To use this feature, you need first to declare it in the application’s manifest. Just add the following extension in the “Extensions” node:

   1: <Extension ExtensionName="LockScreen_Background" ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" />

If you don’t already have an “Extensions” node in the manifest, you’ll have to add it at the same level as the “Capabilities” and “Tasks” nodes:

   1: <Capabilities>
   2:   <!-- ... -->   
   3: </Capabilities>
   4: <Tasks>
   5:   <!-- ... -->
   6: </Tasks>
   7: <Tokens>
   8:   <!-- ... -->
   9: </Tokens>
  10: <Extensions>
  11:   <Extension ExtensionName="LockScreen_Background" ConsumerID="{111DFF24-AA15-4A96-8006-2BFF8122084F}" TaskID="_default" />
  12: </Extensions>

Once the manifest is updated, there’s one remaining step before being able to change the wallpaper: the application must first ask the user for permission. You can check if your app is allowed by checking the value of the “LockScreenManager.IsProvidedByCurrentApplication” property. If the app isn’t allowed, use the “LockScreenManager.RequestAccessAsync()” method to display a popup asking the user for permission.

Then, all you have to do is calling the “LockScreen.SetImageUri” method with the URI of the picture. The URI must be prefixed by “ms-appx:///” if the picture is stored in the resources, or “ms-appdata:///Local/” if the picture is stored in the isolated storage.

The final code should look like (for a picture stored in the isolated storage):

   1: try
   2: {
   3:     var isProvider = Windows.Phone.System.UserProfile.LockScreenManager.IsProvidedByCurrentApplication;
   4:  
   5:     if (!isProvider)
   6:     {
   7:         var permission = await Windows.Phone.System.UserProfile.LockScreenManager.RequestAccessAsync();
   8:  
   9:         isProvider = permission == Windows.Phone.System.UserProfile.LockScreenRequestResult.Granted;
  10:     }
  11:  
  12:     if (isProvider)
  13:     {
  14:         var uri = new Uri("ms-appdata:///Local/LockScreenPicture.jpg", UriKind.Absolute);                    
  15:  
  16:         Windows.Phone.System.UserProfile.LockScreen.SetImageUri(uri);
  17:     }
  18:     else
  19:     {
  20:         MessageBox.Show("Couldn't update the lockscreen picture.");
  21:     }
  22: }
  23: catch (Exception ex)
  24: {
  25:     MessageBox.Show("An error occured while updating the lockscreen picture: " + ex.Message);
  26: }

Of course, once the application is authorized, the lockscreen picture can be changed directly from a background agent. It is a new and interesting way to display information to the user and push phone’s personalization one step further.

Posté le par KooKiz | 0 commentaire(s)

[WP7] Dynamically toggle PanoramaItem visibility

A strange issue with the Panorama control, that has been brought to my attention by @lancewmccarthy, and which has also been encountered by some people on StackOverflow.

The original scenario was a bit too complex for a blog post, but we can reproduce it in a much simpler way.

Create a new page, and add a panorama control called ‘Panorama’. Then add two PanoramaItem, and put a button in the first one:

   1: <controls:Panorama x:Name="Panorama">
   2:     <controls:PanoramaItem Header="Item1" x:Name="Item1">
   3:         <Button Content="Test" Click="Button_Click" />
   4:     </controls:PanoramaItem>
   5:     <controls:PanoramaItem Header="Item2" x:Name="Item2" >
   6:     </controls:PanoramaItem>
   7: </controls:Panorama>

In the click event handler of the button, we toggle the visibility of the second item of the panorama:

   1: private void Button_Click(object sender, RoutedEventArgs e)
   2: {
   3:     this.Item2.Visibility = this.Item2.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
   4: }

Start the application, try tapping on the button, and the visibility of the PanoramaItem changes as expected.

Now, let’s just change the XAML to set the visibility of the second item of the panorama to ‘Collapsed’:

   1: <controls:Panorama x:Name="Panorama">
   2:     <controls:PanoramaItem Header="Item1" x:Name="Item1">
   3:         <Button Content="Test" Click="Button_Click" />
   4:     </controls:PanoramaItem>
   5:     <controls:PanoramaItem Header="Item2" x:Name="Item2" Visibility="Collapsed">
   6:     </controls:PanoramaItem>
   7: </controls:Panorama>

Start the application again, tap on the button, and… Nothing happens! What’s going on?

Diving a bit in the Panorama control source code, using good ol’ friend Reflector, we can see that the Panorama host a PanoramaPanel control. The PanoramaPanel contains most of the items placement logic, and has a ‘VisibleChildren’ property. Looks promising!

image

Only a handful of methods access this property, and we can quickly conclude that the ‘VisibleChildren’ collection is populated only by the `MeasureOverride’ method. From there, we can elaborate a theory: at loading time, our panorama item isn’t visible, and therefore isn’t added to the `VisibleChildren’ collection. Later, when we change the visibility of the PanoramaItem, the panorama’s position isn’t invalidated, so the list of visible items isn’t refreshed, and the PanoramaItem isn’t added back to the ‘VisibleChildren’ collection.

It’s easy to test, let’s just change our click event handler to force the panorama to re-compute its size:

   1: private void Button_Click(object sender, RoutedEventArgs e)
   2: {
   3:     this.Item2.Visibility = this.Item2.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
   4:     this.Panorama.Measure(Size.Empty);
   5: }
And sure enough, it works! Now, the ‘Measure’ method expects a parameter. Giving ‘Size.Empty’ basically tells the control “Use all the space available”. While it should be ok in most case, it may have unforeseen consequences in some specific scenarios.

Unfortunately, just calling the ‘InvalidateMeasure’ method of the panorama doesn’t work. It looks like the event isn’t propagated to the child panel. And we can’t directly access the child panel because it isn’t exposed in a public property. Is there another way out?

By randomly browsing the source code of the PanoramaPanel with Reflector, we can see a ‘NotifyDefaultItemChanged’ method, which looks quite promising:

   1: internal void NotifyDefaultItemChanged()
   2: {
   3:     base.InvalidateMeasure();
   4:     base.InvalidateArrange();
   5:     base.UpdateLayout();
   6: }

Now if we could just trigger this method, our problem would be solved. Using the ‘Analyze’ feature of Reflector, we can see that this method is called by the setter of the ‘DefaultItem’ property of the panorama:

image

That’s perfect! We just have to change the panorama’s default item to ensure that our PanoramaItem becomes visible as expected. Since we don’t want to disrupt the panorama, and since there’s no specific check in the property setter, we just assign back the value of the property to itself:

   1: private void Button_Click(object sender, RoutedEventArgs e)
   2: {
   3:     this.Item2.Visibility = this.Item2.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
   4:     this.Panorama.DefaultItem = this.Panorama.DefaultItem;
   5: }

Now the panorama is behaving as expected, and the visibility of the PanoramaItem is correctly updated, even if the item was collapsed when the page was loaded.

Posté le par KooKiz | 0 commentaire(s)

[WP7] IsolatedStorageException when opening a file played by a MediaElement

Today we’ll dig a little into Windows Phone’s base class library, thanks to a question I found on stack overflow.

The problem occurs with this simple code, used to load a music file from the isolated storage, and play it with a MediaElement:

   1: using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
   2: {            
   3:      using (var isfs = new IsolatedStorageFileStream(selected.Path, FileMode.Open, isf))
   4:      {                        
   5:           this.media.SetSource(isfs);              
   6:           isfs.Close();                        
   7:      }          
   8:           
   9:      isf.Dispose();
  10: }

When executed for the first time, this code works fine. When executed a second time with the same file, it throws an IsolatedStorageException: “Operation not permitted on IsolatedStorageFileStream”.

There isn’t many situations where opening a file from the isolated storage will throw an exception. The main two reasons are: an invalid filename, or a file sharing issue. Since the error only occurs when trying to play the same file twice, the second reason seems a pretty good candidate.
The default value of the ‘FileShare’ parameter in the IsolatedStorageFileStream constructor is ‘FileShare.ReadWrite’. If a file is opened with this flag, any concurrent operation on the same file will fail. And indeed, everything works fine once the code is replaced by:

   1: using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
   2: {            
   3:      using (var isfs = new IsolatedStorageFileStream(selected.Path, FileMode.Open, FileAccess.Read, isf))
   4:      {                        
   5:           this.media.SetSource(isfs);              
   6:           isfs.Close();                        
   7:      }          
   8:           
   9:      isf.Dispose();
  10: }

Now the thing that has been bugging me is: why is there any concurrency issue, since the stream is correctly closed? Note that the ‘Stream.Close’ method is even explicitly closed, even though the dispose should suffice!

To figure that out, I decided to launch Reflector and dig into the source code of the MediaElement.SetSource method:

   1: public void SetSource(Stream stream)
   2: {
   3:     if (stream == null)
   4:     {
   5:         throw new ArgumentNullException("stream");
   6:     }
   7:     if (stream.GetType() != typeof(IsolatedStorageFileStream))
   8:     {
   9:         throw new NotSupportedException("Stream must be of type IsolatedStorageFileStream");
  10:     }
  11:     IsolatedStorageFileStream stream2 = stream as IsolatedStorageFileStream;
  12:     stream2.Flush();
  13:     stream2.Close();
  14:     this.Source = new Uri(stream2.Name, UriKind.Absolute);
  15: }
  16:  
  17:  
  18:  
  19:  

As surprising as it may first seem, the SetSource method doesn’t use the data stored in the stream. It even flushes and closes the stream! All it does is just storing the name of the stream, and the MediaElement will manually re-open the file when you call the ‘Play’ method. And since the control has it own handle on the file, any attempt to open the file with exclusive access while the music is playing will fail.

It’s interesting to note that you can save all the pain of initializing the isolated storage and opening the file. Just setting the file’s URI to the ‘Source’ property of the MediaElement will have exactly the same result,  since the ‘SetSource’ method does nothing more!

Link to the original question on StackOverflow: http://stackoverflow.com/questions/10403631/getting-isolatedstorageexception-operation-not-permitted-on-isolatedstoragefile/

Posté le par KooKiz | 0 commentaire(s)

[WP7] Dynamically change startup page

Let’s say that you want to allow the user to customize the startup page of your application. You can easily change the startup page by editing the ‘NavigationPage’ attribute in the manifest file. But the manifest cannot be modified once the application has been published. How to define it at runtime?

For this, we’ll use a class very useful in ‘classical’ Silverlight, but widely forgotten on Windows Phone: UriMapper.

In this example, we create an application with three pages (Page1, Page2, and Page3), and we want the startup page to be picked randomly among those three pages.

First, change the manifest to use an inexistent dummy starting page:

   1: <DefaultTask Name="_default" NavigationPage="DummyPage.xaml" />

Now, create the UriMapper at the end of the Application constructor (in the App.xaml.cs file) and map the DummyPage to a random page. Then assign the UriMapper to the root frame:

   1: var mapper = new UriMapper();
   2:  
   3: int random = new Random().Next(0, 3);
   4:  
   5: mapper.UriMappings.Add(new UriMapping
   6: {
   7:     Uri = new Uri("/DummyPage.xaml", UriKind.Relative),
   8:     MappedUri = new Uri("/Page" + (random + 1) + ".xaml", UriKind.Relative)
   9: });
  10:  
  11: this.RootFrame.UriMapper = mapper;

And you’re done! Every time you start the application, one of the three pages will be picked at random.

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , , ,

[WP7] Inject a file in a xap using post-build event

Let’s say that your application is dynamically loading resources, and you have to constantly add/remove new resources during the development. In this scenario, it could be a huge-time saver to just tell Visual Studio to take the contents of a folder and inject it into your application. I don’t think there’s an out-of-the-box way to do that, but you can inject the files yourself using a post-build event.

How? The output of every WP7 project is a .xap file, which is just a zip file with a different extension. So you can edit it using whichever zip extractor you like. In our case, let’s use 7zip.

First, create a basic application, and put an image control in the xaml:

   1: <Grid x:Name="ContentPanel"
   2:       Grid.Row="1"
   3:       Margin="12,0,12,0">
   4:     <Image x:Name="Image"
   5:            Width="300"
   6:            Height="300"
   7:            Source="Someimage.png" />
   8: </Grid>

Make sure that the ‘someimage.png’ file does not exist in your project. Compile, run, and surely enough nothing is displayed.

Now, add the following line to the post-build event of your project: (right-click on the project, properties, “Build Events”)

   1: "C:\Program Files (x86)\7-Zip\7z.exe" a -tzip $(ProjectDir)$(OutDir)Test.xap E:\someimage.png
  • "C:\Program Files (x86)\7-Zip\7z.exe" is self explanatory: this is the path where you installed 7zip


  • the “a” switch tells 7zip to add a file to the archive


  • the “-tzip” switch forces the archive format to zip


  • “$(ProjectDir)$(OutDir)” is automatically replaced by the output path of your project


  • “Test.xap” is the name of the generated xap. Change it with the name of the file generated by your project


  • “E:\someimage.png” is the path of the file(s) to inject

 

Rebuild the solution (to force Visual Studio to re-deploy the xap), then run the application, and your ‘someimage.png’ image should be displayed, even though it never had been added to the Visual Studio solution.

Hope that helps!

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , , , ,

[WP7] Cryptic error message in XAML

Unexpected NONE in parse rule ElementBody ::= ATTRIBUTE* ( PropertyElement | Content )* . ENDTAG..

Best. Error message. Ever. Thanks Silverlight.

So, what’s going on?

For the sake of all the devs who’ll come to this page using Google, let’s describe one of the causes of the error message.

   1: <shell:ApplicationBar.MenuItems />

Is that it?

Yes, pretty much. I don’t know if it’s the only possible cause, but it seems that this error message is triggered when an empty element is used in the XAML for a collection. In this case it’s the MenuItems of an ApplicationBar, but you can also have the problem with an empty row or column definition of a grid:

   1: <Grid.ColumnDefinitions />

How to solve it? Either remove the element, if you can, or replace it by an opening tag and the corresponding closing tag:

   1: <Grid.ColumnDefinitions></Grid.ColumnDefinitions>

I’m not sure why the compiler chokes on this, but it’s quite a cryptic message for a simple problem.

Posté le par KooKiz | 0 commentaire(s)

[WP7] How to press the mouse on a control, and detect MouseLeftButtonUp on another

Another issue I found on StackOverflow, which is way more tricky that it seems.

Let’s say we have a Silverlight WP7 application, and we want to add a drag&drop scenario. The user first taps an element, drags his finger to another, and raises his finger on another element. Easy enough! Just handle the MouseLeftButtonDown on each element, store which element triggered the event in a property, handle the MouseLeftButtonUp on each element, and then we have the originator and the destination!

… right?

Well, so I would have thought.

Unfortunately, the MouseLeftButtonUp event will only be triggered if the ‘mouse’ left button (or your finger, sir Claus Jørgensen ;o) )1 is released on the very same control that it was pressed on.

So, if we use this XAML:

   1: <Grid x:Name="ContentPanel"
   2:       Grid.Row="1"
   3:       Margin="12,0,12,0"
   4:       MouseLeftButtonUp="ContentPanel_MouseLeftButtonUp">
   5:     <Grid.RowDefinitions>
   6:         <RowDefinition />
   7:         <RowDefinition />
   8:     </Grid.RowDefinitions>
   9:     <Grid x:Name="g1"
  10:           Background="Green"
  11:           MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  12:           MouseLeftButtonUp="Grid_MouseLeftButtonUp"
  13:           Tag="dragdrop" />
  14:     <Grid x:Name="g2"
  15:           Grid.Row="1"
  16:           Background="Blue"
  17:           MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  18:           MouseLeftButtonUp="Grid_MouseLeftButtonUp"
  19:           Tag="dragdrop" />
  20:  
  21: </Grid>

The layout looks like:

image

In the MouseLeftButtonUp event handler, we want to paint the destination grid:

   1: private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   2: {
   3:     var grid = (Grid)sender;
   4:     
   5:     grid.Background = new SolidColorBrush(Colors.Red);
   6: }

Unfortunately, it doesn’t work. Press your finger on the upper green grid, MouseLeftButtonDown is triggered. Drag your finger to the lower blue greed, lift your finger, MouseLeftButtonUp isn’t triggered.

I already encountered a similar issue with Silverlight a few years ago, so I knew about the CaptureMouse method. What is it? Basically it tells a control to keep track of the mouse even if events are triggered outside of the control bounds. Let’s try to use it.

In the MouseLeftButtonDown, simply capture the mouse:

   1: private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
   2: {
   3:     ((UIElement)sender).CaptureMouse();
   4: }

Now the MouseLeftButtonUp event is triggered! Unfortunately, it’s triggered on the control we called CaptureMouse on, so we still don’t know on which control the finger was when released. But we have the mouse coordinates, so we should be able to find it somehow.

And that ‘somehow’ is the ‘VisualTreeHelper.FindElementsInHostCoordinates’ method. It takes a point and a control, and enumerates all the control’s child that are located at the specified coordinates. Sounds good enough.

So now let’s rewrite our MouseLeftButtonUp event handler, without forgetting to release the mouse capture:

   1: private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   2: {
   3:     var grid = (Grid)sender;
   4:     
   5:     grid.ReleaseMouseCapture();
   6:  
   7:     var mouseUpGrid = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this), this.ContentPanel)
   8:         .OfType<Grid>()
   9:         .FirstOrDefault();
  10:  
  11:     if (mouseUpGrid != null)
  12:     {
  13:         Debug.WriteLine("MouseUp in " + mouseUpGrid.Name);
  14:         mouseUpGrid.Background = new SolidColorBrush(Colors.Red);
  15:     }
  16: }

Test on the emulator, and… it works!

Well, sure it does, but now let’s imagine a more complex scenario:

   1: <Grid x:Name="ContentPanel"
   2:       Grid.Row="1"
   3:       Margin="12,0,12,0">
   4:     <Grid.RowDefinitions>
   5:         <RowDefinition />
   6:         <RowDefinition />
   7:     </Grid.RowDefinitions>
   8:     <Grid x:Name="g1"
   9:           Background="Green"
  10:           MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  11:           MouseLeftButtonUp="Grid_MouseLeftButtonUp" />
  12:     <Grid x:Name="DummyGrid"
  13:           Grid.Row="1"
  14:           Background="Gray">
  15:         <Grid x:Name="g2"
  16:               Margin="100 5 5 5"
  17:               Background="Blue"
  18:               MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  19:               MouseLeftButtonUp="Grid_MouseLeftButtonUp" />
  20:     </Grid>
  21: </Grid>

The layout looks like:

image

We only want to be able to drag from the green grid to the blue one (and the other way around). Unfortunately, using the previous code, the drag&drop will be detected even if we lift our finger on the gray grid. So we need a way to opt-in to the drag&drop detection, rather than detect it on all the grids.

The dirty way would be to store the name of the blue and green grids, and check the name in the MouseLeftButtonDown event. But we can make something more generic using the Tag property.

What is Tag? It’s an object property available on every control. What is stored in this property? Nothing. It’s meant to be used by you, and only by you, to store whichever object you want to personalize the control.

In our XAML, let’s add the “dragdrop” string in the Tag of the green and blue grids:

   1: <Grid x:Name="ContentPanel"
   2:       Grid.Row="1"
   3:       Margin="12,0,12,0">
   4:     <Grid.RowDefinitions>
   5:         <RowDefinition />
   6:         <RowDefinition />
   7:     </Grid.RowDefinitions>
   8:     <Grid x:Name="g1"
   9:           Background="Green"
  10:           MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  11:           MouseLeftButtonUp="Grid_MouseLeftButtonUp"
  12:           Tag="dragdrop" />
  13:     <Grid x:Name="DummyGrid"
  14:           Grid.Row="1"
  15:           Background="Gray">
  16:         <Grid x:Name="g2"
  17:               Margin="100 5 5 5"
  18:               Background="Blue"
  19:               MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  20:               MouseLeftButtonUp="Grid_MouseLeftButtonUp"
  21:               Tag="dragdrop" />
  22:     </Grid>
  23: </Grid>

Then, in the MouseLeftButtonUp event handler, it’s only a matter of filtering which controls have the appropriate tag:

   1: private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   2: {
   3:     var grid = (Grid)sender;
   4:     
   5:     grid.ReleaseMouseCapture();
   6:  
   7:     var mouseUpGrid = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this), this.ContentPanel)
   8:         .OfType<Grid>()
   9:         .FirstOrDefault(element => element.Tag is string && (string)element.Tag == "dragdrop");
  10:  
  11:     if (mouseUpGrid != null)
  12:     {
  13:         Debug.WriteLine("MouseUp in " + mouseUpGrid.Name);
  14:         mouseUpGrid.Background = new SolidColorBrush(Colors.Red);
  15:     }
  16: }

Now the gray grid is excluded, as expected.

And that’s how a seemingly easy problem turns out on a solution requiring the use of Tag, VisualTreeHelper.FindElementsInHostCoordinates, and CaptureMouse. Quite instructive if you ask me.

 

 

 


1: Ok, now I’m putting private jokes on my blog articles. We’re doomed.

Posté le par KooKiz | 0 commentaire(s)

[WP7] ApplicationBar flickering when the phone theme is white

It’s an interesting issue I found on StackOverflow. Interesting because I often forget to test my applications using the Windows Phone’s white theme, so this kind of problem usually goes unnoticed until a user reports it.

Create a simple WP7 application with a black background and an ApplicationBar. Add a button to toggle the bar’s visibility.

The XAML should look like:

   1: <phone:PhoneApplicationPage x:Class="WP7ForumTest.MainPage"
   2:                             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:                             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:                             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:                             xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   7:                             xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   8:                             d:DesignHeight="696"
   9:                             d:DesignWidth="480"
  10:                             FontFamily="{StaticResource PhoneFontFamilyNormal}"
  11:                             FontSize="{StaticResource PhoneFontSizeNormal}"
  12:                             Foreground="{StaticResource PhoneForegroundBrush}"
  13:                             Orientation="Portrait"
  14:                             shell:SystemTray.IsVisible="True"
  15:                             SupportedOrientations="Portrait"
  16:                             mc:Ignorable="d">
  17:     <Grid Background="Black">
  18:         <Button VerticalAlignment="Top"
  19:                 Background="Black"
  20:                 BorderBrush="White"
  21:                 Click="ButtonClick"
  22:                 Content="Toggle Application Bar"
  23:                 Foreground="White" />
  24:     </Grid>
  25:     <phone:PhoneApplicationPage.ApplicationBar>
  26:         <shell:ApplicationBar BackgroundColor="Black"
  27:                               ForegroundColor="White">
  28:             <shell:ApplicationBarIconButton IconUri="/icon.png" Text="Menu" />
  29:         </shell:ApplicationBar>
  30:     </phone:PhoneApplicationPage.ApplicationBar>
  31: </phone:PhoneApplicationPage>
And the ButtonClick method:
   1: private void ButtonClick(object sender, RoutedEventArgs e)
   2: {
   3:     ApplicationBar.IsVisible = !ApplicationBar.IsVisible;
   4: }

Now launch the WP7 emulator, go in the settings, and set the phone theme to ‘light’. Then start the application, and try pressing the button: the application bar disappears as expected, but you may notice a quick white flickering. The same occurs when showing back the bar.

Of course, the problem is also reproducible with a white background and the ‘dark’ phone theme, only slightly less noticeable.

So, what’s happening? Unfortunately, the ApplicationBar control is unmanaged, I can’t dig in it using Reflector. Therefore, I can only make an hypothesis: the background part under the application bar isn’t painted when the bar is visible. When the hiding animation starts, the bar seems to move but the control still occupies the same space, and the background still isn’t painted. Thus it is shown with the phone’s default color instead of the grid’s background. When the animation is over, the ApplicationBar sets its own visibility to ‘collapsed’. The runtime now knows that it have to draw the background, and the white artifact disappears.

How to fix it? We have to find a way to force WP7 to paint the grid’s background under the ApplicationBar. For this, we have just the property we need: Opacity. Just set the opacity of the bar to 0.99: the value is so high that the transparency effect will be invisible, but the runtime will have to draw the background.

   1: <phone:PhoneApplicationPage.ApplicationBar>
   2:     <shell:ApplicationBar BackgroundColor="Black" 
   3:                           ForegroundColor="White"
   4:                           Opacity=".99">
   5:         <shell:ApplicationBarIconButton IconUri="/icon.png" Text="Button 1" />
   6:     </shell:ApplicationBar>
   7: </phone:PhoneApplicationPage.ApplicationBar>

Compile, run, and the flickering effect should be gone.

Posté le par KooKiz | 0 commentaire(s)

[C#/WPF] BindingList

On va s’écarter un peu de WP7 pour un article qui s’inscrit dans la lignée du “Après 8 ans de .NET, j’en découvre encore”.

Au programme, un problème assez frustrant que j’ai pu rencontrer à plusieurs reprise, dans des applications WPF. Commençons par poser les bases : nous avons une liste d’éléments, avec une propriété numérique (ici, “Count”). Nous voulons afficher ces éléments ainsi que la somme de leur “Count”, et permettre à l’utilisateur de les modifier individuellement (ce qui doit bien entendu mettre à jour la somme).

Pas de grande surprise au niveau du XAML (notez toutefois l’utilisation du fort pratique StringFormat pour le binding) :

   1: <Window x:Class="WpfApplication1.MainWindow"
   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         Title="MainWindow" Height="900" Width="500">
   5:     <StackPanel>
   6:         <TextBlock Text="{Binding Path=Total, StringFormat=Total: \{0\}}" />
   7:         
   8:         <ItemsControl ItemsSource="{Binding Path=Items}">
   9:             <ItemsControl.ItemTemplate>
  10:                 <DataTemplate>
  11:                     <TextBox Text="{Binding Path=Count}" />
  12:                 </DataTemplate>
  13:             </ItemsControl.ItemTemplate>
  14:         </ItemsControl>
  15:     </StackPanel>
  16: </Window>

Et la déclaration de la classe “Item” :

   1: public class Item : INotifyPropertyChanged
   2: {
   3:     private int count;
   4:  
   5:     public event PropertyChangedEventHandler PropertyChanged;
   6:  
   7:     public int Count
   8:     {
   9:         get
  10:         {
  11:             return this.count;
  12:         }
  13:  
  14:         set
  15:         {
  16:             this.count = value;
  17:             this.NotifyPropertyChanged("Count");
  18:         }
  19:     }
  20:  
  21:     protected void NotifyPropertyChanged(string propertyName)
  22:     {
  23:         var eventHandler = this.PropertyChanged;
  24:  
  25:         if (eventHandler != null)
  26:         {
  27:             eventHandler(this, new PropertyChangedEventArgs(propertyName));
  28:         }
  29:     }
  30: }

Dans le ViewModel, nous stockons les objets dans une ObservableCollection, afin que la vue soit automatiquement notifiée quand un item est ajouté ou retiré :

   1: public class ViewModel : INotifyPropertyChanged
   2: {
   3:     public ViewModel()
   4:     {
   5:         this.Items = new ObservableCollection<Item>();
   6:  
   7:         this.Items.Add(new Item { Count = 1 });
   8:         this.Items.Add(new Item { Count = 2 });
   9:         this.Items.Add(new Item { Count = 3 });
  10:         this.Items.Add(new Item { Count = 4 });
  11:  
  12:         this.ComputeSum();
  13:     }
  14:  
  15:     public event PropertyChangedEventHandler PropertyChanged;
  16:  
  17:     public ObservableCollection<Item> Items { get; set; }
  18:  
  19:     public int Total { get; protected set; }
  20:  
  21:     protected void ComputeSum()
  22:     {
  23:         this.Total = this.Items.Sum(i => i.Count);
  24:         this.NotifyPropertyChanged("Total");
  25:     }
  26:  
  27:     protected void NotifyPropertyChanged(string propertyName)
  28:     {
  29:         var eventHandler = this.PropertyChanged;
  30:  
  31:         if (eventHandler != null)
  32:         {
  33:             eventHandler(this, new PropertyChangedEventArgs(propertyName));
  34:         }
  35:     }
  36: }

A l’exécution, nous avons bien notre liste d’items qui s’affiche, ainsi que le total. Maintenant, comment faire pour qu’il se mette à jour ?

C’est là que la frustration commence. En effet, ObservableCollection permet d’être notifié lorsqu’un élément est ajouté ou supprimé, mais pas lorsqu’une des propriétés des éléments est modifiée, même si ceux-ci implémentent INotifyPropertChanged. Damned, comment ont-ils pu oublier un besoin aussi élémentaire ?

Du coup, comment faire ? Soit créer une nouvelle collection héritée d’ObservableCollection pour combler ce manque, soit faire toute la tambouille d’abonnement aux évènements “PropertyChanged” directement dans le ViewModel. Dans ce cas-ci, par souci de simplicité, optons pour la seconde solution :

   1: public class ViewModel : INotifyPropertyChanged
   2: {
   3:     public ViewModel()
   4:     {
   5:         this.Items = new ObservableCollection<Item>();
   6:  
   7:         this.Items.CollectionChanged += this.Items_CollectionChanged;
   8:  
   9:         this.Items.Add(new Item { Count = 1 });
  10:         this.Items.Add(new Item { Count = 2 });
  11:         this.Items.Add(new Item { Count = 3 });
  12:         this.Items.Add(new Item { Count = 4 });
  13:  
  14:         this.ComputeSum();
  15:     }
  16:  
  17:     public event PropertyChangedEventHandler PropertyChanged;
  18:  
  19:     public ObservableCollection<Item> Items { get; set; }
  20:  
  21:     public int Total { get; protected set; }
  22:  
  23:     protected void ComputeSum()
  24:     {
  25:         this.Total = this.Items.Sum(i => i.Count);
  26:         this.NotifyPropertyChanged("Total");
  27:     }
  28:  
  29:     protected void NotifyPropertyChanged(string propertyName)
  30:     {
  31:         var eventHandler = this.PropertyChanged;
  32:  
  33:         if (eventHandler != null)
  34:         {
  35:             eventHandler(this, new PropertyChangedEventArgs(propertyName));
  36:         }
  37:     }
  38:  
  39:     private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  40:     {
  41:         if (e.OldItems != null)
  42:         {
  43:             foreach (INotifyPropertyChanged oldItem in e.OldItems)
  44:             {
  45:                 oldItem.PropertyChanged -= this.ItemPropertyChanged;
  46:             }
  47:         }
  48:  
  49:         if (e.NewItems != null)
  50:         {
  51:             foreach (INotifyPropertyChanged newItem in e.NewItems)
  52:             {
  53:                 newItem.PropertyChanged += this.ItemPropertyChanged;
  54:             }
  55:         }
  56:  
  57:         this.ComputeSum();
  58:     }    
  59:  
  60:     private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
  61:     {
  62:         if (e.PropertyName == "Count")
  63:         {
  64:             this.ComputeSum();
  65:         }
  66:     }
  67: }

Ca marche, mais laisse quand même sur sa faim : ce code manque clairement d’élégance.

Mais pas plus tard qu’aujourd’hui, je suis tombé, complètement par hasard, sur une discussion sur StackOverflow décrivant la classe “BindingList”. Il s’agit d’une ObservableCollection en plus puissant, puisqu’elle permet entre autres d’être notifié quand une propriété d’un des éléments est modifiée !

Adaptons donc notre code pour l’utiliser :

   1: public class ViewModel : INotifyPropertyChanged
   2: {
   3:     public ViewModel()
   4:     {
   5:         this.Items = new BindingList<Item>();
   6:             
   7:         this.Items.Add(new Item { Count = 1 });
   8:         this.Items.Add(new Item { Count = 2 });
   9:         this.Items.Add(new Item { Count = 3 });
  10:         this.Items.Add(new Item { Count = 4 });
  11:  
  12:         this.ComputeSum();
  13:  
  14:         this.Items.ListChanged += this.Items_ListChanged;
  15:     }
  16:  
  17:     public event PropertyChangedEventHandler PropertyChanged;
  18:  
  19:     public BindingList<Item> Items { get; set; }
  20:  
  21:     public int Total { get; protected set; }
  22:  
  23:     protected void ComputeSum()
  24:     {
  25:         this.Total = this.Items.Sum(i => i.Count);
  26:         this.NotifyPropertyChanged("Total");
  27:     }
  28:  
  29:     protected void NotifyPropertyChanged(string propertyName)
  30:     {
  31:         var eventHandler = this.PropertyChanged;
  32:  
  33:         if (eventHandler != null)
  34:         {
  35:             eventHandler(this, new PropertyChangedEventArgs(propertyName));
  36:         }
  37:     }
  38:  
  39:     private void Items_ListChanged(object sender, ListChangedEventArgs e)
  40:     {
  41:         if (e.ListChangedType != ListChangedType.ItemChanged
  42:             || (e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor.Name == "Count"))
  43:         {
  44:             this.ComputeSum();
  45:         }
  46:     }
  47: }

Ca marche, et c’est autrement plus classe ;o)

Après vérification, cette classe existe depuis .NET 2.0, rien de neuf donc… Et pourtant !

Posté le par KooKiz | 1 commentaire(s)
Classé sous : , ,

[WP7] Upload a file to Dropbox using ReactiveOAuth.WP7

As I was helping @lancewmccarthy on its last application, I ran across the need to upload a file to Dropbox. While there is already a few libraries for WP7 handling this task, I couldn’t get any of them to work as expected: either they missed the file upload feature, or they used an older version of the API that new applications can’t use anymore. As such, I had to dive into the Dropbox’s OAuth API to write my own upload code. It was far from being painless, so I think the whole process is worth sharing.

First, what is OAuth? I won’t give much details as I discovered that recently, but basically it’s a protocol used to authenticate yourself and send orders to an http-based API. On the surface, it’s just about calling the appropriate webpage. But to prevent some kinds of attacks, OAuth forces you to add a few parameters to the uri, like a random value and a dynamically generated signature. It may be just me, but I think the official documentation isn’t precise enough on how to generate the signature, so I quickly gave up and searched for general-purpose OAuth libraries. The one I found and used is ReactiveOAuth.WP7. It was originally designed for Twitter, but it can also be used for Dropbox, with a few changes.

The documentation for Dropbox REST API can be found here. To use it, you must first register your application on the website, to get a unique code that you will use every time you call a Dropbox function. The application registration is done on this page and is almost instant. Note that your application is first registered with ‘testing’ status. It means you can only use your own Dropbox account with it. To remove this limitation, you have to apply for production status, which requires a validation from Dropbox administrators. You just have to fill a description for your app and explain why you need to use Dropbox, then you should receive a confirmation e-mail after a few hours. Anyway, on the application page you’ll find two codes: “App key” and “App secret”. We need these to use the API.

image

Now, let’s start Visual Studio and create a new WP7 project. Add the ReactiveOAuth.WP7 project to your solution. The whole project, not just the binaries, as we’ll have to make a few changes in the code.

In your main project, add a reference to the ReactiveOAuth project. Also add a reference to the Microsoft.Phone.Reactive and System.Observable assemblies.

image

The OAuth authentication and authorization process works as follows: first call the “request_token” page, giving your app key and secret (the two values that were provided on the Dropbox application page). The server answers with a token which will identify your session. Then, the user must allow your application to use his Dropbox account. For security reasons, you cannot automate this part, it requires user interaction. You’ll have to redirect the user to a specific webpage, where he can type his credentials and explicitly allow your application. If the user do theses steps correctly, then a call to the “access_token” page will return a token that you can use to call any API function you want.

For our tests, we need to upload something to Dropbox. For that purpose, we’ll create a test file when the page is loaded, and store it in the isolated storage. Therefore, we create a “CreateDummyFile” function and call it in the “OnNavigatedTo” event:

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   4:  
   5:     this.CreateDummyFile();
   6: }
   7:  
   8: private void CreateDummyFile()
   9: {
  10:     using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
  11:     {
  12:         if (isolatedStorage.FileExists("test.txt"))
  13:         {
  14:             isolatedStorage.DeleteFile("test.txt");
  15:         }
  16:  
  17:         using (var stream = isolatedStorage.CreateFile("test.txt"))
  18:         {
  19:             var data = Encoding.UTF8.GetBytes("Hello world! - " + DateTime.Now.ToLongTimeString());
  20:  
  21:             stream.Write(data, 0, data.Length);
  22:         }
  23:     }
  24: }

Then we use the “OAuthAutorizer” class to request a token. “Key” and “Secret” are constants added to the page, containing respectively the app key and app secret.

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   4:  
   5:     var authorizer = new OAuthAuthorizer(Key, Secret);
   6:  
   7:     authorizer.GetRequestToken("https://api.dropbox.com/1/oauth/request_token");
   8: }

Once we have the token, we must proceed with the authorization. We need to display a webpage to the user, so we add a few UI elements in the XAML:

   1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   2:     <StackPanel>
   3:         <phone:WebBrowser x:Name="WebBrowser" Height="700"  IsScriptEnabled="True"
   4:                       Visibility="Collapsed" 
   5:                           Navigating="WebBrowser_Navigating" />
   6:         <StackPanel x:Name="UploadingPanel">
   7:             <TextBlock Text="Uploading file..." Height="50" TextAlignment="Center" />
   8:             <ProgressBar x:Name="ProgressBar" Height="20" IsIndeterminate="True" Visibility="Visible"/>
   9:         </StackPanel>
  10:     </StackPanel>
  11: </Grid>

In the code, we use the callback of the “GetRequestToken” method to construct the right uri and navigate to this page. The “authorize” function accepts a “oauth_callback” parameter. This parameter tells Dropbox to redirect to a specific page when the authorization process is done. This way we can detect the redirection to know when the user has finished. So our “OnNavigatedTo” event now looks like:

   1: protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
   2: {
   3:     base.OnNavigatedTo(e);
   4:  
   5:     this.CreateDummyFile();
   6:  
   7:     var authorizer = new OAuthAuthorizer(Key, Secret);
   8:  
   9:     authorizer.GetRequestToken("https://api.dropbox.com/1/oauth/request_token")
  10:         .ObserveOnDispatcher()
  11:         .Subscribe(token =>
  12:         {
  13:             this.RequestToken = token.Token;
  14:             var url = authorizer.BuildAuthorizeUrl("https://www.dropbox.com/1/oauth/authorize", token.Token);
  15:  
  16:             url += "&oauth_callback=http://dummywebsite/dummy";
  17:  
  18:             this.WebBrowser.Navigate(new Uri(url));
  19:         });
  20: }

Note the call to “ObserveOnDispatcher”. It tells the Reactive extensions to call the callback on the dispatcher thread. It’s required as we need to interact with the UI, to tell the WebBrowser control which page it should display. Without “ObserveOnDispatcher”, we would have to call “Dispatcher.BeginInvoke” to avoid a cross-thread exception.

In the XAML code I’ve provided, the WebBrowser is hidden by default. Now we need to set it to visible, and detect the end of the authorization process to hide it back and request the access token. To do that, we’re going to use the “Navigating” event of the WebBrowser control:

   1: private void WebBrowser_Navigating(object sender, NavigatingEventArgs e)
   2: {
   3:     if (e.Uri.AbsolutePath == "/dummy")
   4:     {
   5:         // Authorization done, cancel the navigation, hide the browser control, and proceed.
   6:         e.Cancel = true;
   7:         this.WebBrowser.Visibility = Visibility.Collapsed;
   8:         this.UploadingPanel.Visibility = Visibility.Visible;
   9:         this.GetAccessToken();
  10:     }
  11:     else
  12:     {
  13:         this.WebBrowser.Visibility = Visibility.Visible;
  14:         this.UploadingPanel.Visibility = Visibility.Collapsed;
  15:     }
  16: }

The “GetAccessToken” method simply request the access token, store it in a property, and call the file uploading code:

   1: private void GetAccessToken()
   2: {
   3:     var authorizer = new OAuthAuthorizer(Key, Secret);
   4:     authorizer.GetAccessToken("https://api.dropbox.com/1/oauth/access_token", this.RequestToken, this.RequestToken.Secret)
   5:         .Subscribe(token =>
   6:         {
   7:             this.AccessToken = token.Token;
   8:  
   9:             this.SendFile();
  10:         });
  11: }

Now comes the tricky part. Out of the box, the ReactiveOAuth library is limited to HTTP GET and POST requests. To upload the file, we’ll need to use HTTP PUT. Therefore, there’s two modifications to do in the library.

First, open the “MethodType.cs” file, and add the PUT method:

   1: using System;
   2:  
   3: namespace Codeplex.OAuth
   4: {
   5:     /// <summary>WebRequest HttpMethodType</summary>
   6:     public enum MethodType
   7:     {
   8:         Get, Post, Put
   9:     }
  10:  
  11:     public static class MethodTypeExtensions
  12:     {
  13:         /// <summary>convert to UPPERCASE string</summary>
  14:         public static string ToUpperString(this MethodType methodType)
  15:         {
  16:             switch (methodType)
  17:             {
  18:                 case MethodType.Get:
  19:                     return "GET";
  20:                 case MethodType.Post:
  21:                     return "POST";
  22:                 case MethodType.Put:
  23:                     return "PUT";
  24:                 default:
  25:                     throw new ArgumentException();
  26:             }
  27:         }
  28:     }
  29: }

Then open the “OAuthClient.cs” file and find the “CreateWebRequest” method. Set its visibility to “Public”, and change the first line from:

   1: var requestUrl = (MethodType == OAuth.MethodType.Get) ? Url + "?" + Parameters.ToQueryParameter() : Url;

to:

   1: var requestUrl = (MethodType == OAuth.MethodType.Get || MethodType == OAuth.MethodType.Put) ? Url + "?" + Parameters.ToQueryParameter() : Url;

What are we doing here? The “files_put” API of Dropbox requires parameters to be provided as for a GET request, and the file data to be provided as POST data. ReactiveOAuth.WP7 doesn’t seem to be able to handle this hybrid query, so we’re bypassing it and exposing the internal WebRequest object to do the upload ourselves. Basically, we only use ReactiveOAuth to create the request and fill all the parameters required by the OAuth protocol, like the signature, then we take car of the uploading.

Now we can write our “SendFile” method, with the necessary upload code:

   1: private void SendFile()
   2: {
   3:     var client = new OAuthClient(Key, Secret, this.AccessToken);
   4:  
   5:     client.Url = "https://api-content.dropbox.com/1/files_put/sandbox/test.txt";
   6:  
   7:     client.Parameters.Add("overwrite", "true");
   8:  
   9:     client.MethodType = MethodType.Put;
  10:  
  11:     var webRequest = client.CreateWebRequest();
  12:  
  13:     webRequest.BeginGetRequestStream(this.StartUpload, webRequest);
  14: }
  15:  
  16: private void StartUpload(IAsyncResult asyncResult)
  17: {
  18:     var request = (HttpWebRequest)asyncResult.AsyncState;
  19:     var postStream = request.EndGetRequestStream(asyncResult);
  20:  
  21:     using (var isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
  22:     {
  23:         using (var stream = isolatedStorage.OpenFile("test.txt", FileMode.Open))
  24:         {
  25:             stream.CopyTo(postStream);
  26:  
  27:             postStream.Close();
  28:         }
  29:     }
  30:  
  31:     request.BeginGetResponse(this.EndUpload, request);
  32: }
  33:  
  34: private void EndUpload(IAsyncResult asyncResult)
  35: {
  36:     var request = (HttpWebRequest)asyncResult.AsyncState;
  37:  
  38:     try
  39:     {
  40:         var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
  41:  
  42:         response.Dispose();
  43:  
  44:         this.Dispatcher.BeginInvoke(() =>
  45:         {
  46:             this.ProgressBar.Visibility = Visibility.Collapsed;
  47:             MessageBox.Show("Your file has been sucessfully uploaded to Dropbox!");
  48:         });
  49:     }
  50:     catch (Exception ex)
  51:     {
  52:         this.Dispatcher.BeginInvoke(() => MessageBox.Show("An error occured: " + ex.Message));
  53:     }
  54: }

The “StartUpload” method simply opens the file from the isolated storage, then copies the contents inside the web request. The “EndUpload” callback is called when the upload is finished. There, we display a confirmation message.

And we’re done! After executing this program, the Dropbox webpage should appear, asking for your credentials. Then, when you’re done allowing the app, the upload will begin and a message box will confirm you that the file has been uploaded.

image

Note: my code modification inside the ReactiveOAuth.WP7 library is really ugly, as I don’t even bother to use Reactive extensions. That’s because I didn’t really need Reactive extensions in first place, and any ‘general purpose’ OAuth library would do the trick. Still, if you know an elegant way to use Dropbox’s ‘files_put’ function with ReactiveOAuth without modifying the library, please share it in the comments!

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , , ,

[WP7] ScheduledActionService.Replace: Bug or documentation error?

Let’s consider this simple code:

   1: var beginDate = new DateTime(2011, 12, 01);
   2: var endDate = new DateTime(2011, 12, 02);
   3:  
   4: var reminder = new Reminder("Test");
   5: reminder.BeginTime = beginDate;
   6: reminder.ExpirationTime = endDate;
   7:  
   8: ScheduledActionService.Add(reminder);

With this, a reminder is created and scheduled. Each reminder is associated with an unique name, provided in the constructor. Here, we use “Test” as name.

Now, what if I want to update my Reminder? If I create a new Reminder with the same name, and call ScheduledActionService.Add, an exception is thrown: ‘Test’ already exists. It makes sense, since the name is supposed to be an unique identifier. So to update the Reminder, you have first to call the ScheduledActionService.Remove method, then you can call the ‘Add’ method:

   1:  
   2:             var beginDate = new DateTime(2011, 12, 01);
   3:             var endDate = new DateTime(2011, 12, 02);
   4:  
   5:             var reminder = new Reminder("Test");
   6:             reminder.BeginTime = beginDate;
   7:             reminder.ExpirationTime = endDate;
   8:             
   9:             if (ScheduledActionService.Find("Test") != null)
  10:             {
  11:                 ScheduledActionService.Remove("Test");
  12:             }
  13:  
  14:             ScheduledActionService.Add(reminder);

No problem here, it works perfectly. Is there a better way to do it? A look at MSDN documentation shows the ScheduledActionService.Replace method:

“Replaces an existing ScheduledAction with the one provided. The Name property uniquely identifies ScheduledAction objects and is used to determine which existing object, if any, is replaced.”

It sounds exactly like what we need! Let’s try to use it:

   1: var beginDate = new DateTime(2011, 12, 01);
   2: var endDate = new DateTime(2011, 12, 02);
   3:  
   4: var reminder = new Reminder("Test");
   5: reminder.BeginTime = beginDate;
   6: reminder.ExpirationTime = endDate;
   7:  
   8:  
   9: ScheduledActionService.Replace(reminder);

Compile, run, and… An exception is thrown! “BNS Error: The item doesn't exist”

Why this exception? My Reminder was created previously, with the same name… So it should exist, right?

Let’s investigate. What happens if we retrieve the old Reminder, update it, then call replace with this very same Reminder instance?

   1: var beginDate = new DateTime(2011, 12, 01);
   2: var endDate = new DateTime(2011, 12, 02);
   3:  
   4: var reminder = ScheduledActionService.Find("Test");
   5: reminder.BeginTime = beginDate.AddDays(1);
   6: reminder.ExpirationTime = endDate.AddDays(1);
   7:  
   8: ScheduledActionService.Replace(reminder);

It works! So basically, the ScheduledActionService.Replace method can only be used if we provide the same Reminder instance that was added previously. This clearly contradicts the MSDN documentation, which states that the method replaces the previous Reminder only based on the Name property.

By the way, if it’s not the name, what is used to recognize the Reminder? Let’s dig in using Reflector.

First, the Replace method:

   1: public static void Replace(ScheduledAction action)
   2: {
   3:     lock (m_syncRoot)
   4:     {
   5:         if (!(action is ScheduledNotification))
   6:         {
   7:             throw new NotSupportedException(string.Format("{0} is not supported", "Replacing a ScheduledTask"));
   8:         }
   9:         if (!ActionDictionary.ContainsKey(action.Name))
  10:         {
  11:             throw new InvalidOperationException(string.Format("'{0}' doesn't exist", action.Name));
  12:         }
  13:         SystemNotificationInterop.UpdateNotification(action);
  14:         ActionDictionary[action.Name] = action.CreateCopy();
  15:     }
  16: }
  17:  

It first checks if the Reminder is in the internal ‘ActionDictionary’ dictionary. Otherwise, it throws an exception. The exception message is different, so it’s not where it crashes. To make sure, let’s check the callstack when the exception is thrown:

Microsoft.Phone.dll!Microsoft.Phone.Scheduler.SystemNotificationInterop.CheckHr
Microsoft.Phone.dll!Microsoft.Phone.Scheduler.SystemNotificationInterop.UpdateNotification
Microsoft.Phone.dll!Microsoft.Phone.Scheduler.ScheduledActionService.Replace

The CheckHr is just a method which checks the result code and throws the appropriate exception:

   1: private static void CheckHr(int hr)
   2: {
   3:     switch (hr)
   4:     {
   5:         case -2147467263:
   6:             throw new NotSupportedException();
   7:  
   8:         case -2147024890:
   9:         case -2147024809:
  10:             throw new ArgumentException("E_INVALIDARG");
  11:  
  12:         case -2147024882:
  13:             throw new OutOfMemoryException();
  14:  
  15:         case 0:
  16:         case 1:
  17:             return;
  18:  
  19:         case -2130444030:
  20:             throw new SchedulerServiceException(hr, GetMessageStringFromHResult(hr));
  21:  
  22:         case -2147023174:
  23:         case -2147023173:
  24:             throw new SchedulerServiceException(hr, "System is not ready");
  25:     }
  26:     string message = null;
  27:     try
  28:     {
  29:         message = GetMessageStringFromHResult(hr);
  30:     }
  31:     catch (Exception)
  32:     {
  33:         message = null;
  34:     }
  35:     if (message == null)
  36:     {
  37:         throw new SchedulerServiceException(hr, hr.ToString("X"));
  38:     }
  39:     throw new InvalidOperationException(message);
  40: }
  41:  
  42:  

No intelligence here, it leaves us only the UpdateNotification method.

   1: internal static void UpdateNotification(ScheduledAction action)
   2: {
   3:     BNS_NOTIFICATION bnsNotification = CreateNativeNotificationFromManaged(action);
   4:     CheckHr(BNSIUpdateNotification(ref bnsNotification));
   5:     action.UpdateStatusFrom(CreateManagedNotificationFromNative(bnsNotification));
   6: }

Unfortunately, BNSIUpdateNotification is a native method, so we can’t decompile it using Reflector. The CreateNativeNotificationFromManaged, as it names implied, create a native notification object, which will be sent to the BNSIUpdateNotification method. I won’t copy the code because it’s too long, and it’s a pretty straightforward copy of properties from one object to another.

So… Is that a dead end? Maybe not, let’s see the other methods of the ScheduledActionService class. Especially the ‘Remove’ method:

   1: public static void Remove(string name)
   2: {
   3:     lock (m_syncRoot)
   4:     {
   5:         if (!ActionDictionary.ContainsKey(name))
   6:         {
   7:             throw new InvalidOperationException(string.Format("'{0}' doesn't exist", name));
   8:         }
   9:         SystemNotificationInterop.DeleteNotification(ActionDictionary{name}.ID);
  10:         ActionDictionary.Remove(name);
  11:     }
  12: } 

(note: due to a bug in the blog engine, I had to replace the [] around Name by {} in line 9. Apologies).

Now that’s interesting. The method retrieves the Reminder in the internal dictionary by using its name, then it sends its id to the ‘DeleteNotification’ method, which will in turn send it to the native BNSIDeleteNotification method. Could it be that’s the Id property is used internally to identify a Reminder, and not the Name property?

When creating a Reminder, we don’t specify the value of the Id property. First, let’s check how it’s provided. We have to go up the inheritance tree until the ‘ScheduledAction’ class to find it:

   1: internal ScheduledAction(string name, ScheduledActionType type)
   2: {
   3:     this.m_id = Guid.NewGuid();
   4:     this.m_content = string.Empty;
   5:     this.Name = name;
   6:     this.m_type = type;
   7:     this.m_status = ScheduledActionStatus.Completed;
   8:     this.m_endTime = DateTime.MaxValue;
   9: }
  10:  

It’s simply a random generated value. So if we create two Reminder, with the same name, they’ll have different id. Have we found our issue? Let’s try to figure by manually changing the Id property.

The Id property isn’t exposed anywhere, but fortunately we can modify it using Visual Studio’s debugger.

First, create a reminder named “Test”, add it to the ScheduledActionService, then execute again our crashing code:

   1: var beginDate = new DateTime(2011, 12, 01);
   2: var endDate = new DateTime(2011, 12, 02);
   3:  
   4: var reminder = new Reminder("Test");
   5: reminder.BeginTime = beginDate;
   6: reminder.ExpirationTime = endDate;
   7:  
   8: ScheduledActionService.Replace(reminder);

This time, set a breakpoint on the '”ScheduledActionService.Replace(reminder);” line. When Visual Studio steps into it, open the QuickWatch window (Shift + F9). Then, call the “ScheduledActionService.Find” method to retrieve the old Reminder, and get the value of its m_id property:

image

Now, replace the m_id property of the new Reminder with this one:

image

Now exit the QuickWatch window and press F10 to continue execution. And… It works! So the problem really is the Id property. The value isn’t based on the Name property, and is randomly generated when a new Reminder is created. So if we create a new Reminder instance, even if we use the same name, we’ll have a different Id. Then, when we call the Replace method, the Id property is used rather than the Name. So, of course, the old Reminder instance cannot be found, and an exception is thrown.

Now let’s just hope for a fix in a WP7 next release, or at the very least an update of the MSDN documentation.

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , , , ,

[WP7] Pensez à gérer la désactivation des background agent !

Une erreur que j’ai pu voir sur quelques applications publiées sur le marketplace. Il est possible pour l’utilisateur de désactiver manuellement un background agent créé par une application, en allant dans les paramètres du téléphone, –> applications –> tâches en arrière-plan. Si les background agent ont été désactivés pour votre application, alors une exception sera levée lorsque vous tenterez d’en créer :

System.InvalidOperationException: "BNS Error: The action is disabled"

Cela peut être particulièrement problématique si votre application créé un background agent au chargement, auquel cas le crash sera direct. Pour gérer ce cas, un simple bloc try/catch autour de l’appel à “ScheduledActionService.Add” fera l’affaire, avec éventuellement un message explicatif à destination de l’utilisateur.

Je trouve dommage que ce point ne soit pas vérifié dans le processus de certification pour le marketplace, vu comme l’erreur est simple à faire.

Posté le par KooKiz | 0 commentaire(s)
Classé sous : , , ,
Plus de Messages Page suivante »


Les 10 derniers blogs postés

- Compte rendu : SharePoint / O365 : des pratiques pour une meilleure productivité par The Mit's Blog le 12-12-2014, 18:11

- [TFS] Suppression des feature SQL Entreprise en masse par Blog de Jérémy Jeanson le 12-06-2014, 09:18

- [Clean Code] règles de nommage par Fathi Bellahcene le 12-04-2014, 22:59

- Windows To Go 10 et Upgrades impossibles par Blog de Jérémy Jeanson le 12-04-2014, 21:38

- SharePoint OnPremise: Statistiques d’utilisation pour traquer les sites fantomes par Blog Technique de Romelard Fabrice le 12-03-2014, 10:28

- SharePoint 2007: Script PowerShell permettant le backup de toutes les collections de sites d’une application Web par Blog Technique de Romelard Fabrice le 12-02-2014, 10:00

- Xamarin : un choix précieux par .net is good... C# is better ;) le 12-01-2014, 15:10

- Office 365: Comparaison des composants pour préparer votre migration de SharePoint 2007 vers Office 365 par Blog Technique de Romelard Fabrice le 11-28-2014, 16:20

- Créer un périphérique Windows To Go 10 ! par Blog de Jérémy Jeanson le 11-21-2014, 04:54

- RDV à Genève le 12 décembre pour l’évènement “SharePoint–Office 365 : des pratiques pour une meilleure productivité !” par Le blog de Patrick [MVP Office 365] le 11-19-2014, 10:40