Getting started with Windows 7 phone application development..


I have just started developing a fairly simple Windows 7 phone application that talks to a web service and thought I would put together a few sites that helped me get started. I’ll briefly describe some of the issues I ran into during the process and also share a C# utility class I have put together to wrap all the web services calls.

  1. The MSDN documentation is probably the best place to get started. How to: Create Your First Silverlight Application for Windows Phone tutorial in Visual Studio 2010 is pretty good; I was having a bit of trouble finding the Windows Phone Developer Tools in App Hub – I went to ‘my dashboard’ and created an account, but if you just want to download the tools you need to go to resources -> downloads.
  2. How to: Perform Page Navigation on Windows Phone was probably the next most useful site. There’s a number of other tutorials/blogs describing the ‘how to get started’ process as well – Google is your friend.
  3. I wanted to talk to a web service and get the authentication token so that I can implement a ‘log in’ screen. I had some JavaScript code that was doing this already, but with my limited ASP.net knowledge, it was a challenge to figure out how to implement this. Fortunately, Developing Windows Phone 7 Applications for SharePoint 2010 site came to the rescue.

Initially, I had my two pages – a login page and another page to display after logging in. Idea was to let a user log in and then retrieve some data from a web service (using SOAP). Following is the authentication code I had. The web site didn’t have cookie based authentication, so the CookieJar stuff mentioned in (3) above wasn’t necessary – I have put together a neat Utils class that wraps all the web services calls (well, I thought it was neat anyway!) and the details are at the end, but in essence, this is what I had:

