Tag: Windows Phone 7

The Starbucks Wi-Fi Login Scenario: Why does this crash?

University of Arizona Starbucks

Developer Scenario: You have written your app to make sure there is a network connection prior to downloading data and populating the app. You have thoroughly tested you app at work with wired internet, in your car with 2, 3, 4 and no G (no connection) cellular connections. You also diligently tested your app with your home Wi-Fi and it works GREAT! You publish your app to the marketplace and find you are getting a few bad reviews (app is crashing). In reviewing your analytics, you see that these crashes are occurring when connected to Wi-Fi. You take your app to your favorite internet café and notice that the Windows Phone navigates to the web browser after connecting to the Wi-Fi access point. Wondering what will happen if you do NOT agree to the ‘terms-of-service’ you fire up your app and BOOM it crashes! 🙁 You start your debugger and find that your app is in fact downloading data, but instead of being in XML format:

<?xml version="1.0" encoding="utf-8"?>
<breakfast_menu>
  <food>
    <name>Belgian Waffles</name>
    <price>$5.95</price>
  </food>
</breakfast_menu>

its in the form of:

<!DOCTYPE html>
<html class=" " lang="en-US">
  <head>
    <title>Starbucks Coffee Company</title>
. . .
Your app proceeds to deserialize the incoming data and quickly crashes since it is not in the expected format.

Detecting and Mitigating this scenario

The best way to protect against this scenario, is to validate the data. You don’t need to validate the entire response stream, just enough to know that its XML (or JSON, or whatever) and NOT HTML. You can the very quickly check a very small portion of the returned data to determine if the data you are receiving is the expected data. You can also validate against a response header. Here are the two methods (assume you are downloading xml):

        void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)

        {

            string res = e.Result.ToString(); // make sure returned data is not Café’s login page.

            if (res.Contains("<?xml"))

                result = "Connected!";

            else

                result = "Not Connected!";

        }

 

        void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)

        {

            var t = (sender as WebClient).ResponseHeaders[HttpRequestHeader.ContentType];

 

            if (res.Contains("text/xml")) // make sure returned data is xml and not Café’s (html) login page.

                result = "Connected!";

            else

                result = "Not Connected!";

 

            }

        }

Thanks,

Mike

WP7: TimeZone Info and how to Get It

timeUnlike on the desktop, Silverlight for Windows Phone only provides local and UTC time zone information. I had a requirement for a stock trading application to display all of the transaction times in Eastern time. The difficulty was determining if Daylight Savings Time was in effect or not. On the desktop TimeZoneInfo supports all of the time zones supported in Windows 7, so this is very straight forward.

On Windows Phone, we considered using a rule based on dates, but of course this would break ff the daylight savings rules ever change, which they do from time to time. The following is a solution using the GeoNames service. GeoNames has several geo-location related web services including time zone. (GeoNames is free for up to 30K requests per day. They have a variety of pay plans offering various requests and response times.)

The time zone web service returns the current time zone information for a given latitude, longitude pair. In a nutshell to display transaction times in Eastern time, you apply the East Coast time zone offset returned by GeoNames to the stored UTC value. Code below:

private void button1_Click(object sender, RoutedEventArgs e)
{
    // get account at http://www.geonames.org/ 30K requests / username / day limit.
    // get current timezone info for NYC
    var txtURL = new Uri(http://api.geonames.org/timezone?lat=40.71417&lng=-74.00639&username=demo1);
    var request = (HttpWebRequest)WebRequest.Create(txtURL);
    string responseString = "";
    request.Method = "GET";
    request.BeginGetResponse(a =>
    {
        var response = request.EndGetResponse(a);
        var responseStream = response.GetResponseStream();
        using (var sr = new StreamReader(responseStream))
        {
            using (StreamReader reader = new StreamReader(responseStream))
            {
                DateTime t1 = new DateTime();
                try
                {
                    responseString = reader.ReadToEnd();
                    Debug.WriteLine(responseString);

                    // Get dst offset for NYC
                    XDocument doc = XDocument.Parse(responseString);
                    var dstOffset = GetTime(doc, "dstOffset");
                    var offset = Convert.ToDouble(dstOffset);

                    // Convert local current time to UTC - we will display this in NYC time.
                    var t = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Utc);
                    // apply offset to adjust current utc time to East Coast Time.
                    t1 = t.AddHours(offset);
                }
                catch (Exception ex)
                {
                    Dispatcher.BeginInvoke(() =>
                    {
                        if (ex.Message == "NullReferenceException")
                        {
                            XDocument doc = XDocument.Parse(responseString);
                            var statusMessage = doc.Descendants("status");
                            var q = from c in statusMessage
                                    select c;
                            var en = q.GetEnumerator();
                            en.MoveNext();
                            MessageBox.Show(en.Current.Attribute("message").Value.ToString());
                            return;
                        }
                        else
                        {
                            MessageBox.Show(responseString);
                        }
                        return;
                    });
                }

                Dispatcher.BeginInvoke(() =>
                {
                    localt.Text = DateTime.Now.ToString();
                    ect.Text = t1.ToString();
                });
            }
        }
    }, null);

}

