<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Andy Pennell's Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/andypennell/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/andypennell/feed.xml</link>
    <description>See me blather about software development, Gadgets, Technology, Fatherhood, plus anything else that gets me going.</description>
    <lastBuildDate>Tue, 16 Apr 2019 04:39:21 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Updating Excel data with Project Siena</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/updating-excel-data-with-project-siena</link>
      <pubDate>Fri, 10 Oct 2014 13:10:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2014/10/10/updating-excel-data-with-project-siena/</guid>
      <description><![CDATA[This post describes a method of using Project Siena with Excel data that can be updated later...]]></description>
      <content:encoded><![CDATA[<p>This post describes a method of using Project Siena with Excel data that can be updated later without having to change the app. [Yes Its been a while since I posted anything, thought it was about time so here is my first post, using the cool product I am now working on]</p>
<p>Siena can import Excel data in all its glory, but only while you are creating your app. The data is literally inserted into the app, so cannot be updated once your app has been published. (Before being published you can update your data by tapping the Refresh icon on the data sources page). This post describes a way of making your app consume updated data, after the app is done. All you need is to be able to write a .txt file to a location on your intranet or the internet.</p>
<h3>TL;DR version</h3>
<ol>
<li>Save as CSV</li>
<li>Convert to JSON as .txt file</li>
<li>Put .txt file on intranet/internet somewhere</li>
<li>Create WADL file that points to this .txt file</li>
<li>Import WADL file into Siena</li>
</ol>
<h3>Here is the rather more detailed version</h3>
<p><br />Firstly lets make a sample spreadsheet called Movies.xlsx which contains a simple table of the best movies out there, and call the table itself Movies.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/3107.moviesinexcel.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/3107.moviesinexcel.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/3107.moviesinexcel.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/3107.moviesinexcel.PNG" alt="" border="0" /></a><br /><br />In Excel do File / Save As a CSV file to create Movies.csv. You will likely get a warning about CSV files losing some information, but that&rsquo;s ok, remember to keep the original xlsx file safe just in case. I&rsquo;m sure there any many ways to convert Excel data to JSON, but the first one I found, that worked for me, was <a href="http://www.convertcsv.com/csv-to-json.htm">http://www.convertcsv.com/csv-to-json.htm</a> . (This should not be taken as any kind of Microsoft endorsement of this site). Load your CSV file then click Convert CSJ to JSON, and from there click Save to Disk. This will create a .json file, I recommend renaming it to .txt as some web hosts (eg mine) are not happy delivering .json files over the internet (due to mime type configuration I think).</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5504.convertjson.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5504.convertjson.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5504.convertjson.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5504.convertjson.PNG" alt="" border="0" /></a></p>
<p>Next you need to put this movies.txt file on the intranet/internet using whatever method is available to you, such as FTP. Lets pretend I put this file on <a href="http://www.contoso.com/data/movies.txt">http://www.contoso.com/data/movies.txt</a> (I did also try putting this on a corporate Sharepoint site, but the authentication got in the way).</p>
<p>Next install the WADL Generator <a href="http://social.technet.microsoft.com/Forums/en-US/82c7b717-5100-423c-aa44-effed28caf74/wadl-generator-for-project-siena?forum=projectsiena">http://social.technet.microsoft.com/Forums/en-US/82c7b717-5100-423c-aa44-effed28caf74/wadl-generator-for-project-siena?forum=projectsiena</a> and fire it up.<br />Give the service a name (lets call it MovieDB), click Next. There is no authentication, so Next on that screen. We get to the functions screen, in the Name field put GetAll, and in the url field put the path to the .txt file. Click &ldquo;Try It&rdquo; and you should see the JSON in the window. Click Save and Next a few times, then name this file MoviesWADL.xml.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4846.wadlfinal.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4846.wadlfinal.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4846.wadlfinal.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4846.wadlfinal.png" alt="" border="0" /></a></p>
<p>OK lets make a simple Siena app that consumes the Excel file directly, then we&rsquo;ll update it to use the online version. Start up Siena,&nbsp;go to App Data / Data Sources / Excel and import the original Movies.xlsx file, selecting the table called Movies, then Import Data. The screen should show our data, so hit the back button to get to the canvas. Click + and add a new Text Gallery, then set its Items source to Movies, and instantly you&rsquo;ll see the data in a scrollable list.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5047.moviegallery.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5047.moviegallery.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5047.moviegallery.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5047.moviegallery.png" alt="" border="0" /></a></p>
<p>Save this Siena file, though of course in the real world you would create a fantastic app using this Movies data source. OK, lets switch the app over to the online version: back to Data Sources and delete the Movies.xlsx data. Instead click on REST and select the MoviesWADL.xml file we made earlier. To make sure it works, click the Try it button to see the data. Click Back to get to the canvas again, select the gallery we made earlier (which will be showing error triangles at this point), and change its Items property to MoviesDB!GetAll(). As if by magic the data will show up just as before, but now it is getting it live, well when the app starts up at least.</p>
<p>If you have multiple Excel tables you need to expose like this, create additional functions in the WADL Generator pointing to different .txt files, and give them other names. When your Excel data changes, you just have to repeat steps 1-3 above &ndash; no change in the WADL file or your app required. This method works well for data that doesn&rsquo;t change very often, but if updates are required too often then you should consider creating a proper web service connected to a database, and expose a real REST API,&nbsp;instead of this manual conversion process.</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Common Questions (and Answers) about Windows Phone 8</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/common-questions-and-answers-about-windows-phone-8</link>
      <pubDate>Thu, 06 Feb 2014 17:26:35 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2014/02/06/common-questions-and-answers-about-windows-phone-8/</guid>
      <description><![CDATA[Here at Microsoft we have a high percentage of Windows Phone 8 users (go figure?), however despite...]]></description>
      <content:encoded><![CDATA[Here at Microsoft we have a high percentage of Windows Phone 8 users (go figure?), however despite this I see so many of the same questions being asked by WP users on the DLs and internal Yammer pages. Here are those questions, and my answers to them. If some MS employees don&rsquo;t know these things, chances are pretty good that other folks don&rsquo;t either. Remember, I am not an official Windows Phone spokesperson, and I don't play one on TV. Plus I haven't even worked on Phone for several years now. However I have been an avid user and fan of WP as well as a developer of apps, so I pay attention to things. I may not be able to remember my kids' teachers' names, but I can remember how to unbrick a 920 for example.

<h3>When is my phone going to get the GDR3 / Black update?</h3>

Nokia have this covered here: <a href="http://www.nokia.com/global/support/software-update/wp8-software-update/">http://www.nokia.com/global/support/software-update/wp8-software-update/</a><br>Microsoft's page listing the revisions is here: <a href="http://www.windowsphone.com/en-us/how-to/wp8/basics/windows-phone-8-update-history">http://www.windowsphone.com/en-us/how-to/wp8/basics/windows-phone-8-update-history</a>

<h3>My phone is unlocked and on a different carrier, when is my phone going to get the update?</h3>

The carrier your phone is <em>currently on</em> doesn&rsquo;t matter at all for updates, what matters is the carrier it was <em>originally sold by</em>. Go to Settings / About and under Model you will see Carrier: use this to go back to the table above and see when your update is coming. (If your phone is "open market" ie sold unlocked in a saner cellphone market than the US I don't honestly know what it will say here).

<h3>My Lumia 920 is stuck at the gears screen after I updated/reset my phone</h3>

First off wait 24 hours. I'm not kidding, pre-GDR3 builds can take a gigantic amount of time for a few folks. If its still stuck, consider reflashing the original ROM image, using the official Nokia tool, downloaded from the actual Nokia site (lots of sketchy sites seem to have it too, ignore them). <a href="http://www.nokia.com/us-en/support/software-recovery/">http://www.nokia.com/us-en/support/software-recovery/</a> Note this will wipe all data from the phone, which is why it is important to ensure everything has sync to SkyDrive etc. before taking an update.

<h3>I have trouble with Bluetooth in my car</h3>

First off get GDR3, a pile of work was done for Bluetooth there. It also has a new option to work with cars that don&rsquo;t quite follow the Bluetooth spec correctly. (See earlier post from me on this here <a href="http://blogs.msdn.com/b/andypennell/archive/2011/09/27/car-head-unit-compatibility-with-windows-phone-7-5-mango.aspx">http://blogs.msdn.com/b/andypennell/archive/2011/09/27/car-head-unit-compatibility-with-windows-phone-7-5-mango.aspx</a>).

<h3>After the update, most of my apps don&rsquo;t start</h3>

Firstly reboot your phone as sometimes that doesn&rsquo;t happen automatically after an update. If that doesn&rsquo;t help, download and install a new keyboard layout. I know this sounds crazy, but it always works.

<h3>Will an unlocked Lumia 1020 work on T-Mobile (USA)?</h3>

Yes, its what I use every day, so long as it&rsquo;s the AT&amp;T or Rogers version of the 1020 (due to LTE radio frequencies). I got mine when it came out and unlocking it was easy (via eBay), but it isn&rsquo;t so easy these days, and I'm not going to detail on any of that, because I don&rsquo;t know the current state.

<h3>My Lumia makes the charging sound randomly (eg in the middle of the night) when using a wireless charger</h3>

This annoyance was fixed in the Black firmware update.

&nbsp;

<p>Please don&rsquo;t post additional questions below, as I probably don&rsquo;t know the answer and/or I am not a support service. I just don&rsquo;t like repeating myself, that's all.&nbsp;Hope you find this useful.</p>]]></content:encoded>
    </item>
    <item>
      <title>Getting the Name of your Windows Phone 8 device</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/getting-the-name-of-your-windows-phone-8-device</link>
      <pubDate>Sat, 09 Nov 2013 13:49:01 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2013/11/09/getting-the-name-of-your-windows-phone-8-device/</guid>
      <description><![CDATA[You can name your phone to whatever you like, but how can an app display that name? There isn't an...]]></description>
      <content:encoded><![CDATA[<p>You can name your phone to whatever you like, but how can an app display that name? There isn't an obvious API for this, but I accidentally discovered an un-obvious API. The quick version is that you can ask getaddrinfo for the canonical network name. Too quick? OK, here is some C++/CX code to show exactly:</p>
<pre style="background: white; color: black; font-family: Consolas;">Platform::<span style="color: #2b91af;">String</span>&nbsp;^&nbsp;<span style="color: #2b91af;">DeviceName</span>::PhoneName::get()
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">WSADATA</span>&nbsp;wsadata;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">ADDRINFOA</span>&nbsp;*result&nbsp;=&nbsp;<span style="color: blue;">nullptr</span>;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;error;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">wchar_t</span>&nbsp;wszName[256]&nbsp;=&nbsp;L<span style="color: #a31515;">"Unknown"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">ADDRINFOA</span>&nbsp;hints&nbsp;=&nbsp;{};
 
&nbsp;&nbsp;&nbsp;&nbsp;hints.ai_flags&nbsp;=&nbsp;<span style="color: #6f008a;">AI_CANONNAME</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;error&nbsp;=&nbsp;WSAStartup(<span style="color: #6f008a;">MAKEWORD</span>(2,2),&nbsp;&amp;wsadata);
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(error==0)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error&nbsp;=&nbsp;::getaddrinfo(<span style="color: #a31515;">""</span>,&nbsp;<span style="color: #a31515;">"0"</span>,&nbsp;&amp;hints,&nbsp;&amp;result);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(error==0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">char</span>&nbsp;*name&nbsp;=&nbsp;result-&gt;ai_canonname;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(name!=<span style="color: blue;">nullptr</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MultiByteToWideChar(<span style="color: #6f008a;">CP_ACP</span>,&nbsp;0,&nbsp;name,&nbsp;strlen(name),&nbsp;wszName,&nbsp;256);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;::freeaddrinfo(result);
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">ref</span>&nbsp;<span style="color: blue;">new</span>&nbsp;Platform::<span style="color: #2b91af;">String</span>(wszName);
}
</pre>
<p>If you haven't used C++/CX on the phone you might need a little more info on how to hook this up, so here is the C++/CX Super-QuickStart guide by me:</p>
<p>Load your C# project into VS2012. Right click on the Solution then Add / New Project / Visual C++ / Windows Phone / Windows Phone Runtime Component and call it NetHelper.</p>
<p>In pch.h add:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">#include</span>&nbsp;<span style="color: #a31515;">&lt;windows.h&gt;</span>
<span style="color: blue;">#include</span>&nbsp;<span style="color: #a31515;">&lt;winsock2.h&gt;</span>
<span style="color: blue;">#include</span>&nbsp;<span style="color: #a31515;">&lt;ws2tcpip.h&gt;</span>
</pre>
<p>Replace NetHelper.h with this simple ref class containing a single static property:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">#pragma</span>&nbsp;<span style="color: blue;">once</span>
 
<span style="color: blue;">namespace</span>&nbsp;NetHelper
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">ref</span>&nbsp;<span style="color: blue;">class</span>&nbsp;DeviceName&nbsp;<span style="color: blue;">sealed</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">property</span>&nbsp;Platform::String&nbsp;^&nbsp;PhoneName
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Platform::String&nbsp;^&nbsp;get();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;};
}</pre>
<p>Replace NetHelper.cpp with the code at the start of the article, prefixed with this:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">#include</span>&nbsp;<span style="color: #a31515;">"pch.h"</span>
<span style="color: blue;">#include</span>&nbsp;<span style="color: #a31515;">"NetHelper.h"</span>
 
<span style="color: blue;">using</span>&nbsp;<span style="color: blue;">namespace</span>&nbsp;NetHelper;
<span style="color: blue;">using</span>&nbsp;<span style="color: blue;">namespace</span>&nbsp;Platform;
 
<span style="color: blue;">#pragma</span>&nbsp;<span style="color: blue;">comment</span>(<span style="color: blue;">lib</span>,&nbsp;<span style="color: #a31515;">"ws2_32.lib"</span>)
</pre>
<p>Build the NetHelper project.</p>
<p>Right click on your C# project's References tree / Add Reference / Solution / add a checkmark next to NetHelper / OK</p>
<p>In C# you can now using it with</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">var</span>&nbsp;name&nbsp;=&nbsp;NetHelper.<span style="color: #2b91af;">DeviceName</span>.PhoneName;
</pre>
<p>That's it!</p>]]></content:encoded>
    </item>
    <item>
      <title>Handling Contract assertions on Windows Phone 8</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/handling-contract-assertions-on-windows-phone-8</link>
      <pubDate>Fri, 18 Oct 2013 13:57:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2013/10/18/handling-contract-assertions-on-windows-phone-8/</guid>
      <description><![CDATA[If you are a fan of System.Diagnostics.Contracts and use them on WP8 you may find a problem: when a...]]></description>
      <content:encoded><![CDATA[<p>If you are a fan of System.Diagnostics.Contracts and use them on WP8 you may find a problem: when a contract fails, you get no clue why. Here's how to find out.</p>
<p>Firstly lets start with a trivial repro app, take the default WP8 project, add a <em>using System.Diagnostics.Contracts</em>, add a button, then this code:</p>
<pre style="background: white; color: black; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">void</span>&nbsp;TestContract(<span style="color: blue;">int</span>&nbsp;arg)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Contract</span>.Assert(arg&nbsp;==&nbsp;42);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">void</span>&nbsp;Button_Click(<span style="color: blue;">object</span>&nbsp;sender,&nbsp;<span style="color: #2b91af;">RoutedEventArgs</span>&nbsp;e)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestContract(41);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
</pre>
<p>OK, lets F5 this in the Emulator, click the button and the process exits. If you look in the Output window you will see just this:</p>
<p><span style="font-family: courier new,courier;">The program '[2192] TaskHost.exe' has exited with code -2147483645 (0x80000003).</span></p>
<p>TaskHost.exe is not your exe, its the Silverlight host that all managed apps get run in on the phone. The exit code is instantly recognizable (to crazed folks such as myself at least) as Breakpoint, ie an int 3 (in x86) was hit somewhere. OK so that's obviously not helpful, so switch to the Native debugger (remember the debugger is like Highlander, there can only be one [type]).</p>
<p>Same repro under the native debugger is a bit more useful, you get a MessageBox and the Output window says the same thing:</p>
<p><span style="font-family: courier new,courier;">**** MessageBox invoked, title 'TaskHost.exe - Assertion failed.' ****</span></p>
<p><span style="font-family: courier new,courier;">&nbsp; Description: Assertion failed.</span></p>
<p><span style="font-family: courier new,courier;">********</span></p>
<p><span style="font-family: courier new,courier;">TaskHost.exe has triggered a breakpoint.</span></p>
<p>Well at least it stopped in the debugger this time, lets have a look at the callstack:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5305.stack_nosymbols.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5305.stack_5F00_nosymbols.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/5305.stack_nosymbols.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/350x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/5305.stack_5F00_nosymbols.PNG" alt="" border="0" /></a></p>
<p>Not terribly helpful, so lets apply the Microsoft Public Symbols to this callstack:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8561.stack_publicsymbols.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8561.stack_5F00_publicsymbols.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8561.stack_publicsymbols.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8561.stack_5F00_publicsymbols.PNG" alt="" width="452" height="182" border="0" /></a></p>
<p>Much better, and as with <a href="http://blogs.msdn.com/b/andypennell/archive/2013/06/17/native-code-on-windows-phone-8-the-assert-problem.aspx">asserts</a>, the root cause of this problem is that some code in the CLR wasn't updated for Phone, and tries to show the assert in a MessageBox which doesn't exist, so it dies. Working at Microsoft grants me certain special powers, so I used these (for good, obviously) and ran the repro with full symbols. Turns out if you go to the third frame on the callstack, and enter this into the Watch window, you can get a string containing the managed callstack of the contract failure:</p>
<p><span style="font-family: courier new,courier;">(wchar_t**)($vframe+12)</span></p>
<p>(This is for x86 ie the Emulator, I have not tried this on an ARM device)</p>
<p>You can then use the totally awesome and brilliant Text Visualizer [you might be able to guess whose idea that feature was] to see the managed callstack that caused the contract failure. Note that there are no line numbers, ah well.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4341.stack_good.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4341.stack_5F00_good.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4341.stack_good.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4341.stack_5F00_good.PNG" alt="" border="0" /></a></p>
<p>Now you know, somewhat, where the contract failure was, you can go fix it. Don't forget to switch back to the managed debugger when you want to debug that code. We can all hope this this problem (and the assert one) get fixed in the next release of the Phone tools (and it would be super-nice if we could debug both managed and native at the same time, like we have been able to do on the Desktop for a long time).</p>
<p>Please note that I know almost nothing about Contracts, I'm just working in an existing codebase that uses them, and I figured this out while trying to debug that code.</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Native Code on Windows Phone 8: The assert Problem</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/native-code-on-windows-phone-8-the-assert-problem</link>
      <pubDate>Mon, 17 Jun 2013 12:36:30 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2013/06/17/native-code-on-windows-phone-8-the-assert-problem/</guid>
      <description><![CDATA[So there you are, cranking out some new C/C++ (or CX/C++) code on Windows Phone. Its all new, so you...]]></description>
      <content:encoded><![CDATA[So there you are, cranking out some new C/C++ (or CX/C++) code on Windows Phone. Its all new, so you add asserts all over the place to make sure its working. You run your app under the debugger to see how far it gets, and boom: it exits for no obvious reason. Turns out <em>assert</em> is broken on WP8.

First off ensure you have the Native debugger set on the project's property page (it defaults to Managed). Note that unlike the Desktop you can't debug both managed and native code at once. (The phone debugger is like Highlander: there can only be one).

After a few repeated attempts where I thought I might be dreaming, I tried to figure out why my process was exiting. The tried-and-true breakpoint of

<span style="font-family: courier new,courier;">{,,kernelbase.dll}ExitProcess</span>

didn't even bind (once I had configured VS to use the Microsoft Symbol Server). Turns out that to intercept process exit in the debugger,

<span style="font-family: courier new,courier;">{,,ntdll.dll}RtlExitUserProcess</span>

is the breakpoint to use. This hit, and it turns out someone forgot to update the C-runtime code for assert to make it work on WP8, so it tries (and fails) to put up a MessageBox, then aborts the process.

Thanks to James McNellis on the VS Tools team, the way to get assert to work a <em>little</em> better is to add this to the startup of your app:

<br><span style="font-family: courier new,courier;">#ifndef NDEBUG&nbsp;&nbsp;&nbsp;</span><br><span style="font-family: courier new,courier;">signal(SIGABRT, [](int)</span><br><span style="font-family: courier new,courier;">{</span><br><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;__debugbreak();&nbsp;&nbsp;</span><br><span style="font-family: courier new,courier;">});&nbsp;</span><br><span style="font-family: courier new,courier;">#endif</span>

<p>This makes the assert stop in the debugger, at least. If you want to know what the assert message is and where in the code it is you'll have to use the callstack, but this is&nbsp;a huge improvement over the in-box behavior. Thanks James!</p>]]></content:encoded>
    </item>
    <item>
      <title>The Best Microsoft Keyboard. Ever. The Natural Keyboard Pro.</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/the-best-microsoft-keyboard-ever-the-natural-keyboard-pro</link>
      <pubDate>Wed, 03 Apr 2013 14:14:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2013/04/03/the-best-microsoft-keyboard-ever-the-natural-keyboard-pro/</guid>
      <description><![CDATA[Keyboards are a very personal thing. For those of us whose job requires their use basically all day,...]]></description>
      <content:encoded><![CDATA[<p>Keyboards are a very personal thing. For those of us whose job requires their use basically all day, every day, they are the piece of hardware that we <em>have</em> to be comfortable with. A good keyboard will stay with you for years, decades in fact, but PCs will be upgraded and so will your monitors. Microsoft has been producing keyboards since the early 90s, but in all those years there is but one keyboard that remains the best: the Natural Keyboard Pro (henceforth referred to as the NKP). Microsoft has produced one keyboard that was better before then and no keyboards since then that are remotely as good. Some may disagree with me: they are wrong. Keyboard preference is like religion: if someone disagrees with you then there can be no dissuasion.</p>
<p>The first, best Microsoft keyboard was the classic Natural Keyboard, released in 1994. It had a great feel, it was my introduction to the ergonomic layout and it was built to last. I still have four of these in my possession, and would still be using them if I hadn&rsquo;t switched to the NKP when it was released in 1999. The NKP added media keys and USB support, and although the feel wasn&rsquo;t quite as good it was a worthy successor. I am still using this keyboard today, the very same item, some fourteen years later. (I don't think I have any other piece of electronics I am still using from that long ago). Like I said, these keyboards were built very well indeed. Those of us who worship the NKP, and we are a substantial band, hoard them. They stopped being manufactured long ago, so it is important to keep the ones we have working, and to hoard any that we see being careless tossed aside by others. These keyboards can and do get filthy after years of use, which acts as a handy deterrent to anyone else wanting to use our machines. I thought my main keyboard was bad (check out the space bar in particular), but then I saw two co-workers NKPs. They looked disgusting, but still work perfectly and will only be taken from their owners from their cold, dead hands.</p>
<p>Mine: not bad for 14yr old hardware</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8507.MyNKP.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8507.MyNKP.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8507.MyNKP.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8507.MyNKP.jpg" alt="" border="0" width="100%" /></a></p>
<p>Pete's:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/6443.petesNKP.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/6443.petesNKP.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/6443.petesNKP.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/6443.petesNKP.jpg" alt="" border="0" width="100%" /></a></p>
<p>&nbsp;</p>
<p>At some point the MS keyboard team seem to lose their collective minds, frankly. They re-arranged the cursor pad from an inverted T to an actual cross, as if moving those keys around wouldn&rsquo;t matter. They introduced the incredibly annoying F-lock feature, designed to really tick off everyone that used function keys (a tradition that is still going strong, see later). There were even attempts to rearrange Insert/Delete/Home/End layout as well. Over the years the quality of the keyboards just seemed to not be there any more, though this I&rsquo;m guessing may be related to the falling MSRPs for keyboards in general. For home use I tried a Natural Ergonomic Keyboard 4000 a while ago. It&rsquo;s kind of a black-colored leather-trimmed successor to the NKP, but the feel was mushy in comparison, and the build quality was missing. After a few months of light use at home the Q-T section actually creaked when I pressed those keys, so I quit using it and gave it to my kids. I got a &ldquo;fresh&rdquo; NKP out from my stash and use that instead now. Some co-workers on my team are using these 4000 models, but they don&rsquo;t look very good as the lettering wears off the keys (I&rsquo;ve never seen any lettering worn off an NKP).</p>
<p>The keyboard madness continues somewhat into this decade: the Surface&rsquo;s Touch keyboard doesn&rsquo;t have them at all, but that&rsquo;s ok as no-one is going to use that for serious work (eg Visual Studio). However the Surface Type keyboard, which <em>is</em> intended for more serious work, does have function keys, but they default to media usage: you have to actually press the FN key every time you want to use a function key as, well, a function key. The Lenovo Yoga has a similar bizarre feature, but that can be overridden via a BIOS setting.</p>
<p>Will Microsoft ever better the NKP? What will its legions of fans do when their NKPs finally fail (or their kids pour something into them, a more likely scenario)? Will function keys still exist in a decade's time? Who knows. I think I know what keyboard I'll be using though.</p>]]></content:encoded>
    </item>
    <item>
      <title>Using Flurry Analytics on Windows Phone</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/using-flurry-analytics-on-windows-phone</link>
      <pubDate>Tue, 17 Apr 2012 13:35:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2012/04/17/using-flurry-analytics-on-windows-phone/</guid>
      <description><![CDATA[I recently added Flurry Analytics support to my application and in the few weeks it has been...]]></description>
      <content:encoded><![CDATA[<p>I recently added <a href="http://www.flurry.com">Flurry Analytics</a> support to my application and in the few weeks it has been available I have learnt huge amounts about my users, their habits, and found and fixed several bugs that no-one has even reported.</p>
<p>[This post is my personal opinion as some guy producing Windows Phone apps, and should not be taken as any kind of Microsoft endorsement for this company or its products]</p>
<p>Flurry is an analytics engine that you add to your app and then it reports as much or as little as you would like it to about your app, within about four hours. It is very easy to use (its documentation consists of a tiny text file), it works very well, and its web site lets you grok the information in an easy to use way. Oh and it is totally free!</p>
<h3>Getting Started</h3>
<p>First off sign up on the site and you can register your application and get a unique key for it. Download the Windows Phone Flurry SDK and add a reference to their assembly in your project. Add a couple of calls to your Application class per their documentation and you already have the basics: you will now know how many new users you have, how long they use your app, in what country they are, what phone and what version they are using, and lots of other information. Those of us accustomed to the glacial performance of the App Hub system and web site, with its inexplicable reporting delays, will be especially pleased with all aspects of the Flurry system. Apart from the default data collection, the data that I find most important is ship-asserts and basic analytics.</p>
<h3>Basic Analytics</h3>
<p>I report when any page in the app is visited, sometimes with an additional argument. I also report which buttons are clicked as well, to determine which features customers are using and which they are not. I use the Flurry LogEvent API for this, with arguments of &ldquo;Button&rdquo;, the page name (&ldquo;LittleWatson" in the example below) and then the name of the button itself.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/0827.buttongraph.PNG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/0827.buttongraph.PNG"><img border="0" alt="" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/0827.buttongraph.PNG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/0827.buttongraph.PNG" /></a></p>
<h3>Ship Asserts</h3>
<p>There are points in my code where I have ship-asserts: they are similar to regular asserts, but actually exist in release builds. Before Flurry they would generate emails, based on an extended version of my LittleWatson code, but I learned when I first added Flurry to the beta version of my app that even beta testers often don&rsquo;t bother to send those emails (I instrumented LittleWatson itself, see the graph above). So now I report ship-asserts, often with additional useful data, via Flurry. Flurry has a limit of 255 characters per argument but I often have data larger than that I want to see, so I use this code so I can still see all of the data:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">internal</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">void</span>&nbsp;ShipAssert(<span style="color: blue;">string</span>&nbsp;name,&nbsp;<span style="color: blue;">string</span>&nbsp;item,&nbsp;<span style="color: blue;">string</span>&nbsp;value)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">const</span>&nbsp;<span style="color: blue;">int</span>&nbsp;max&nbsp;=&nbsp;250;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">List</span>&lt;FlurryWP7SDK.Models.<span style="color: #2b91af;">Parameter</span>&gt;&nbsp;items&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">List</span>&lt;FlurryWP7SDK.Models.<span style="color: #2b91af;">Parameter</span>&gt;();
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(value&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;<span style="color: #a31515;">"&lt;null&gt;"</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(value.Length&nbsp;&lt;&nbsp;max)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;items.Add(<span style="color: blue;">new</span>&nbsp;FlurryWP7SDK.Models.<span style="color: #2b91af;">Parameter</span>(item,&nbsp;value));
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;chop&nbsp;up&nbsp;long&nbsp;values</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;chunk&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">for</span>&nbsp;(<span style="color: blue;">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;value.Length;&nbsp;i&nbsp;+=&nbsp;max)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;len&nbsp;=&nbsp;<span style="color: #2b91af;">Math</span>.Min(value.Length&nbsp;-&nbsp;i,&nbsp;max);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;key&nbsp;=&nbsp;item;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(chunk&nbsp;!=&nbsp;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;key&nbsp;=&nbsp;item&nbsp;+&nbsp;<span style="color: #a31515;">"_"</span>&nbsp;+&nbsp;chunk.ToString();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;items.Add(<span style="color: blue;">new</span>&nbsp;FlurryWP7SDK.Models.<span style="color: #2b91af;">Parameter</span>(key,&nbsp;value.Substring(i,&nbsp;len)));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;chunk++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;FlurryWP7SDK.<span style="color: #2b91af;">Api</span>.LogEvent(name,&nbsp;items,&nbsp;<span style="color: blue;">false</span>);
}
</pre>
<p>On the Flurry web site if you <em>Export as CSV</em> on an Event it will gather together the _1, _2 etc chunks for you (though not in order), you can&rsquo;t see this in the default view. Two of my ship-asserts led me to find bugs that I had no idea existed: no-one had emailed me directly or via LittleWatson, and no-one reported it in Reviews (which is a terrible way to report bugs IMHO).</p>
<h3>Version</h3>
<p>By default Flurry reports the version of the app by reading it from AppManifest.xml. However in my apps I always leave that at 1.0.0.0 and update the versions in AssemblyInfo.cs, so I set this version explicitly via their API. It is important to do this as soon as possible, else some data will be reported as coming from version 1.0.0.0 erroneously. I also change the version number reported for Debug, Beta and Trial apps, and Flurry produces handy graphs based on the version:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: green;">//&nbsp;Get&nbsp;the&nbsp;Version,&nbsp;as&nbsp;set&nbsp;in&nbsp;AssemblyInfo.cs</span>
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">string</span>&nbsp;_version;
<span style="color: blue;">internal</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">string</span>&nbsp;Version
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">get</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(_version&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;name&nbsp;=&nbsp;System.Reflection.<span style="color: #2b91af;">Assembly</span>.GetExecutingAssembly().FullName;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_version&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;System.Reflection.<span style="color: #2b91af;">AssemblyName</span>(name).Version.ToString();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;_version;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
 
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">string</span>&nbsp;FLURRY_KEY;
 
<span style="color: green;">//&nbsp;call&nbsp;this&nbsp;from&nbsp;Application_Launching&nbsp;and&nbsp;Application_Activated</span>
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">void</span>&nbsp;InitFlurry()
{
&nbsp;&nbsp;&nbsp;&nbsp;FlurryWP7SDK.<span style="color: #2b91af;">Api</span>.StartSession(FLURRY_KEY);
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;ver;
<span style="color: blue;">#if</span>&nbsp;DEBUG
&nbsp;&nbsp;&nbsp;&nbsp;ver&nbsp;=&nbsp;<span style="color: #a31515;">"D"</span>&nbsp;+&nbsp;<span style="color: #2b91af;">App</span>.Version;
<span style="color: blue;">#elif</span>&nbsp;BETA
<span style="color: gray;">&nbsp;&nbsp;&nbsp;&nbsp;ver&nbsp;=&nbsp;"B"&nbsp;+&nbsp;App.Version;</span>
<span style="color: blue;">#else</span>
<span style="color: gray;">&nbsp;&nbsp;&nbsp;&nbsp;ver&nbsp;=&nbsp;App.Version;</span>
<span style="color: blue;">#endif</span>
&nbsp;&nbsp;&nbsp;&nbsp;FlurryWP7SDK.<span style="color: #2b91af;">Api</span>.SetVersion(ver);
}
</pre>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/2538.versiongraph.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/2538.versiongraph.png"><img border="0" alt="" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/2538.versiongraph.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/2538.versiongraph.png" /></a></p>
<h3>LittleWatson</h3>
<p>Flurry also has a mechanism to report crashes, so I added it to my existing LittleWatson code. I will probably disable LittleWatson-generated emails in the next update, except in beta builds as beta users sometimes add additional information (e.g. the repro) when they send<br />them.</p>
<h3>Beta testing</h3>
<p>I think it is very important to beta test with Flurry enabled, so you can be sure you are reporting the right things in the right way, and that you find the resulting data genuinely useful before you ship your app. I use a different application key for beta versions and release versions, as I have a lot less users of the former than the latter and it is easier to detect anomalies (eg spikes in asserts) in the beta version when the amount of data (and users) is small. It also means that I can easily remove the beta data when it becomes obsolete or was generated by a bug in my reporting code in the beta itself.</p>
<h3>Opting Out</h3>
<p>As with any app that collects data from users, I highly recommend letting the user opt-out of this data collection. Although all data collected is anonymous, it is a simple courtesy to allow customers to not send it. I have a simple checkbox in my app to do this. Sure, you lose data from those who choose to opt-out, but those same users can hardly complain if you don&rsquo;t fix an issue that they didn&rsquo;t even inform you about by disabling the option.</p>
<h3>Support</h3>
<p>The support folks at Flurry were very responsive on the only occasion I had need of them: I had a beta tester in Europe telling me the app crashed on startup after a timezone change. I could not repro this myself, so I took a look at the callstacks from the App Hub (after the requisite two-day delay) and could see that the crash was in Flurry itself, so I reported it to them. In a few weeks there was an update to the Flurry SDK to fix this issue.</p>
<h3>Conclusion</h3>
<p>I highly recommend adding Flurry support to your Windows Phone application if you want to know more about your users and how they use your application, so you can fix and improve it.</p>]]></content:encoded>
    </item>
    <item>
      <title>UPNP on Windows Phone 7.5 Part 2: Invoke</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/upnp-on-windows-phone-7-5-part-2-invoke</link>
      <pubDate>Sun, 08 Jan 2012 15:19:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2012/01/08/upnp-on-windows-phone-7-5-part-2-invoke/</guid>
      <description><![CDATA[With UPnP discovery worked out, the next feature is to turn the discovery results into a Device,...]]></description>
      <content:encoded><![CDATA[<p>With UPnP discovery worked out, the next feature is to turn the discovery results into a Device, find&nbsp;a Service on that device, and to make calls on that service, ie Invoke some Actions. I'll start with the code from my test app, to give you an idea of where we are heading. I wanted to choose an Action that as many readers as possible could try, so I chose a method from ContentDirectory which is available from MediaServer, on any PC in your network once Media Sharing is enabled. The test code searches the network for media servers, and for each device it enquires for a ContentDirectory service. If it finds one, it does a call to find the top level of the "media tree" from that service. This works against Windows Media Sharing as well as my Sonos gear, plus anything DLNA-related that may be on your nextwork.</p>
<p>Note that this code requires the C# Async CTP for Phone to be installed in order to compile and run, get if from <a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&amp;id=9983">here</a>. Once installed copy the AsyncCtpLibrary_Phone.dll to your project and add it as a Reference. Note that the recommendation is for asynchronous methods to end with the word "Async", but I chose to add an underscore in there to make it totally obvious which methods are async. Feel free to totally ignore my convention. You will also need to add a <em>using from System.Threading.Tasks</em> to your cs files to use it.</p>
<p>So here is the code from the test app:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">private</span>&nbsp;<span style="color: blue;">void</span>&nbsp;buttonMedia_Click(<span style="color: blue;">object</span>&nbsp;sender,&nbsp;<span style="color: #2b91af;">RoutedEventArgs</span>&nbsp;e)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Find&nbsp;all&nbsp;Media&nbsp;Servers</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;item&nbsp;=&nbsp;<span style="color: #a31515;">"urn:schemas-upnp-org:device:MediaServer:1"</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Disable&nbsp;button&nbsp;while&nbsp;scanning</span>
&nbsp;&nbsp;&nbsp;&nbsp;buttonMedia.IsEnabled&nbsp;=&nbsp;<span style="color: blue;">false</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;ScanNetwork(item,&nbsp;(newservice)&nbsp;=&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(newservice&nbsp;!=&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestMediaBrowseAsync(newservice);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Finished&nbsp;scan,&nbsp;so&nbsp;re-enable&nbsp;button</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buttonMedia.IsEnabled&nbsp;=&nbsp;<span style="color: blue;">true</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
}
 
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">void</span>&nbsp;ScanNetwork(<span style="color: blue;">string</span>&nbsp;item,&nbsp;<span style="color: #2b91af;">Action</span>&lt;<span style="color: blue;">string</span>&gt;&nbsp;FoundCallback)
{
&nbsp;&nbsp;&nbsp;&nbsp;UPnP.<span style="color: #2b91af;">Scanner</span>&nbsp;finder&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;UPnP.<span style="color: #2b91af;">Scanner</span>();
 
&nbsp;&nbsp;&nbsp;&nbsp;finder.FindDevices(item,&nbsp;3,&nbsp;(findresult)&nbsp;=&gt;
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dispatcher.BeginInvoke(()&nbsp;=&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(findresult&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FoundCallback(<span style="color: blue;">null</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Got&nbsp;a&nbsp;response</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;newservice&nbsp;=&nbsp;UPnP.<span style="color: #2b91af;">Scanner</span>.GetSSDPLocation(findresult);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(IsNewLocation(newservice))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FoundCallback(newservice);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;});
}
 
<span style="color: blue;">async</span>&nbsp;<span style="color: blue;">void</span>&nbsp;TestMediaBrowseAsync(<span style="color: blue;">string</span>&nbsp;uri)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;device&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;UPnP.<span style="color: #2b91af;">Device</span>.Create_Async(uri);
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;contentdir&nbsp;=&nbsp;device.FindService(<span style="color: #a31515;">"urn:upnp-org:serviceId:ContentDirectory"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(contentdir&nbsp;!=&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">ContentDirectory</span>&nbsp;cd&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">ContentDirectory</span>(contentdir);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;cdresult&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;cd.Browse(<span style="color: #a31515;">"0"</span>,&nbsp;<span style="color: #a31515;">"BrowseMetadata"</span>,&nbsp;<span style="color: #a31515;">"*"</span>,&nbsp;(<span style="color: blue;">uint</span>)0,&nbsp;(<span style="color: blue;">uint</span>)1,&nbsp;<span style="color: #a31515;">""</span>);
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"Found&nbsp;device:&nbsp;"</span>&nbsp;+&nbsp;device.FriendlyName);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(cdresult.Error&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"Number={0},&nbsp;Total={1},&nbsp;UpdateID={2},&nbsp;Result={3}\n"</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cdresult.NumberReturned,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cdresult.TotalMatches,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cdresult.UpdateID,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cdresult.Result);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"ERROR:&nbsp;{0}&nbsp;on&nbsp;BrowseMetadata&nbsp;call"</span>,&nbsp;cdresult.Error.Message);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}
 
<span style="color: blue;">private</span>&nbsp;<span style="color: #2b91af;">List</span>&lt;<span style="color: blue;">string</span>&gt;&nbsp;RootDevicesSoFar&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">List</span>&lt;<span style="color: blue;">string</span>&gt;();
 
<span style="color: green;">//&nbsp;Have&nbsp;we&nbsp;seen&nbsp;this&nbsp;before?</span>
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">bool</span>&nbsp;IsNewLocation(<span style="color: blue;">string</span>&nbsp;location)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(location&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">false</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(!RootDevicesSoFar.Contains(location))
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RootDevicesSoFar.Add(location);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">true</span>;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">false</span>;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p>Note that if run on the emulator it will NOT find the media server on the same machine for me, I have to run this code on an actual device to find shared media. (I can find Sonos gear just fine from the emulator). I have no explanation for this. YMMV as they say.</p>
<p>Now we'll jump back to the bottom of the stack: Part 1s function returned us the raw results of an SSDP discovery, so from that we need to find the http: address of the description file:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: green;">//&nbsp;SSDP&nbsp;response&nbsp;handler,&nbsp;returns&nbsp;location&nbsp;of&nbsp;xml&nbsp;file,&nbsp;or&nbsp;null</span>
<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">string</span>&nbsp;GetSSDPLocation(<span style="color: blue;">string</span>&nbsp;response)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;dict&nbsp;=&nbsp;ParseSSDPResponse(response);
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(dict&nbsp;!=&nbsp;<span style="color: blue;">null</span>&nbsp;&amp;&amp;&nbsp;dict.ContainsKey(<span style="color: #a31515;">"location"</span>))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;dict[<span style="color: #a31515;">"location"</span>];
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">null</span>;
}
 
<span style="color: green;">//&nbsp;Probably&nbsp;not&nbsp;exactly&nbsp;compliant&nbsp;with&nbsp;RFC&nbsp;2616&nbsp;but&nbsp;good&nbsp;enough&nbsp;for&nbsp;now</span>
<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: #2b91af;">Dictionary</span>&lt;<span style="color: blue;">string</span>,&nbsp;<span style="color: blue;">string</span>&gt;&nbsp;ParseSSDPResponse(<span style="color: blue;">string</span>&nbsp;response)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">StringReader</span>&nbsp;reader&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">StringReader</span>(response);
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;line&nbsp;=&nbsp;reader.ReadLine();
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(line&nbsp;!=&nbsp;<span style="color: #a31515;">"HTTP/1.1&nbsp;200&nbsp;OK"</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">null</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Dictionary</span>&lt;<span style="color: blue;">string</span>,&nbsp;<span style="color: blue;">string</span>&gt;&nbsp;result&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">Dictionary</span>&lt;<span style="color: blue;">string</span>,&nbsp;<span style="color: blue;">string</span>&gt;();
 
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">for</span>&nbsp;(;&nbsp;;&nbsp;)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line&nbsp;=&nbsp;reader.ReadLine();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(line&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(line&nbsp;!=&nbsp;<span style="color: #a31515;">""</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;colon&nbsp;=&nbsp;line.IndexOf(<span style="color: #a31515;">':'</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(colon&nbsp;&lt;&nbsp;1)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">null</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;name&nbsp;=&nbsp;line.Substring(0,&nbsp;colon).Trim();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;value&nbsp;=&nbsp;line.Substring(colon&nbsp;+&nbsp;1).Trim();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(<span style="color: blue;">string</span>.IsNullOrEmpty(name))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">null</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result[name.ToLowerInvariant()]&nbsp;=&nbsp;value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;result;
}
</pre>
<p>So here is my basic Device class:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">namespace</span>&nbsp;UPnP
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">Device</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">XElement</span>&nbsp;Dom;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Uri</span>&nbsp;BaseUri;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">const</span>&nbsp;<span style="color: blue;">string</span>&nbsp;NS&nbsp;=&nbsp;<span style="color: #a31515;">"urn:schemas-upnp-org:device-1-0"</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">async</span>&nbsp;<span style="color: #2b91af;">Task</span>&lt;<span style="color: #2b91af;">Device</span>&gt;&nbsp;Create_Async(<span style="color: blue;">string</span>&nbsp;uri)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;xml&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">WebClient</span>().DownloadStringTaskAsync(uri);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;dom&nbsp;=&nbsp;<span style="color: #2b91af;">XElement</span>.Parse(xml);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(dom.GetDefaultNamespace()&nbsp;!=&nbsp;<span style="color: #a31515;">"urn:schemas-upnp-org:device-1-0"</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"Bad&nbsp;default&nbsp;namespace&nbsp;"</span>&nbsp;+&nbsp;dom.GetDefaultNamespace());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">null</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Device</span>&nbsp;device&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">Device</span>(uri,&nbsp;dom);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;device;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;Device(<span style="color: blue;">string</span>&nbsp;uri,&nbsp;<span style="color: #2b91af;">XElement</span>&nbsp;dom)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.Dom&nbsp;=&nbsp;dom;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.BaseUri&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">Uri</span>(uri,&nbsp;<span style="color: #2b91af;">UriKind</span>.Absolute);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">string</span>&nbsp;FriendlyName
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">get</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;(<span style="color: blue;">string</span>)Dom.Descendants(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"friendlyName"</span>,&nbsp;NS)).FirstOrDefault();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">string</span>&nbsp;FindService(<span style="color: blue;">string</span>&nbsp;urn)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;service&nbsp;=&nbsp;<span style="color: blue;">from</span>&nbsp;item&nbsp;<span style="color: blue;">in</span>&nbsp;Dom.Descendants(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"service"</span>,&nbsp;NS))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">where</span>&nbsp;(<span style="color: blue;">string</span>)item.Element(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"serviceId"</span>,&nbsp;NS))&nbsp;==&nbsp;urn
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">select</span>&nbsp;<span style="color: blue;">new</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServiceType&nbsp;=&nbsp;(<span style="color: blue;">string</span>)item.Element(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"serviceType"</span>,&nbsp;NS)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ServiceId&nbsp;=&nbsp;(<span style="color: blue;">string</span>)item.Element(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"serviceId"</span>,&nbsp;NS)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SCP&nbsp;=&nbsp;(<span style="color: blue;">string</span>)item.Element(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"SCPDURL"</span>,&nbsp;NS)),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ControlURL&nbsp;=&nbsp;(<span style="color: blue;">string</span>)item.Element(<span style="color: #2b91af;">XName</span>.Get(<span style="color: #a31515;">"controlURL"</span>,&nbsp;NS))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;The&nbsp;ControlURL is&nbsp;relative&nbsp;to&nbsp;the&nbsp;base&nbsp;uri</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;service&nbsp;!=&nbsp;<span style="color: blue;">null</span>&nbsp;?&nbsp;BaseUri.GetComponents(<span style="color: #2b91af;">UriComponents</span>.SchemeAndServer,&nbsp;<span style="color: #2b91af;">UriFormat</span>.Unescaped)&nbsp;+&nbsp;service.First().ControlURL&nbsp;:&nbsp;<span style="color: blue;">null</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;}
 
}
</pre>
<p>The Create_Async method is the only public way to make a device: pass it a uri that points to the device's description xml file, and it will load it into an XML document then return a new Device object to the caller. The FindService method can then be used to get a specific service from the device. There isn't much else to this class except a helper property to get the FriendlyName of the device. (Please excuse my LINQ: it is probably not remotely the best way of doing this, but my LINQ level is such that if it gets any results, its good enough for me).</p>
<p>Next is to get a Service object from the uri that the Device returns to invoke Actions upon it. I modelled this behavior on the way WCF does this, with the idea that some automated tool runs against the service's xml file and generates helper classes for each action (and the necessary types). However I don't have such a tool ready yet, so I hand-rolled a couple of actions, and coded them such that an automated tool should have no problem generating them. UPNP Actions have input args and output args, so I chose to return the output args in a strongly-typed struct for each action, along with any error information, just like WCF. So here is the Service code, which won't necessarily make sense until you see the sample action later on:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">namespace</span>&nbsp;UPnP
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">Service</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">string</span>&nbsp;Address;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">string</span>&nbsp;ServiceType;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Every&nbsp;Action&nbsp;needs&nbsp;to&nbsp;have&nbsp;an&nbsp;instance&nbsp;of&nbsp;this&nbsp;information,&nbsp;which&nbsp;encapsulates&nbsp;all&nbsp;the&nbsp;data</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;necessary&nbsp;to&nbsp;make&nbsp;a&nbsp;call</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">struct</span>&nbsp;<span style="color: #2b91af;">ActionInfo</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">string</span>&nbsp;name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">string</span>[]&nbsp;argnames;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">int</span>&nbsp;outargs;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Every&nbsp;Action&nbsp;that&nbsp;has&nbsp;any&nbsp;OUT&nbsp;arguments&nbsp;needs&nbsp;to&nbsp;have&nbsp;a&nbsp;type&nbsp;inherited&nbsp;from&nbsp;this&nbsp;one,&nbsp;containing</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;each&nbsp;OUT&nbsp;argument&nbsp;as&nbsp;a&nbsp;member</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">ActionResult</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: #2b91af;">Exception</span>&nbsp;Error;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Stores&nbsp;last&nbsp;error&nbsp;returned</span>
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Override&nbsp;this&nbsp;to&nbsp;convert&nbsp;an&nbsp;array&nbsp;of&nbsp;strings&nbsp;into&nbsp;the&nbsp;desired&nbsp;type</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">virtual</span>&nbsp;<span style="color: blue;">void</span>&nbsp;Fill(<span style="color: blue;">string</span>[]&nbsp;rawdata)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.Assert(rawdata.Length&nbsp;==&nbsp;0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;will&nbsp;fire&nbsp;if&nbsp;failed&nbsp;to&nbsp;override&nbsp;for&nbsp;non-empty&nbsp;results</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Helper&nbsp;methods</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">protected</span>&nbsp;<span style="color: blue;">bool</span>&nbsp;ParseBool(<span style="color: blue;">string</span>&nbsp;item)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;item&nbsp;==&nbsp;<span style="color: #a31515;">"1"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">protected</span>&nbsp;Service(<span style="color: blue;">string</span>&nbsp;uri,&nbsp;<span style="color: blue;">string</span>&nbsp;type)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.Address&nbsp;=&nbsp;uri;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.ServiceType&nbsp;=&nbsp;type;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">async</span>&nbsp;<span style="color: #2b91af;">Task</span>&lt;<span style="color: #2b91af;">ActionResult</span>&gt;&nbsp;Action_Async(<span style="color: #2b91af;">ActionInfo</span>&nbsp;actionInfo,&nbsp;<span style="color: blue;">object</span>[]&nbsp;args,&nbsp;<span style="color: #2b91af;">ActionResult</span>&nbsp;actionResult)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actionResult.Error&nbsp;=&nbsp;<span style="color: blue;">null</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">try</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">StringBuilder</span>&nbsp;soap&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">StringBuilder</span>(<span style="color: #a31515;">"&lt;u:"</span>&nbsp;+&nbsp;actionInfo.name&nbsp;+&nbsp;<span style="color: #a31515;">"&nbsp;xmlns:u=\""</span>&nbsp;+&nbsp;ServiceType&nbsp;+&nbsp;<span style="color: #a31515;">"\"&gt;"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;currentarg&nbsp;=&nbsp;0;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(args&nbsp;!=&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">foreach</span>&nbsp;(<span style="color: blue;">var</span>&nbsp;arg&nbsp;<span style="color: blue;">in</span>&nbsp;args)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soap.AppendFormat(<span style="color: #a31515;">"&lt;{0}&gt;{1}&lt;/{0}&gt;"</span>,&nbsp;actionInfo.argnames[currentarg++],&nbsp;FormatArg(arg));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soap.AppendFormat(<span style="color: #a31515;">"&lt;/u:{0}&gt;"</span>,&nbsp;actionInfo.name);
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;result&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;SOAPRequest_Async(<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">Uri</span>(Address),&nbsp;soap.ToString(),&nbsp;actionInfo.name);
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;TODO&nbsp;handle&nbsp;errors&nbsp;from&nbsp;services&nbsp;here</span>
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(actionInfo.outargs&nbsp;!=&nbsp;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Children&nbsp;of&nbsp;u:&lt;VERB&gt;Response&nbsp;are&nbsp;the&nbsp;result&nbsp;we&nbsp;want</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">XName</span>&nbsp;xn&nbsp;=&nbsp;<span style="color: #2b91af;">XName</span>.Get(actionInfo.name&nbsp;+&nbsp;<span style="color: #a31515;">"Response"</span>,&nbsp;ServiceType);
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;items&nbsp;=&nbsp;<span style="color: blue;">from</span>&nbsp;item&nbsp;<span style="color: blue;">in</span>&nbsp;result.Descendants(xn).Descendants()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">select</span>&nbsp;(<span style="color: blue;">string</span>)item.Value;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Check&nbsp;that&nbsp;we&nbsp;have&nbsp;at&nbsp;least&nbsp;the&nbsp;expected&nbsp;number&nbsp;of&nbsp;results</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(items.Count()&nbsp;&lt;&nbsp;actionInfo.outargs)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw</span>&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">ArgumentException</span>();
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actionResult.Fill(items.ToArray());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">catch</span>&nbsp;(<span style="color: #2b91af;">Exception</span>&nbsp;ex)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actionResult.Error&nbsp;=&nbsp;ex;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;actionResult;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">async</span>&nbsp;<span style="color: #2b91af;">Task</span>&lt;<span style="color: #2b91af;">XElement</span>&gt;&nbsp;SOAPRequest_Async(<span style="color: #2b91af;">Uri</span>&nbsp;target,&nbsp;<span style="color: blue;">string</span>&nbsp;soap,&nbsp;<span style="color: blue;">string</span>&nbsp;action)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;req&nbsp;=&nbsp;<span style="color: #a31515;">"&lt;?xml&nbsp;version=\"1.0\"&nbsp;encoding=\"utf-8\"?&gt;"</span>&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a31515;">"&lt;s:Envelope&nbsp;xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"&nbsp;s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"&gt;"</span>&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a31515;">"&lt;s:Body&gt;"</span>&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soap&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a31515;">"&lt;/s:Body&gt;"</span>&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a31515;">"&lt;/s:Envelope&gt;"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">HttpWebRequest</span>&nbsp;r&nbsp;=&nbsp;(<span style="color: #2b91af;">HttpWebRequest</span>)<span style="color: #2b91af;">HttpWebRequest</span>.Create(target);
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;We&nbsp;only&nbsp;ever&nbsp;need&nbsp;to&nbsp;do&nbsp;this&nbsp;over&nbsp;non-cellular&nbsp;networks</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r.SetNetworkPreference(<span style="color: #2b91af;">NetworkSelectionCharacteristics</span>.NonCellular);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r.Method&nbsp;=&nbsp;<span style="color: #a31515;">"POST"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">byte</span>[]&nbsp;b&nbsp;=&nbsp;<span style="color: #2b91af;">Encoding</span>.UTF8.GetBytes(req);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r.Headers[<span style="color: #a31515;">"SOAPACTION"</span>]&nbsp;=&nbsp;<span style="color: #a31515;">"\""</span>&nbsp;+&nbsp;ServiceType&nbsp;+&nbsp;<span style="color: #a31515;">"#"</span>&nbsp;+&nbsp;action&nbsp;+&nbsp;<span style="color: #a31515;">"\""</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r.ContentType&nbsp;=&nbsp;<span style="color: #a31515;">"text/xml;&nbsp;charset=\"utf-8\""</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;stream&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;r.GetRequestStreamAsync();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stream.Write(b,&nbsp;0,&nbsp;b.Length);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stream.Close();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;else&nbsp;will&nbsp;get&nbsp;"Not&nbsp;Supported"</span>
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">WebResponse</span>&nbsp;resp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">try</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resp&nbsp;=&nbsp;<span style="color: blue;">await</span>&nbsp;r.GetResponseAsync();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">catch</span>&nbsp;(System.Net.<span style="color: #2b91af;">WebException</span>&nbsp;ex)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw</span>&nbsp;ConvertException(ex);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var</span>&nbsp;st&nbsp;=&nbsp;resp.GetResponseStream();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;st.Seek(0,&nbsp;<span style="color: #2b91af;">SeekOrigin</span>.Begin);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: #2b91af;">XElement</span>.Load(st);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">UPnPException</span>&nbsp;:&nbsp;<span style="color: #2b91af;">Exception</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">int</span>&nbsp;ErrorCode
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">get</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">set</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">override</span>&nbsp;<span style="color: blue;">string</span>&nbsp;Message
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">get</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: #a31515;">"UPnP&nbsp;Error&nbsp;"</span>&nbsp;+&nbsp;ErrorCode.ToString();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">internal</span>&nbsp;UPnPException(<span style="color: blue;">int</span>&nbsp;error)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ErrorCode&nbsp;=&nbsp;error;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;If&nbsp;an&nbsp;exception&nbsp;is&nbsp;really&nbsp;a&nbsp;UPNP&nbsp;error,&nbsp;convert&nbsp;it</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: #2b91af;">Exception</span>&nbsp;ConvertException(<span style="color: #2b91af;">WebException</span>&nbsp;ex)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">WebResponse</span>&nbsp;resp&nbsp;=&nbsp;ex.Response;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;error&nbsp;=&nbsp;ex.Message;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(resp&nbsp;!=&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">using</span>&nbsp;(<span style="color: #2b91af;">Stream</span>&nbsp;respstream&nbsp;=&nbsp;resp.GetResponseStream())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;respstream.Seek(0,&nbsp;<span style="color: #2b91af;">SeekOrigin</span>.Begin);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">TextReader</span>&nbsp;reader&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">StreamReader</span>(respstream,&nbsp;<span style="color: #2b91af;">Encoding</span>.UTF8);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error&nbsp;=&nbsp;reader.ReadToEnd();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;This&nbsp;can&nbsp;be&nbsp;a&nbsp;UPnP&nbsp;error</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;TODO&nbsp;actually&nbsp;parse&nbsp;the&nbsp;XML</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;errstart&nbsp;=&nbsp;error.IndexOf(<span style="color: #a31515;">"&lt;errorCode&gt;"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">int</span>&nbsp;errend&nbsp;=&nbsp;error.IndexOf(<span style="color: #a31515;">"&lt;/errorCode&gt;"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;((errstart&nbsp;!=&nbsp;-1)&nbsp;&amp;&amp;&nbsp;(errend&nbsp;!=&nbsp;-1))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error&nbsp;=&nbsp;<span style="color: #a31515;">"UPnP&nbsp;ERROR:"</span>&nbsp;+&nbsp;error.Substring(errstart&nbsp;+&nbsp;11,&nbsp;errend&nbsp;-&nbsp;errstart&nbsp;-&nbsp;11);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">UPnPException</span>(<span style="color: blue;">int</span>.Parse(error.Substring(errstart&nbsp;+&nbsp;11,&nbsp;errend&nbsp;-&nbsp;errstart&nbsp;-&nbsp;11)));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;ex;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">string</span>&nbsp;FormatArg(<span style="color: blue;">object</span>&nbsp;obj)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;result;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(obj&nbsp;==&nbsp;<span style="color: blue;">null</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;<span style="color: #a31515;">""</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>&nbsp;<span style="color: blue;">if</span>&nbsp;(obj&nbsp;<span style="color: blue;">is</span>&nbsp;<span style="color: blue;">string</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;UPnP.<span style="color: #2b91af;">Utility</span>.XmlEscape(obj.ToString());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>&nbsp;<span style="color: blue;">if</span>&nbsp;(obj&nbsp;<span style="color: blue;">is</span>&nbsp;<span style="color: blue;">bool</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;(<span style="color: blue;">bool</span>)(obj)&nbsp;?&nbsp;<span style="color: #a31515;">"1"</span>&nbsp;:&nbsp;<span style="color: #a31515;">"0"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;<span style="color: #2b91af;">Convert</span>.ChangeType(obj,&nbsp;obj.GetType(),&nbsp;<span style="color: #2b91af;">CultureInfo</span>.InvariantCulture).ToString();
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;result;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;}
 
}
</pre>
<p>So now for the first Action, which is&nbsp;the Browse&nbsp;method on the ContentDirectory service, which you can read all about in the UPnP spec. It has six input args and four output args:</p>
<pre style="background: white; color: black; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">ContentDirectory</span>&nbsp;:&nbsp;UPnP.<span style="color: #2b91af;">Service</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;ContentDirectory(<span style="color: blue;">string</span>&nbsp;uri)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;<span style="color: blue;">base</span>(uri,&nbsp;<span style="color: #a31515;">"urn:schemas-upnp-org:service:ContentDirectory:1"</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">private</span>&nbsp;<span style="color: blue;">static</span>&nbsp;UPnP.<span style="color: #2b91af;">Service</span>.<span style="color: #2b91af;">ActionInfo</span>&nbsp;Browse_Info&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;UPnP.<span style="color: #2b91af;">Service</span>.<span style="color: #2b91af;">ActionInfo</span>()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;<span style="color: #a31515;">"Browse"</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;argnames&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: blue;">string</span>[]&nbsp;{&nbsp;<span style="color: #a31515;">"ObjectID"</span>,&nbsp;<span style="color: #a31515;">"BrowseFlag"</span>,&nbsp;<span style="color: #a31515;">"Filter"</span>,&nbsp;<span style="color: #a31515;">"StartingIndex"</span>,&nbsp;<span style="color: #a31515;">"RequestedCount"</span>,&nbsp;<span style="color: #a31515;">"SortCriteria"</span>&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outargs&nbsp;=&nbsp;4,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">Browse_Result</span>&nbsp;:&nbsp;UPnP.<span style="color: #2b91af;">Service</span>.<span style="color: #2b91af;">ActionResult</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">string</span>&nbsp;Result;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">uint</span>&nbsp;NumberReturned;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">uint</span>&nbsp;TotalMatches;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">uint</span>&nbsp;UpdateID;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">override</span>&nbsp;<span style="color: blue;">void</span>&nbsp;Fill(<span style="color: blue;">string</span>[]&nbsp;rawdata)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Result&nbsp;=&nbsp;rawdata[0];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NumberReturned&nbsp;=&nbsp;<span style="color: blue;">uint</span>.Parse(rawdata[1]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TotalMatches&nbsp;=&nbsp;<span style="color: blue;">uint</span>.Parse(rawdata[2]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UpdateID&nbsp;=&nbsp;<span style="color: blue;">uint</span>.Parse(rawdata[3]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">async</span>&nbsp;<span style="color: #2b91af;">Task</span>&lt;<span style="color: #2b91af;">Browse_Result</span>&gt;&nbsp;Browse(<span style="color: blue;">string</span>&nbsp;ObjectID,&nbsp;<span style="color: blue;">string</span>&nbsp;BrowseFlag,&nbsp;<span style="color: blue;">string</span>&nbsp;Filter,&nbsp;<span style="color: blue;">uint</span>&nbsp;StartingIndex,&nbsp;<span style="color: blue;">uint</span>&nbsp;RequestedCount,&nbsp;<span style="color: blue;">string</span>&nbsp;SortCriteria)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;<span style="color: blue;">await</span>&nbsp;<span style="color: blue;">base</span>.Action_Async(Browse_Info,&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: blue;">object</span>[]&nbsp;{&nbsp;ObjectID,&nbsp;BrowseFlag,&nbsp;Filter,&nbsp;StartingIndex,&nbsp;RequestedCount,&nbsp;SortCriteria&nbsp;},&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">Browse_Result</span>())&nbsp;<span style="color: blue;">as</span>&nbsp;<span style="color: #2b91af;">Browse_Result</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p>The Browse method bundles up the input args into an array of objects and passes them to the base class. The result is packaged into a Browse_Result class containing the output arguments, all with the same names as declared in the service's xml. To round this off here is a little utility method used by the soap encoder:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">namespace</span>&nbsp;UPnP
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">class</span>&nbsp;<span style="color: #2b91af;">Utility</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Emulate&nbsp;http://msdn.microsoft.com/en-us/library/system.security.securityelement.escape.aspx</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;SecurityElement.Escape</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public</span>&nbsp;<span style="color: blue;">static</span>&nbsp;<span style="color: blue;">string</span>&nbsp;XmlEscape(<span style="color: blue;">string</span>&nbsp;input)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return</span>&nbsp;input.Replace(<span style="color: #a31515;">"&amp;"</span>,&nbsp;<span style="color: #a31515;">"&amp;amp;"</span>).Replace(<span style="color: #a31515;">"&lt;"</span>,&nbsp;<span style="color: #a31515;">"&amp;lt;"</span>).Replace(<span style="color: #a31515;">"&gt;"</span>,&nbsp;<span style="color: #a31515;">"&amp;gt;"</span>).Replace(<span style="color: #a31515;">"\""</span>,&nbsp;<span style="color: #a31515;">"&amp;quot;"</span>).Replace(<span style="color: #a31515;">"\'"</span>,&nbsp;<span style="color: #a31515;">"&amp;apos;"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p>There's quite a lot of code here, following user request all the source and a simple sample are on <a href="http://wpupnp.codeplex.com/">Codeplex </a>now.</p>]]></content:encoded>
    </item>
    <item>
      <title>Waiting for Your Windows Phone Beta App</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/waiting-for-your-windows-phone-beta-app</link>
      <pubDate>Fri, 30 Dec 2011 07:55:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2011/12/30/waiting-for-your-windows-phone-beta-app/</guid>
      <description><![CDATA[Publishing a beta version of a WP7 app is both exciting (&ldquo;does it work on someone else&rsquo;s...]]></description>
      <content:encoded><![CDATA[<p>Publishing a beta version of a WP7 app is both exciting (&ldquo;does it work on someone else&rsquo;s phone?&rdquo;) and frustrating (&ldquo;when is it going to actually show up?&rdquo;). Having submitted maybe five beta versions over the holidays I wanted to improve the latter problem.</p>
<p>The beta submission process today looks like this:</p>
<ul>
<li>Submit your app in the App Hub as a beta app</li>
<li>At some random point in time (1-8 hours in my experience) you receive an email from Windows Phone Marketplace claiming it is available to your beta testers, including a zune: link</li>
<li>You try the zune: link and it says &ldquo;sorry this item is no longer available in this marketplace&rdquo; and you curse</li>
<li>At some other random point in time (2-24 hours in my experience) the link will start working</li>
<li>You send email to your beta testers for them to beat on your app</li>
</ul>
<p>What follows is a simple PC tool I wrote that at least will tell me when the download is available. There isn&rsquo;t much I can do about the delay in getting the email, but once the email arrives I copy the appid out of the link (the appid is the guid portion), paste it into this fugly WinForms app, and click Wait.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4643.DudeWheresMyApp.JPG" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4643.DudeWheresMyApp.JPG"><img border="0" alt="" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4643.DudeWheresMyApp.JPG" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4643.DudeWheresMyApp.JPG" /></a></p>
<p>I won&rsquo;t insult you (or embarrass myself) by showing you the WinForms code, but here is the crucial part:</p>
<pre style="background: white; color: black; font-family: Consolas;"><span style="color: blue;">private</span>&nbsp;<span style="color: blue;">void</span>&nbsp;button1_Click(<span style="color: blue;">object</span>&nbsp;sender,&nbsp;<span style="color: #2b91af;">EventArgs</span>&nbsp;e)
{
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Guid</span>&nbsp;appid;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;guid&nbsp;=&nbsp;textBox1.Text;
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if</span>&nbsp;(!<span style="color: #2b91af;">Guid</span>.TryParse(guid,&nbsp;<span style="color: blue;">out</span>&nbsp;appid))
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">MessageBox</span>.Show(<span style="color: #a31515;">"Not&nbsp;a&nbsp;valid&nbsp;guid"</span>);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;uri&nbsp;=&nbsp;<span style="color: #a31515;">"http://catalog.zune.net/v3.3/en-us/apps/"</span>&nbsp;+&nbsp;guid&nbsp;+&nbsp;<span style="color: #a31515;">"?store=Zest&amp;clientType=WinMobile+7.1"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.Text&nbsp;=&nbsp;<span style="color: #a31515;">"Waiting&nbsp;now..."</span>;
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">for</span>&nbsp;(;&nbsp;;&nbsp;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">try</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string</span>&nbsp;result&nbsp;=&nbsp;<span style="color: blue;">new</span>&nbsp;<span style="color: #2b91af;">WebClient</span>().DownloadString(uri);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"Download&nbsp;found&nbsp;at&nbsp;"</span>&nbsp;+&nbsp;<span style="color: #2b91af;">DateTime</span>.Now.ToString());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">catch</span>&nbsp;(<span style="color: #2b91af;">Exception</span>&nbsp;ex)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;Wait&nbsp;for&nbsp;10&nbsp;minutes</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.Threading.<span style="color: #2b91af;">Thread</span>.Sleep(10&nbsp;*&nbsp;60&nbsp;*&nbsp;1000);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">this</span>.Text&nbsp;=&nbsp;<span style="color: #a31515;">"Found&nbsp;it!"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</pre>
<p>Of course you could extend this idea to emit a loud beeping sound, or send yourself or your beta testers an email, or even turn it into a Phone app with a background agent and Live Tile notification. (If you do the latter, be sure to send me a beta!).</p>
<h3>7.0 Apps</h3>
<p>I only do Mango apps myself these days: if you are still creating 7.0 apps, change the url to use 3.2 and 7.0 instead of 3.3 and 7.1.</p>
<h3>Detecting Non Beta Apps</h3>
<p>This same idea can also be used to detect when an update to a non-beta app is available, but it requires a little more work: you will need to parse the string returned from DownloadString for the current version, and trigger your alarm only when it has changed. This is left as an exercise for the reader.</p>
<p>In an ideal world the App hub would publish beta apps in minutes from submission, but until it does I hope this tool proves useful.</p>
<p></p>]]></content:encoded>
    </item>
    <item>
      <title>Car Head Unit Compatibility with Windows Phone 7.5 Mango</title>
      <link>https://docs.microsoft.com/archive/blogs/andypennell/car-head-unit-compatibility-with-windows-phone-7-5-mango</link>
      <pubDate>Tue, 27 Sep 2011 07:43:00 GMT</pubDate>
      <dc:creator><![CDATA[Andy-Pennell]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andypennell/2011/09/27/car-head-unit-compatibility-with-windows-phone-7-5-mango/</guid>
      <description><![CDATA[I recently had a desperate need to upgrade the audio system in my car, and wanted a good...]]></description>
      <content:encoded><![CDATA[<p>I recently had a desperate need to upgrade the audio system in my car, and wanted a good Mango-compatible system. I learnt a lot about Bluetooth and what to look for to get a great in-car experience, which I am sharing here. Please note these are my personal findings and opinions, none of this should be taken as a Microsoft-approved endorsement of anything.</p>
<p>Earlier in the year I picked up a 2006 Cayenne and for audio and navigation it came with <a href="http://www.porsche.com/usa/accessoriesandservices/personalisation/tequipment/cayenne-1stgeneration/audioandcommunication/pcm">PCM</a> (Porsche Communication Management System). This is an over-priced, deeply underperforming piece of equipment. I shall save my large list of rants about it for another day, suffice to say it was a $4k or so option when new and offered no Bluetooth and no auxiliary audio inputs, to name but two critical missing features. This steaming pile of technology was fitted to Porsches from 2004 to 2009 and I can only hope that whoever was responsible for it is no longer with the company (or Harman/Becker who were the offending OEM). I suffered with it for many months, using a Zune RF car kit as the only way to get music into it, and by breaking the law using my phone directly on occasion. Finally with the release of Mango I knew that the expanded Bluetooth features of the phone should combine with a good head unit to solve all of my audio woes, and more, so I went shopping.</p>
<p>It became obvious very quickly that Apple totally owns the in-car audio experience right now: every head unit has as a built-in or optional feature of iPhone/iPod support. The power of that big proprietary connecter is huge, and that combined with the massive worldwide presence of the devices has resulted in excellent support.&nbsp; How can a Mango phone possibly complete, with a lowly micro-usb jack and a tiny percentage of Apple&rsquo;s market share? Without any wires, that is how, via the Bluetooth standard, as you will see.</p>
<h3>Andy's Bluetooth Tests</h3>
<p>Here are the Bluetooth tests that I performed and that I recommend you perform in choosing your next head unit (or new vehicle for that matter) if these things are similarly important to you:</p>
<ul>
<li>First off pair your phone with the head unit under test. Get someone to call you, and verify the audio is routed over the speakers, and you can reply. This I call the Voice Test, and requires the most basic BT support from a head unit.</li>
<li>Next play the radio on the unit, and get someone who is in your Contact list to send you a text message. This should cause the radio to mute, and then the phone should announce via the car speaker something like &ldquo;Message from Chris&rdquo;. You can reply &ldquo;Read it&rdquo; and the message is read. You can also reply to text messages by speaking. This I call the Text Test, and for me is the single most important BT test, and one of my most favorite Mango features.</li>
<li>I remain a Zune fan (go <a href="http://blogs.msdn.com/b/andypennell/archive/2008/09/30/zune-installation-in-a-boxster.aspx">here</a> for the installation of same into my Boxster) but of course I can fit a bunch of tunes on my phone too. At minimum I needed a head unit with an analog audio input (so I could connect via the phone&rsquo;s headphone jack), but a stretch goal for this plan was to play music from my phone wirelessly, over Bluetooth. This I call the Music Test.</li>
<li>When music is being streamed from your phone for the Music Test, it is obviously useful to be able to Pause, Next Track etc, in my case from the steering wheel controls. I call this the Control Test.</li>
<li>When you use an Apple device and cable it to one of these head units you are able to see what is being played (the artist name, track etc). This is possible with Bluetooth too, and I call this the Metadata Test.</li>
</ul>
<p>Now to get a bit technical: each feature used in my Tests require one or more <a href="http://en.wikipedia.org/wiki/Bluetooth_profile">Bluetooth profiles</a>, and different head units support different profiles (and different versions of those profiles). I do not pretend to be an expert in this, but to handle my tests above a head unit must support the following protocols:</p>
<ul>
<li>Voice Test: HFP (Hands Free Profile)</li>
<li>Text Test: Unknown profile</li>
<li>Music Test: A2DP (Advanced Audio Distribution Profile)</li>
<li>Control Test: AVRCP 1.3 (Audio/Video Remote Control Profile)</li>
<li>Metadata Test: AVRCP 1.3</li>
</ul>
<p>Note that there are other Bluetooth features that I don&rsquo;t care about so did not test, such as phone book download (which uses the OPP Profile). Also missing is making a phone call, I very seldom do that from the car, but if that is important to you then I urge you to test it before you buy.</p>
<p>Some things can be immediately determined from a spec sheet, for example if the unit does not support A2DP then obviously it cannot pass the Music Test&nbsp; and the Control and Metadata tests are irrelevant. The hardest one to determine is the Text Test: it isn&rsquo;t clear which profile(s) it requires and it is the hardest test to pass (as you&rsquo;ll see in my results later). Reading the specification sheets for a given head unit will tell you little about how well it will work with a Mango phone, you have to either try it yourself, or find someone who has already tried it. A list of head units that failed my tests can be found later, but to skip to the chase I highly recommend three JVC units.</p>
<h3>Decision Time: JVC</h3>
<p>So after trying out several options, I ended up with the JVC KW-NT50HDT which is a double-DIN touch screen LCD screen system with navigation. The KW-NT30HDT is probably a fiscally more sensible choice: the only difference is lifetime traffic data (which it gets from the HD radio data stream), and so far at least in the Seattle area this has not proved to be as useful as the <a href="http://www.wsdot.wa.gov/traffic/seattle/">DOT bitmap</a> which has earned a shortcut on the home screen of my phone. This was connected to the existing amplifier and speaker system, as well as the steering wheel controls, and this is no mean feat. One of the many annoying features of the PCM system is that it is entirely digital using fiber optics to communicate between devices: this makes it overly difficult to get any kind of analog signal into or out of the system. Fortunately it is not impossible, if you go to an installer who knows what they are doing. It&rsquo;s not cheap though.</p>
<p>I must say I am very happy with the JVC+Mango experience. First of course I get my favorite music and playlists onto my phone via the desktop Zone software, then start playing something on the phone and pause it. After I start the car it takes a few seconds for the Bluetooth to pair, then I can select BT Audio then Play, and that is it: wireless high quality music. The JVC displays all of the metadata (except the album art which is a limitation of <a href="https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=66609">AVRCP</a>) and I can use my steering wheel buttons to navigate tracks. It even displays the state of my phone&rsquo;s battery, which is handy as streaming audio is going to put some additional load on it for sure. When I turn the ignition off the phone automatically pauses my music, until I return the next day when it will resume where it left off. Just like the Zune in my Boxster, but wirelessly. Bliss:<br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4478.JVCMedia%20(800x600).jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4478.JVCMedia-_2800_800x600_2900_.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/4478.JVCMedia%20(800x600).jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/400x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/4478.JVCMedia-_2800_800x600_2900_.jpg" alt="" border="0" /></a></p>
<p>There are other features of the JVC not directly related to Mango that I also appreciate, such as a USB port on the front which can charge your phone or take a memory stick full of music, and it shows all metadata including album art in this case. It also has a rear AV input which I use for my Zune, an input for backup camera that I had installed at the same time, and a very nice navigation system from Navteq. All of this is fast and easy to use, i.e. it bears no similarity to the PCM system it replaced: I had to get the instructions out just to use the radio on the PCM.</p>
<p>Life is not entirely perfect in Mango-land for Bluetooth though and there remain a few gaps. For example while the built-in media player works very well over Bluetooth, third-party applications that use a BackgroundAudioAgent do not work so smoothly. (You can tell which apps these are as you can control Play/Pause/Next/Prev with the lock screen down using the Universal Volume Control or UVC). The UVC is not really connected to Bluetooth, so while you can sometimes Pause/Play those apps from the head unit (and steering wheel) you cannot change tracks. You also get no metadata at all sent to the head unit. For tethered Apple hardware the JVC supports iheartradio and many other units support Pandora by building it into the head unit itself, which is clearly not a sustainable model even for Apple, so Microsoft has an opportunity to improve the support for arbitrary 3rd party applications over Bluetooth here:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8875.JVCStream%20(800x600).jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8875.JVCStream-_2800_800x600_2900_.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/31/07/8875.JVCStream%20(800x600).jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/400x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-31-07/8875.JVCStream-_2800_800x600_2900_.jpg" alt="" border="0" /></a></p>
<p>Although I installed my Zune myself in the Boxster, I was not about to attempt to install this lot into the Cayenne, so I went to <a href="http://www.benchmarkmotoring.com/">Benchmark Motoring</a> for it. I highly recommend them, speak to Justin if you pay them a visit. For reference all my testing was done on a Dell Venue Pro, which had a deservedly poor Bluetooth reputation before the BSP update that Dell shipped around the NoDo timeframe. Since that update mine has been rock solid.</p>
<p>A final note about Zune: sadly SoundGate products are <a href="http://www.kicker.com/soundgate">no more</a>, so connecting a Zune to a head unit via a quality connection is even more of a challenge. Fortunately I did score one of the last ZNCBLPAK kits on the planet and had it installed along with everything else. Although my Zune for music is more or less obsolete now in the Cayenne, it can do video through the JVC which might keep the kids occupied for a while on occasion.</p>
<h3>Known Andy-Compatible Units</h3>
<ul>
<li>JVC KW-NT50HDT</li>
<li>JVC KW-NT30HD</li>
<li>JVC KD-A925BT</li>
</ul>
<h3><br />Known Andy-Incompatible Units</h3>
<ul>
<li>Alpine INA-W900BT: Voice Test passed. Text Test failed, no A2DP support so all other tests N/A</li>
<li>Kenwood DNX9980HD: Voice Test passed. Text Test failed, except while running the Music Test! (which passed)</li>
<li>Kenwood KIV-BT900: Voice Test passed, Text Test failed. Can directly control a wired Zune though!</li>
<li>Dension Gateway 500: Every test fails (even Voice!) except Music.</li>
</ul>
<h3>A Comment about Comments</h3>
<p>Please don&rsquo;t post comments asking me if device XYZ is compatible: everything I know about compatibility is listed here. On the other hand if you have tried Mango on some other hardware and can share your test results in a comment that would be much appreciated.</p>]]></content:encoded>
    </item>
  </channel>
</rss>