public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); auth = new Authentication(); auth.LoggedIn += new EventHandler(UserLoggedIn); } private void btnLogin_Click(object sender, RoutedEventArgs e) { // get userName/password from controls auth.Authenticate(userName, userPassword); } private void UserLoggedIn(object sender, EventArgs e) { LoggedInEventArgs le = e as LoggedInEventArgs; string token = le.Token; Dispatcher.BeginInvoke(() => { NavigationService.Navigate(new Uri("/somepage.xaml?token=" + token, UriKind.RelativeOrAbsolute)); }); } }

So basically I register the UsedLoggedIn event handler in the MainPage and then fire the LoggedIn event once the user successfully logs in from the Authentication class shown below:

public class Authentication { public event EventHandler LoggedIn; private const string AuthServiceUri = "http://...AuthenticationWS.asmx"; private const string AuthEnvelope = @"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""> <soap:Body> <GetToken xmlns='http://../Authentication'> <userName>{0}</userName> <pswd>{1}</pswd> </GetToken> </soap:Body> </soap:Envelope>"; private string _userName, _userPassword; public void Authenticate(string userName, string userPassword) { _userName = userName; _userPassword = userPassword; HttpWebRequest spAuthReq = HttpWebRequest.Create(AuthServiceUri) as HttpWebRequest; spAuthReq.Headers["SOAPAction"] = "http://..."; spAuthReq.ContentType = "text/xml; charset=utf-8"; spAuthReq.Method = "POST"; spAuthReq.BeginGetRequestStream(new AsyncCallback(spAuthReqCallBack), spAuthReq); } private void spAuthReqCallBack(IAsyncResult asyncResult) { UTF8Encoding encoding = new UTF8Encoding(); HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; Stream _body = request.EndGetRequestStream(asyncResult); string envelope = string.Format(AuthEnvelope, _userName, _userPassword); byte[] formBytes = encoding.GetBytes(envelope); _body.Write(formBytes, 0, formBytes.Length); _body.Close(); request.BeginGetResponse(new AsyncCallback(ResponseCallback), request); } private void ResponseCallback(IAsyncResult asyncResult) { HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult); if (request != null && response != null) { if (response.StatusCode == HttpStatusCode.OK) { StreamReader reader = new StreamReader(response.GetResponseStream()); string responseString = reader.ReadToEnd(); XDocument xmlStr = XDocument.Parse(responseString); string token = xmlStr.Root.Value; if (this.LoggedIn != null) { // event will be null if no one is listening for it - you can't fire an event to no one, so check for null this.LoggedIn(this, new LoggedInEventArgs(token)); } } } } } public class LoggedInEventArgs : EventArgs { private string token; public LoggedInEventArgs(string tt) { this.token = tt; } public string Token { get { return token; } } }

Things were just fine until I got to this stage. I started running into problems only on the second page, after getting the authentication token. On my second page this is what I had:

// auto generated InitializeComponent(); code in constructor protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); string token = ""; if (NavigationContext.QueryString.TryGetValue("token", out token)) { ExecuteSelectQuery(token); } } private void ExecuteStoredQuery(string token) { HttpWebRequest spAuthReq = HttpWebRequest.Create(qpurl) as HttpWebRequest; spAuthReq.Method = "POST"; spAuthReq.ContentType = "text/xml"; spAuthReq.Headers[".."] = token; spAuthReq.Headers[".."] = ".."; spAuthReq.Headers["SOAPAction"] = "http://.."; spAuthReq.BeginGetRequestStream(new AsyncCallback(spAuthReqCallBack), spAuthReq); } private void spAuthReqCallBack(IAsyncResult asyncResult) { string qryProcUrl = "http://....asmx"; string envelope = "<?xml version='1.0' encoding='utf-8'?>" + "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:q1='http://microsoft.com/wsdl/types/' xmlns:t='http://tempuri.org/'>" + "<soap:Body>" + // your soap body "</soap:Body>" + "</soap:Envelope>"; UTF8Encoding encoding = new UTF8Encoding(); HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; Stream _body = request.EndGetRequestStream(asyncResult); byte[] formBytes = encoding.GetBytes(envelope); _body.Write(formBytes, 0, formBytes.Length); _body.Close(); request.BeginGetResponse(new AsyncCallback(ResponseCallback), request); } private void ResponseCallback(IAsyncResult asyncResult) { HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult); if (request != null && response != null) { if (response.StatusCode == HttpStatusCode.OK) { StreamReader reader = new StreamReader(response.GetResponseStream()); string responseString = reader.ReadToEnd(); XDocument xmlDoc = XDocument.Parse(responseString); // got the required data. Parse xmlDoc and do stuff on this page } } }

The strange issue I encountered was that this page was working just fine within the company network, but from home when I’m connected to the internet, I would keep getting a:

A first chance exception of type ‘System.Net.WebException’ occurred in System.Windows.dll

error which is not helpful at all!

image

The same code was working just fine on the network, so I was suspecting some security related issue. However, the JavaScript code I had which was running in IE/FF was working just fine, and both of them were talking to the same web service.

After an embarrassingly long time of trying to debug this issue, I found out about Fiddler – a neat tool to monitor network traffic. Eric Law’s blog on Fiddler and the Windows Phone 7 Emulator looked promising, however, I was having trouble getting the C# app on the phone to log traffic. Almost all the posts on the net show how to monitor traffic on the browser on the emulator, not traffic from an app on the emulator. Then I spotted how Eric says ‘1. Install Fiddler 2.3.0.7’ in his instructions, so I just checked mine, and it was v2.3.0.6. Downloaded the latest beta, which was v2.3.1.0, and I could see traffic from my app!!

Finally I at least knew where the problem was. I was getting the token just fine, but then when I was trying to talk to the web service to get some actual data, it was failing with an http 500 error (which is a server side error! – http://www.checkupdown.com/status/E500.html) . I was getting the following:

image

This didn’t explain why I was getting this error. After all, I could still talk to the same web service using IE/FF, and the same C# code worked just fine from work. I was sending the same request (so I thought anyway!) to the same web service, only difference was one was from the browser and the other from the emulator, so why would I keep getting a server error? This eliminated the possibility that it was something to do with the server authentication/security cos don’t think the server cares about the client the request originated from. This is when I started comparing the request from the browser and the request from the Win7 phone app using Fiddler.

Initially I didn’t spot anything different cos they both looked exactly the same. I was getting pretty frustrated by this time..Anyway, after some more painstakingly long time, I spotted the following:

Request from emulator: image

Request from browser: image

As you may (or may not!) see, the +s in the authorisation token were getting replaced by spaces! arghh…Steaming mad

Once I spotted this, it was easy to get the rest of it sorted. Now that I knew what to search for, I saw quite a few people had run into the same problem..only if I knew what to search for before! I still don’t know why the same C# app works fine from within the corporate network..but don’t think I’m going to bother. May be some proxy between my machine and the actual server ‘fixes’ the spaces in the request before it reaches the server.

People have suggested using HttpUtility.UrlEncode(string), but don’t think it’s going to work for me. It encodes the whole strong (see Using HttpUtility.UrlEncode to Encode your QueryStrings). In my case the issue is in using:

string token = ""; if (NavigationContext.QueryString.TryGetValue("token", out token)) { // do stuff }

and this is where ‘token’ gets its spaces. You can encode the Uri before passing it, 

token = HttpUtility.UrlEncode(token); Dispatcher.BeginInvoke(() => { NavigationService.Navigate(new Uri("/SomePage.xaml?token=" + token, UriKind.RelativeOrAbsolute)); });

but still in the NavigationContext.QueryString token would have spaces instead of the encoded values:

image vs image

So for the time being, I use a simple:

token.Replace(” “, “+”)

although this may not be an elegant solution. Happy to take any suggestions.

Finally, I promised to include the Utility class I have created which takes care of all the callbacks and the web service calls. This is what I have written (reusing some code from Developing Windows Phone 7 Applications for SharePoint 2010):

public class SelectQueryExecutedEventArgs : EventArgs { private string caller; private XDocument qryResult; public SelectQueryExecutedEventArgs(XDocument qryResult, string caller) { this.qryResult = qryResult; this.caller = caller; } public string Caller { get { return caller; } } public XDocument QueryResult { get { return qryResult; } } } public class Utils { public static event EventHandler SelectQueryExecuted; private static string envelope, caller; public static void ExecuteSelectQuery(string url, Dictionary<string, string> headers, string envelope, string caller) { HttpWebRequest spAuthReq = HttpWebRequest.Create(url) as HttpWebRequest; foreach (string s in headers.Keys) { spAuthReq.Headers[s] = headers[s]; } Utils.envelope = envelope; Utils.caller = caller; spAuthReq.ContentType = "text/xml; charset=utf-8"; spAuthReq.Method = "POST"; spAuthReq.BeginGetRequestStream(new AsyncCallback(spAuthReqCallBack), spAuthReq); } private static void spAuthReqCallBack(IAsyncResult asyncResult) { UTF8Encoding encoding = new UTF8Encoding(); HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; Stream _body = request.EndGetRequestStream(asyncResult); byte[] formBytes = encoding.GetBytes(envelope); _body.Write(formBytes, 0, formBytes.Length); _body.Close(); request.BeginGetResponse(new AsyncCallback(ResponseCallback), request); } private static void ResponseCallback(IAsyncResult asyncResult) { HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult); if (request != null && response != null) { if (response.StatusCode == HttpStatusCode.OK) { StreamReader reader = new StreamReader(response.GetResponseStream()); string responseString = reader.ReadToEnd(); XDocument xDoc = XDocument.Parse(responseString); // now that we've got xDoc, fire the event so that the event handler // in the corresponding page can 'do stuff' Utils.SelectQueryExecuted(null, new SelectQueryExecutedEventArgs(xDoc, Utils.caller)); } } } }

I think the code should be fairly easy to follow, but basically all we are doing is having a SelectQueryExecuted event and firing it once we have the required data. The handlers that have registered with this event should then be able to handle it as needed. I’m passing the ‘caller’ back since all registered events will be getting this event, but we want only one of them to handle the event. May be there’s a better way to do this, but this is what I came up with. I have made the functions above ‘static’, but we could easily make them instance methods as well and then create a ‘Utils’ instance within each page, and this way only that page will get the event. I felt this would make the code cleaner, and easier to use. To register/call the above, this is what we do:

public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); Utils.SelectQueryExecuted += new EventHandler(Authenticated); } private void btnLogin_Click(object sender, RoutedEventArgs e) { string authUri = @"http://...asmx"; string envelope = @"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""> <soap:Body> <GetToken xmlns='http://..'> <userName>{0}</userName> <pswd>{1}</pswd> </GetToken> </soap:Body> </soap:Envelope>"; envelope = string.Format(envelope, userName, userPassword); Dictionary<string, string> d = new Dictionary<string,string>(); d.Add("SOAPAction", "http://.."); //.. any other headers that may be needed Utils.ExecuteSelectQuery(authUri, d, envelope, this.GetType().Name); } private void Authenticated(object sender, EventArgs e) { SelectQueryExecutedEventArgs se = e as SelectQueryExecutedEventArgs; if (se.Caller.Equals(this.GetType().Name)) { XDocument xDoc = se.QueryResult; string token = xDoc.Root.Value; Dispatcher.BeginInvoke(() => { NavigationService.Navigate(new Uri("/NextPage.xaml?token=" + token, UriKind.RelativeOrAbsolute)); }); } } } // Similarly, in the 'NextPage', this is what we have public partial class NextPage : PhoneApplicationPage { public NextPage() { InitializeComponent(); Utils.SelectQueryExecuted += new EventHandler(GotStuff); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); string token = ""; if (NavigationContext.QueryString.TryGetValue("token", out token)) { string qryProcUrl = "http://...asmx"; string envelope = "<?xml version='1.0' encoding='utf-8'?>" + "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:q1='http://microsoft.com/wsdl/types/' xmlns:t='http://tempuri.org/'>" + "<soap:Body>" + //... "</soap:Body>" + "</soap:Envelope>"; Dictionary<string, string> d = new Dictionary<string, string>(); d.Add("tokenKey", token.Replace(" ", "+")); d.Add("userid", ".."); //.. any other headers Utils.ExecuteSelectQuery(qryProcUrl, d, envelope, this.GetType().Name); } } private void GotVitals(object sender, EventArgs e) { SelectQueryExecutedEventArgs se = e as SelectQueryExecutedEventArgs; if (se.Caller.Equals(this.GetType().Name)) { XDocument xDoc = se.QueryResult; Dispatcher.BeginInvoke(() => { foreach (XElement item in xDoc.Descendants()) { // update whatever needed in the UI thread } }); } } }

I hope someone would find this info useful Smile

 

Tags/Keywords: Windows phone 7 development; A first chance exception of type ‘System.Net.WebException’ occurred in System.Windows.dll; Pluses (+s) getting replaced with spaces; C# utility class for calling SOAP web services; Passing authentication token between pages while navigating after logging in

Advertisements
This entry was posted in Windows 7 phone development and tagged . Bookmark the permalink.

6 Responses to Getting started with Windows 7 phone application development..

  1. Praveen says:

    Hi thank you very much for your information, it helped to solve issue….

  2. Praveen says:

    and good explanation through code

  3. edu says:

    Thank you for sharing your experience. It’s hard to find auth over soap examples for WP7

  4. ramupaideti says:

    Can u provide article how to store WP7 image at server side by using web services…

    Really i am searching for this from so many days….even though i got little bit ideas from google, I am not able to get the result finally, because…I am new to this WINDOWS PHONE7 environment….

    As I have to implement and submit project at this month i.e 22 August…Please provide article with service code.
    Thanks in advance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s