string GetTime(XDocument doc, string element)
{
    var q = from c in doc.Descendants(element)
            select c;
    var en = q.GetEnumerator();
    var t = en.MoveNext();
    return en.Current.Value;
}

You can download a complete sample here.

Thanks,

Mike

WP7: How to Simulate an Application Update

Scenario: You’re developing version 2.0 of your hugely successful Windows Phone application. You want to provide to your users a seamless experience when updating their app to this latest version. That is, you want bring over the current app state into the 2.0 version and not require the user to have to start all over again. You have included the necessary code to do this, but need a way to test it. You ask ‘ Is there any way to simulate an application update with the Windows Phone developer tools?’. You noticed that when using the ‘Application Deployment’ tool that the isolated storage for the app is initialized before the app is installed.

Yes you can simulate an application update. You need to use Visual Studio to do this. When Visual Studio deploys an application, it does not first uninstall the application.

Here is the process to simulate an app update. This will preserve the isolated storage between deployments.

1. Sideload the 1.0 XAP file to the phone or emulator using the ‘Application Deployment’ tool. (We’ll call this MyApplication.XAP)

2. Using Visual Studio create a dummy Windows Phone application (We’ll call this PhoneApp1)

3. Build PhoneApp1 (debug or release)

4. Open the folder where the PhoneApp1.xap is located (e.g. D:\p\PhoneApp1\PhoneApp1\Bin\Debug)

5. Delete PhoneApp1.XAP

6. Copy the updated XAP for your application to this folder (MyApplication.XAP 2.0).

7. Rename MyApplicaiton.XAP to PhoneApp1.XAP

8. Go back to Visual Studio

9. Right Click on Project

10. Select Deploy.

11. The 2.0 version of MyApplication.XAP will now be deployed to the Emulator / Phone. Note that the application binaries are updated, but the isolated storage is the same as before.

12. You can use Windows Phone Power tools to verify.

Thanks,

Mike

WP7: Images in Secondary Tiles do not appear

Problem: Background Images in secondary tiles do not appear if using remote images.

Solution:

<wp:BackgroundImage> and <wp:BackBackgroundImage> can use remote URLs, however, the domains need to be included in the Approved URLS list. For example:

// Lists domains that can send tile updates and so forth as push notifications.
// Only these authorized domains will be allowed by the shell to push new tiles to the phone

var ListOfAllowedDomains = new Collection<Uri> {
new Uri(@"http://i. contoso.com"), new Uri(@"http://i-staging.contoso.com") // e.g. if you published a webservice at http://foo.com/service1.svc — put "http://foo.com" here.
};
pushChannel.BindToShellTile(ListOfAllowedDomains);

By including the domains in the ‘allowed list’ your background images will show up.

Thanks,

Mike

Windows Phone Push Notifications: Frequently Asked Questions

Updated March 3, 2013 – added clarifications around the use of certificates.
The following is a collection of FAQs regarding Windows Phone Push Notifications.

Q: How can I get started with Push Notifications?
A: A good way to approach Push Notifications is using a known working sample. Entire sample including client and server code here. Once you get the sample working, you can transfer that to an actual application. This way you can be sure you are composing the push notifications correctly. I would they suggest they get the sample working and then compare that to how they are doing the same notification type.

Q: Do we have to get a new URI if the original URI was received more than 30 days ago?
A: No. As long as the URI has not changed, you can continue to use the URI.

