Hi Folks,
In this next blog post we’ll review how to automate the Windows Phone 7 Emulator and what are some real-world usages this technique.
This blog post is the second blog post in a series of unrelated advanced Windows Phone 7 blog posts.
The previous blog post in this series is “Windows Phone 7 – Unlocked ROMs”.
Why is Windows Phone 7 Automation Important?
Currently, the Windows Phone 7 Emulator can only launch developer packages (XAPs) from inside Visual Studio 2010.
Meaning, if we’ve got a XAP we can’t get the emulator to load it without Visual Studio.
As we’ve seen in a previous blog post the emulator can be launched and even installed on machines that do not have Visual Studio. More on that here.
Let’s consider that in order to load XAPs into the Emulator we currently have to use Visual Studio.
The problem is – How do we load XAPs into WP7 Emulators and Devices without using Visual Studio?
So how does our Build Server run nightly tests? Or how do our customers see our applications?
Customers and Build servers don't have Visual Studio 2010 installed.
So, how can we deploy XAPs to an emulator running on these machines?
Quit your Jibber-Jabber, show me how to automate the WP7 emulator!
Let’s create a new solution with a Console Application and a Windows Phone 7 Project.
We’ll use the ConsoleApplication as our automation harness that can run on any computer with the .Net framework.
The WindowsPhone Application is the application whose deployment we’ll automate.
The CoreCon 10 WP7 API
The secret sauce for WP7 Automation is the CoreCon API.
These CoreCon DLLs have been in deep hibernation since 2007 when they last got a major upgrade.
For WP7 Tools the CoreCon API has been updated and are used by Visual Studio 2010 when deploying applications to WP7 devices and emulators.
Let’s add a reference to Microsoft.SmartDevice.Connectivity.dll from the CoreCon API.
On a x64 developer box, the DLL can be found at: C:\Program Files (x86)\Common Files\microsoft shared\Phone Tools\CoreCon\10.0\Bin\Microsoft.Smartdevice.Connectivity.dll
Now that we have the CoreCon DLL reference in place, we can start automating WP7.
Here’s the User Experience we’re aiming for in this demo:
We’ll have to build the following workflow:
We’ll start off by getting an instance of the WP7 CoreCon SDK instance to work with:
// Get CoreCon WP7 SDK
DatastoreManager dsmgrObj = new DatastoreManager(1033);
Platform WP7SDK = dsmgrObj.GetPlatforms().Single(p => p.Name == "New Windows Mobile 7 SDK");
Next up we’ll have to get a reference to either a WP7 physical device or a WP7 Emulator.
This demo will use the emulator, but if we set “useEmulator = false” this demo will try and connect to a physical WP7 device.
bool useEmulator = true;
Device WP7Device = null;
if (useEmulator)
WP7Device = WP7SDK.GetDevices().Single(d => d.Name == "Windows Phone 7 Emulator");
else
WP7Device = WP7SDK.GetDevices().Single(d => d.Name == "Windows Phone 7 Device");
Now we’ll launch the WP7 Emulator/Device.
Note that the default WP7 Emulator Image is the one we’ll launch into, and in a previous blog post I’ve explained how to use the Unlocked ROM as the default ROM.
// Connect to WP7 Emulator / Device
Console.WriteLine("Connecting to Windows Phone 7 Emulator/Device...");
WP7Device.Connect();
Console.WriteLine("Windows Phone 7 Emulator/Device Connected...");
Next, we’ll have to check if our application is already installed, and if it is – we’ll uninstall it.
Since the the UpdateApplication Method in the current CoreCon API doesn’t work, this is our best bet on easily updating installed apps.
In order to Install or Uninstall our application, we’ll need it’s Application Product Identifier (GUID).
We can get the application GUID from the Properties/WMAppManifest.xml file.
We’ll write the code to check if an application is installed, and if it is – we’ll uninstall it.
Guid appID = new Guid("{5e75bba1-fbf6-463c-94ac-fa4a78f8fd12}");
RemoteApplication app;
if (WP7Device.IsApplicationInstalled(appID))
{
Console.WriteLine("Uninstalling sample XAP to Windows Phone 7 Emulator/Device...");
app = WP7Device.GetApplication(appID);
app.Uninstall();
Console.WriteLine("Sample XAP Uninstalled from Windows Phone 7 Emulator/Device...");
}
At this point, we’ll install our XAP.
In order to do that, we’ll need 3 pieces of information:
1. The location of our XAP.
2. The Application GUID.
3. The location of the application icon.
// Install XAP
Console.WriteLine("Installing sample XAP to Windows Phone 7 Emulator/Device...");
app = WP7Device.InstallApplication(
appID,
appID,
"NormalApp",
@"D:\visual studio 2010\Projects\ConsoleApplication1\WindowsPhoneApplication1\ApplicationIcon.png",
@"D:\visual studio 2010\Projects\ConsoleApplication1\WindowsPhoneApplication1\Bin\Debug\WindowsPhoneApplication1.xap");
Console.WriteLine("Sample XAP installed to Windows Phone 7 Emulator...");
The last step is to actually launch our application.
// Launch Application
Console.WriteLine("Launching sample app on Windows Phone 7 Emulator...");
app.Launch();
Console.WriteLine("Launched sample app on Windows Phone 7 Emulator...");
We’re done. And indeed when we run our sample application the app runs as expected:
So What is WP7 Automation good for?
There’s a lot of good use cases: Automating Unit tests, Loading Nightly builds into the emulator and even letting customers use the WP7 Emulator.
There’s really a plethora of reasons and situations WP7 Automation can prove useful in and it’s up to you to figure it out for your projects.
For instance in Vertigo, we’re using the WP7 Automation harness to run the WP7 Emulator on multi-touch laptops that our customers have.
So Vertigo’s customers, as part of an iterative process, get access to our current build without having to install or use Visual Studio 2010.
Access to Isolated Storage
One use-case in particular interests me, which is automating running all unit tests in the project after a Continuous Integration nightly build.
We can pretty much do all of the work to automate WP7 tests using WP7 Automation we’ve seen so far, but how do we get results into application? Or get instructions in?
The easiest method I can think off is communicate through cold storage – read & write files into Isolated Storage.
We’ll start off by writing a file into our WP7 Application IsoStore.
Remember that this code executes in the WP7 Emulator/Device.
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
using (var sw = new StreamWriter(isoStore.OpenFile("Foo.txt", FileMode.OpenOrCreate, FileAccess.Write)))
{
sw.Write("Hello WP7! (Written from WP7 IsoStore, read by Console Harness!)");
}
}
Basically, We’ve written a Foo.txt file into our IsoStore and added some text into it.
Next, we would have used the RemoteIsolatedStorage class and get access to the emulator IsoStore, but that has not been enabled yet in CoreCon10.
We would have used RemoteApplication.GetIsolatedStorage() but that has not been implemented yet.
We’ll use a FileDeployer class instead.
There’s some reflection magic associated with getting a copy of a FileDeployer class that can be used for our application.
Thread.Sleep(10000);
//app.GetIsolatedStore(); <-- Throws NotImplementedException
object ConManServer = WP7Device.GetType().GetField("mConmanServer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(WP7Device);
FileDeployer f = (FileDeployer)typeof(FileDeployer).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0].Invoke(new object[] { ConManServer });
Next, we’ll copy the Foo.txt file from the application isolated storage into our local binary folder and read it into the console:
f.ReceiveFile(@"\Applications\Data\" + appID + @"\data\isolatedstore\Foo.txt", @"\Foo.txt");
Console.WriteLine("\t" + File.ReadAllText(@"foo.txt"));
When we run this sample we can see the text written from IsoStore showing up on our console.
And we’re done, we’ve esteblished a two-way communication channel between WP7 Emulator/Device and Managed .net code.
Console WP7 Automation Harness Source Code
Here’s the source code we’ve built in this project:
// Get CoreCon WP7 SDK
DatastoreManager dsmgrObj = new DatastoreManager(1033);
Platform WP7SDK = dsmgrObj.GetPlatforms().Single(p => p.Name == "New Windows Mobile 7 SDK");
// Get Emulator / Device
bool useEmulator = true;
Device WP7Device = null;
if (useEmulator)
WP7Device = WP7SDK.GetDevices().Single(d => d.Name == "Windows Phone 7 Emulator");
else
WP7Device = WP7SDK.GetDevices().Single(d => d.Name == "Windows Phone 7 Device");
// Connect to WP7 Emulator / Device
Console.WriteLine("Connecting to Windows Phone 7 Emulator/Device...");
WP7Device.Connect();
Console.WriteLine("Windows Phone 7 Emulator/Device Connected...");
Guid appID = new Guid("{5e75bba1-fbf6-463c-94ac-fa4a78f8fd12}");
RemoteApplication app;
if (WP7Device.IsApplicationInstalled(appID))
{
Console.WriteLine("Uninstalling sample XAP to Windows Phone 7 Emulator/Device...");
app = WP7Device.GetApplication(appID);
app.Uninstall();
Console.WriteLine("Sample XAP Uninstalled from Windows Phone 7 Emulator/Device...");
}
// Install XAP
Console.WriteLine("Installing sample XAP to Windows Phone 7 Emulator/Device...");
app = WP7Device.InstallApplication(
appID,
appID,
"NormalApp",
@"D:\visual studio 2010\Projects\ConsoleApplication1\WindowsPhoneApplication1\ApplicationIcon.png",
@"D:\visual studio 2010\Projects\ConsoleApplication1\WindowsPhoneApplication1\Bin\Debug\WindowsPhoneApplication1.xap");
Console.WriteLine("Sample XAP installed to Windows Phone 7 Emulator...");
// Launch Application
Console.WriteLine("Launching sample app on Windows Phone 7 Emulator...");
app.Launch();
Console.WriteLine("Launched sample app on Windows Phone 7 Emulator...");
Console.WriteLine("Reading Foo.txt Isolated Storage file:");
Thread.Sleep(10000);
//app.GetIsolatedStore(); <-- Throws NotImplementedException
object ConManServer = WP7Device.GetType().GetField("mConmanServer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(WP7Device);
FileDeployer f = (FileDeployer)typeof(FileDeployer).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0].Invoke(new object[] { ConManServer });
f.ReceiveFile(@"\Applications\Data\" + appID + @"\data\isolatedstore\Foo.txt", @"\Foo.txt");
Console.WriteLine("\t" + File.ReadAllText(@"foo.txt"));
Console.ReadLine();
Fin
In this blog post we’ve talked about why Automating the WP7 Emulator and Device deployment process is important and how to go about doing that.
Hopefully now you know how to automate WP7 XAP Deployment.
In the next blog post in this series we’ll talk about setting up a Continuous Integration build for WP7.
Leave a Comment
Are there any other use cases for WP7 Automation?
Are you planning on using this technique somewhere?
Was this a good blog post? Sound off in the comments.
Sincerely,
-- Justin Angel
Comments