Q: How do we know if the URI has changed?
A: When the app starts, do an HttpNotificationChannel.Find and compare to the previously received channelURI stored in isolated storage. If different, update the web service registration with the new channelUri. If empty, recreate the Channel. See the file MainPage.xaml.cs in the sample here where this logic has been implemented. Note that it is a best practice to send along with the URI information that uniquely identifies the device (DeviceExtendedProperties.GetValue("DeviceUniqueId"). You can use this information when re-registering channel URIs with your web service to replace the existing (invalid) URI with the new one.

Q: How can we tell at the web service if the URI we are sending to the MPNS is still valid?

A: The MPNS will return a 404 response code with one the following notification statuses if the URI is not valid:

Response Code NotificationStatus DeviceConnectionStatus SubscriptionStatus
404 Not Found Dropped Connected Expired
404 Not Found Dropped Temporarily Disconnected Expired
404 Not Found Dropped Disconnected Expired

Note that your web service should stop sending notifications to this URI and un-register it, if the MPNS returns 404 (Dropped).

Q: What causes the MPNS to return the 404 (expired) response code?
A: The following conditions will cause a channel URI to become expired:
1) No activity for 30 days (the channel uri is not used).
2) The device is unable to connect with the MPN server for 30 days. In this case, MPN will return the 412 (inactive) response code, if it cannot connect within 30 days, it will return 404 (dropped).
3) If the xml payload of the notification request is not valid, MPNS will invalidate the channel uri and return 404.

When the application restarts it will receive a new channel URI, which should replace the current (expired) channel URI registered with the web service.

Q: What causes the MPNS to return the 412 (inactive) response code?
A: If the device has been off or otherwise cannot connect with the MPNS it is considered ‘temporarily disconnected’. When the device is in this state, the MPNS will return:

Response Code NotificationStatus DeviceConnectionStatus SubscriptionStatus
200 OK Received Temporarily Disconnected Active

If the web service sends another notification to this URI and at least 90 minutes have passed since the last ‘temporarily disconnected’ notification, MPNS flags the URI ‘inactive’ and will return the 412 response code. In this state, MPNS stops attempting to connect to the device. Note that temp disconnected and inactive are both natural states for a mobile device and shouldn’t be viewed negatively. Commonly 30-40% of devices show temp disconnected at any one time and 10-20% show inactive because mobile devices transition between network conditions and situations frequently.

Q: Besides sending a notification request to MPNS, is there any other way to know if a channel uri (device) has transitioned to the ‘inactive’ state?
A: No. Previous documented was a callback mechanism where authentication notifications could receive a callback if the device transitioned from active to inactive. This has been deprecated and removed from the documentation.

Q: If the PNS is no longer attempting to connect to the device, when does it start communicating again?
A: The PNS will attempt to connect to the device when the Web Service sends the next notification request. The response code returned reflects the devices state (200 – URI OK, 404, URI invalid, 412, URI OK, Device out of communication)

Q: What does the PNS response code 200 suppressed mean?
A: You can receive the suppressed status when for a:

  • Tile notification if the user hasn’t pinned the tile
  • Raw when the app is not in the foreground
  • Toast and Tile when the device is in low battery state

Q) What is the limit of notifications I can send to the users of my app?
A) By default, the limit is 500 notifications per channel URI per day. So if you have multiple notifications (say Toast and Tile) in your application, you can send 500 of each type. That is 500 of each type of notification per device / user. For example, if you have 100 users of your application and 1 type of notification, you can send up to 50,000 (1 * 500 * 100) notifications per day. Authenticated notifications are unlimited.

Q) How do I setup authenticated notifications?
A) Follow the guidance here: Setting Up an Authenticated Web Service to Send Push Notifications. Pay particular attention to the set up of the certificate. For more information on setting up the certificate, see here: No-quota push notifications using a root Certificate Authority.

Q) What can cause a 401 (Unauthorized) status returned when attempting to send an authenticated notification?
A) Can can get a 401 if the Service Name in the certificate does not match the service name parameter in the HttpNotificationChannel constructor. Also, the client will receive a URI with an ‘http’ prefix. If functioning properly HttpNotificationChannel  will return a Uri with an ‘https’ prefix.

Q) I am setting up authenticated notifications and am getting back a 403 status code in my web service code. What is going wrong?
A) Generally, a 403 (forbidden) status is what you would get if either you are not including a certificate in the POST request or it is not a valid/trusted cert. Note that the certificates need to be deployed onto the web servers sending the requests. These certificates should include the private key. (The certificate uploaded to Microsoft should NOT include the private key.) If using an intermediate certificate, ALL of the certificates in the chain should be added prior to the POST request (see below).  When submitting your application to Marketplace, you will also need to associate your application with the appropriate certificate. This is done in the ‘describe’ step.

Q) How do I include a certificate(s) in the POST request?
A) If you are using .NET in your server code, you can add the certificate to the HttpWebRequest as follows:

Request.ClientCertificates.Add(new X509Certificate2("[PathToCertificate]", "[Password]"));

If you are using Ruby, you can add the certificate as follows:

—————————————————-

#!/usr/bin/ruby

SERVER_CERT_FILE = "certs/freshCA.crt"

CERT_FILE = "certs/s2 at magnesium.crt"

CERT_FILE_KEY = "certs/s2 at magnesium.key"

require ‘net/https’

https = Net::HTTP.new(‘fresh’, 443)

https.use_ssl = true

#client certificates

https.cert = OpenSSL::X509::Certificate.new( File.read(CERT_FILE) )

https.key = OpenSSL::PKey::RSA.new( File.read(CERT_FILE_KEY), ‘panza’)

#server certificate

https.ca_file = SERVER_CERT_FILE

https.verify_mode = OpenSSL::SSL::VERIFY_PEER     #VERIFY_NONE

https.read_timeout = 120

https.start do |https|

  request = Net::HTTP::Get.new(‘/notes/test_auth’)

#  request.basic_auth ‘s’, ‘x’

  response = https.request(request)

  response.value

  puts response.body

end

—————————————————-

Q) In the code above, does this mean the private certificate (definition below) is sent as part of the request?
A) No. The private certificate is used to sign the request. The server then uses the included public cert to decrypt the request.

Q) When do I use a private certificate and when do I a public certificate?
A) First some definitions: a private certificate is one that includes the private key or is used along with a separate key (.key) file. A public certificate is one that does not include the private key. Note that with push notifications, you will use both private and public versions of your certificate.

Q) When do I use the public certificate? When do I use the private?
A) Upload certificate to Dev Center: Public
Install certificate on my web server certificate store: Private
Certificate included with POST request to MPNS: Private

Q) What can cause a 409 to be returned from the MPNS?
A) A 409 is returned when using authenticated notifications and the certificate has expired. You will need to submit a new certificate.

Thanks,

Mike

More Application Optimizing for 256 MB Devices

Background Tasks

Make sure you have exception handling in your Background Task scheduling code. This prevent your app from crashing when run on 256MB devices, if the maximum number of tasks have been scheduled and allowed and if Background Tasks have been disabled by the user. For example:

try
{
    ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException exception)
{
    if (exception.Message.Contains("BNS Error: The action is disabled"))
    {
        MessageBox.Show("Background agents for this application have been disabled by the user.");
    }
    if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
    {
        // No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
    }
}
catch (SchedulerServiceException)
{
    // No user action required.
    PeriodicCheckBox.IsChecked = false;
}

The ability to run code truly in the background is a great feature of Windows Phone 7.1. However, your apps should not be dependent on background tasks. Why? For three reasons:

  • PeriodicTasks can be disabled on an app by app basis. Check out Settings | applications | background tasks on your phone. You will see the list of applications that are using background tasks. Here the user can disable your applications use of background tasks.
  • There is a maximum number of PeriodicTask tasks that can be run. Once this limit is hit, no further tasks can be scheduled.
  • PeriodicTask and ResourceIntensiveTask are not supported on 256MB devices. However, you can include code for these in your application – just understand that these tasks will not run on this class of device. This makes it easy to write a single application that will run on all devices types. In fact on these devices, you will see the ‘background tasks’ is still part of the Settings menu (Settings | applications | background tasks).

If your app is running on a 256 device and includes background tasks, you will see that they are disabled in the applications settings (Settings | applications | background tasks | advanced). See in the screen shot (running the 256MB emulator) below, the background tasks included in the application ‘BackgroundAgentSample’ are disabled.

image

When the maximum number of PeriodicTasks have been scheduled, and you attempt to add another, a InvalidOperationException exception is thrown. The exception message is ‘BNS Error: The maximum number of ScheduledActions of this type have already been added.’ On 256MB devices, this is called anytime you attend to add a scheduled task since the maximum number of PeriodicTasks allowed on these devices is zero.

Note: On premium devices, if you attempt to call ‘Add’ and background agents have been disabled, the exception InvalidOperationException is thrown.

Full SDK sample here.

 

 

 

 

Additional Best Practices

Optimizing Apps for Lower Cost Devices
Developing for 256-MB Devices
Best practice tips for delivering apps to Windows Phone with 256 MB – Nokia Developer Wiki

Performance Measurement Tools in Windows Phone SDK 7.1

The following is a summary of some of the performance measurement tools available from Microsoft for Windows Phone applications.

Marketplace Test Kit – Automated tests for peak memory usage, launch time, app closure, and back button.

These tests are part of the Windows Phone 7.1 SDK. To launch the tools, right click on your project in Visual Studio and select ‘Open Marketplace Test Kit’.

Test Name

Test Description

Launch time

Validation of application launch time.

Peak memory consumption

Validation of application peak memory consumption

Application closure

Validation of all exceptions being handled and application not closing unexpectedly.

Use of Back Button

Validation of proper behavior when pressing the Back button.

Frame Rate Counters for Drawing Performance

To enable:

Application.Current.Host.Settings.EnableFrameRateCounter = true;
Microsoft.Phone.Shell.SystemTray.IsVisible = false; // System Tray not visible 

image

Render Thread FPS: The number of frames per second that the independent simple animations and rendering thread is using. Keeping around 60 will provide a great experience, while a number of 30 fps will begin to show a poor experience to the end user.

Under 30 fps this counter will turn red in post-beta builds.

User Interface Thread FPS: The number of fps that the primary user interface thread is experiencing. Property change notifications, data binding, primary managed code execution, and animations not handled on the render thread use this threads’ resources.

Turns red when the count is at or below 15 fps.

Texture Memory Usage: A specialized memory counter indicating the video memory used for storing application textures.

Surface Counter: A count of the number of surfaces that are passed to the graphics chip.

Intermediate Texture Count: The number of intermediate textures created for compositing.

Screen Fill Rate: A metric representing the number of complete phone screens being painted each and every frame.

Memory APIs for measuring / logging memory usage

long applicationCurrentMemoryUsage = 
     (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
long applicationPeakMemoryUsage = 
     (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage"); 

Thanks,

Mike

Windows Phone 7: New App Hub Tips

apphub

 

Todd Brix gives a summary here of the new features included in the App Hub Update that went live on July 18th. In this post I’ll review a couple of the new features and tips for using them.

App Name now determined by XAP, not entry in App Hub.

Previously you provided the application name in the description step (step 2) See screenshot below. You needed to specify this for each language your application supported. (See below.)

Pre-July 2011 App Hub Update - app name provided by developer for each language app supported.

Now with the App Hub update, the application name is derived from the Title attribute of the App element in WMAppManifest.xml:

<App xmlns="" ProductID="{xxx5d2d6-ebb1-4e5b-bd12-7e1371696e80}" Title="People and Places" 
       RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" 
       Author="Mike Francis" Description="" Publisher="Mike Francis">

This is great improvement since now the application title is maintained in one place: your application. See this blog post for information on localizing the application title.  App Hub will automatically retrieve the appropriate localized application title for each supported language. See here for a sample project with this implemented. Try creating a test submission (you can delete it later) and upload the resulting XAP file from the sample. Note how App Hub is creating a language version for each language supported. Also note how the localized application name is automatically populated.

image

Default Language now must be specified in Project Settings

As stated in this forum post, the App Hub now requires a default language specified in your application. This is the ‘Neutral Language’ assembly setting in Project settings.

  1. Right Click on your project in Solution Explorer
  2. Application | Assembly Information. . .
  3. Neutral Language
  4. Pick a specific default language. For example for English:

image 

App artwork can now be specified in one click!

In the ‘Describe’ section (step 2) of your application submission, you provide the screenshots and icons that are used by the PC and Phone versions of Marketplace. Another nice AppHub feature is the ability to upload all of the artwork at once. You can do this as long as you have the art work in the same directory. For my application, I have created a directory <Project Name>\Images\Marketplace and placed my icons and screenshots there. Then when submitting my app, in the ‘Artwork’ section of step 2, I click ‘Browse”, and multi-select all of the artwork in this folder. App Hub intelligently associates the correct size icon with the corresponding App Hub icon slot. See video below.

App Hub: One click application art upload

Tip: This feature reads the screenshot images in alphabetical order, so if you care about the order of your screenshots, rename them so that the filenames are in alpha-order. This way when the tool reads in your screenshots, they will be in the order you expect. For example, (1ScreenShot.png, 1ScreenShot.png, etc.)

screenshots in alpha order

Thanks,

Mike

Windows Phone 7: Login UI Sample Code and Coding4Fun

image_4

The Coding4Fun library (http://coding4fun.codeplex.com) for Windows Phone 7 includes many super useful classes that will save you time when writing Windows Phone 7 applications. My favorite is their implementation of very the professional looking and easily extensible user prompts.

While a good assortment of prompts, utilities and other controls have been implemented in coding4fun, what is missing is a login prompt. In this post I’ll review how I’ve used Coding4Fun to create a login prompt.

Can’t I just use a login page?

Many developers have struggled with the Microsoft enforced application navigation flow that mandates:
1) Pressing the Back button must return the application to the previous page or return to any previous page within the back stack.
2) Pressing the Back button from the first screen of an application must close the application.

These rules are difficult to reconcile with an application that has a login prompt on the first page. A couple of problems with this:
1) After the user has successfully logged in, and they have navigated away from the login page, it is confusing if pressing BACK returns them to the Login screen. The expectation is that once they have logged in they should not see the login screen again.
2) Even if you structure the program to navigate to the Login page automatically from the main page if the user has not yet logged in, this gets rid of the ‘BACK navigates to the Login screen’ problem, but it violates the first rule above where BACK on the first screen should exit the app.

See Peter Torr’s article, Introducing the concept of “Places”, for a way to think of ‘places’ and ‘transient UI’. In Peter’s article, he recommends one way to handle the login scenario is with the use of a popup. Enter Coding4Fun.

How It Works

The Coding4Fun’s MessagePrompt class, includes a Body property that you can use to add code on the fly. For example:

messagePrompt.Body = new TextBlock { Text = "Hello World!", Foreground = new SolidColorBrush(Colors.Green),

FontSize = 30.0}

I extended this idea to include, a user control ‘LoginUserControl’ which contains the XAML layout and Username and Password members which are databound to the user control.

In the code below I instantiate the LoginUserControl control, add it to the Body member of an instance of MessagePrompt, display the prompt, and process the user results.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var luc = new LoginUserControl(); // Customer user control with Login UI

    MessagePrompt messagePrompt = new MessagePrompt(); // Coding4Fun extensible MessagePrompt
    messagePrompt.IsCancelVisible = true; // Show cancel button
            
    messagePrompt.Body = luc; // Add user control as body of MessagePrompt
    messagePrompt.Completed += (str, res)=> // Handler for MessagePrompt user action
    {
 
       if (res.PopUpResult == PopUpResult.Cancelled)
        {
            luc.Username = luc.Password = "";
            MessageBox.Show("Login Cancelled");
        }
        else
        {
            MessageBox.Show(string.Format("Username: {0}, Password: {1}", luc.Username, luc.Password));
        }
    };
            
    messagePrompt.Show(); 
}

You can download the sample code here.

Mike

Windows Phone 7: Be Careful with that Product ID!

A couple developers I have been working with have had a problem with their application crashing during certification testing, or their app not working as expected after being published. This problem was difficult to debug since the app worked fine for the developer, but would crash out of the gate during testing. After some investigation, we found that the developer was relying on the Product ID to be a known value.

The Product ID is a GUID used to identify your application in the Windows Phone installed application list. This value stays the same across application updates. The Product ID is not controlled by the developer, but is assigned during the ingestion process. That is, the value created by Visual Studio and stored in WMAppManifest.xml, is not the value that will be used when the application is published.

If you are relying on the Product ID to be a certain value – it won’t be what you expect, when your application is published.

<App xmlns="" 
      ProductID="{eb186f6f-78ab-49d2-937d-daa5d0cb7889}" 
      Title="BestAppEver" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal"
      Author="BestAppEver author" Description="Sample description" Publisher="Cookie1">

Here are a couple of workarounds to consider:

  1. Instead of using the Product ID, use a private scheme to store the unique value your app needs.
  2. If your really need to use the Product ID, you can leverage the way App Hub works. That is, the Product ID is assigned early on in the submission process. Specifically, in Step 1 when you upload your app. You can upload a preliminary version of your app and not submit it for testing. You can then use this Product ID in your code. When your application is ready for submission, you can re-upload your app, overwriting the preliminary version with the final version. The Product ID will not change. See below for sample code on how to read the Product ID at run-time.

To see the Product ID as assigned by Product ID, in your “my apps” list on the dashboard, click on the name of your submission.

clip_image002

Here, you can see the Product ID and the Deep Link. This will not change for this submission.

clip_image004

Code to read the Product ID at run time:

public static string GetProductId() 
{
    System.Xml.Linq.XElement xml = System.Xml.Linq.XElement.Load("WMAppManifest.xml");
    var appElement = (from manifest in xml.Descendants("App") select manifest).SingleOrDefault();
    if (appElement != null) { 
        return appElement.Attribute("ProductID").Value; 
    } 
    return string.Empty; 
}

Mike