Windows Phone 7 and WCF: a great combination, but can be tricky to get going. Here is how I did it.

Internally at Microsoft there has been quite a bit of interest with developers wanting to produce WCF (Windows Communication Framework) applications for Windows Phone 7. Bizarrely I turned out to be one of them, despite having no clue about WCF a month ago. While I can hardly be called an expert in the subject or even an intermediate WCF user, I did get everything working. However lots of internal folks failed, and I am guessing more than a few external folks will also, so here is a quick run-through of creating a WCF Stand Alone Host application on the PC and a WCF Client on the phone. I did this with the RTM tools for this, but the same steps should also work fine on the beta release (though not the older CTP). This might not work on Visual Studio Express though, I don’t have that to test on, sorry, I use Ultimate and I know VS Pro works too.

Making a WCF Server (from a WinForms app)

In Visual Studio do File / New Project / Visual C# / Windows / Windows Forms Application and name it WCFSimpleHost.

Choose Project / Properties and on the Application tab change Target Framework from “.NET Framework 4 Client Profile” to “.Net Framework 4” and say Yes to the confirmation dialog. This is required as we need access to the next assembly:

Project /Add References /  .Net /  System.ServiceModel (ensure it is version 4.0.0.0)

Project / Add New Item / Visual C# Items / Interface / ITest.cs

Should look like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ServiceModel;

 

namespace WCFSimpleHost

{

    [ServiceContract]

    interface ITest

    {

        [OperationContract]

        string Add(int x, int y);

    }

}

 

A trivial interface that adds two integers and returns the result as a string. Why not? With the interface defined, lets implement it:

Project / Add / New Item / Visual C# Items / Class / Test.cs looks like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace WCFSimpleHost

{

    class Test : ITest

    {

        public string Add(int x, int y)

        {

            int total = x + y;

            return total.ToString();

        }

    }

}

 

From Solution Explorer double click on Form1.cs and the Designer will open. Double-click on the Form and in the Form1_Load method add

using System.ServiceModel;

using System.ServiceModel.Description;

 

 

and this:

        private ServiceHost HostProxy;

 

        private void Form1_Load(object sender, EventArgs e)

        {

            string address = "http://localhost:8001/test";

            HostProxy = new ServiceHost(typeof(Test), new Uri(address));

 

            // Enable metadata publishing.

            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();

            smb.HttpGetEnabled = true;

            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;

            HostProxy.Description.Behaviors.Add(smb);

 

            // Open the ServiceHost to start listening for messages. Since

            // no endpoints are explicitly configured, the runtime will create

            // one endpoint per base address for each service contract implemented

            // by the service.

            try

            {

                HostProxy.Open();

                MessageBox.Show("The service is ready at " + address);

            }

            catch (AddressAccessDeniedException)

            {

                MessageBox.Show("You need to reserve the address for this service");

                HostProxy = null;

            }

            catch (AddressAlreadyInUseException)

            {

                MessageBox.Show("Something else is already using this address");

                HostProxy = null;

            }

            catch (Exception ex)

            {

                MessageBox.Show("Something bad happened on startup: " + ex.Message);

                HostProxy = null;

            }

        }

 

(By the way I didn’t make this up, I stole it from http://msdn.microsoft.com/en-us/library/ms731758.aspx “How to: Host a WCF Service in a Managed Application”)

If you Build this it should be ok. If you Run it you will get the AddressAccessDeniedException message. Exit the app, then open a CMD window as Administrator and type

netsh http add urlacl url=http://+:8001/test user=Everyone

(I got this from http://msdn.microsoft.com/en-us/library/ms733768.aspx : if you are running something older than Vista you have lost my respect and you’ll need to read the article and take different steps). Note the port number and the Uri must match the one in your code, and a tighter ACL list than “Everyone” would be advised if you are running this on a network with people you don’t entirely trust. As I share my home network with my wife, my Sonos and my TiVo, that isn’t an issue for me.

Now run the application and you should get the success message, click on OK and leave the application running. To check it works, fire up a web browser and enter an URL such as

http://localhost:8001/test

and you should see an HTML page showing “Test Service”. However ignore the contents of it, it doesn’t quite apply to Silverlight for the Phone.

Client Side

Fire up another instance of Visual Studio, it makes things a lot easier to use a separate instance and a separate project for Client and Server. Do File/New/Project /Visual C#/Silverlight for Windows Phone/Windows Phone Application and call it WCFSimpleClient.

Project / Add Service Reference and enter the Uri of the service, in our case http://localhost:8001/test and click Go. Assuming you left the server exe running from above, it will show you the Test contract and class. Change the Namespace to TestService and click on the Advanced… button. I recommend changing Collection type: to System.Array and unchecking “Reuse types in Referenced Assemblies”. I don’t have a specific reason for this, except that others have recommended it and it works for me. Click OK from the Settings dialog and OK from the Add Service dialog. This will generate a new item in the Project under Service References called TestService.

Create a Button in the middle of the design surface and double-click on it. Add this:

using WCFSimpleClient.TestService;

using System.Diagnostics;

using System.ServiceModel;

 

And this:

        private TestClient client;

 

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            if (client == null)

            {

                client = new TestClient(new System.ServiceModel.BasicHttpBinding(), new EndpointAddress("http://localhost:8001/test"));

 

                client.AddCompleted += new EventHandler<AddCompletedEventArgs>(client_AddCompleted);

            }

 

            client.AddAsync(30, 12);

        }

 

        void client_AddCompleted(object sender, AddCompletedEventArgs e)

        {

            if (e.Error==null)

            {

                Debug.WriteLine("The answer is {0}", e.Result);

            }

        }

 

Then F5 to build and run this under the emulator or a tethered device. Click the button and look in the Output window: you should see the result.

WCF from Silverlight is all asynchronous, and this took some getting used to for me. Getting out arguments and error checking is interesting, you can see the beginnings of this in the example. The key is to always have a xxxCompleted event handler defined, even if it does nothing, so that if the server goes away your app doesn’t too.

WCF Untethered

So now it is time to try this untethered (assuming you have an actual device). Be sure to set up wifi on the phone for your home network, then change the code on the client to be something like

string address = "http://mymachinename:8001/test";

(no server-side changes required). I’ll spare you the suspense but it won’t work. Took me hours to figure this out, but the problem is the Windows Firewall blocks the request from the phone, and the solution is not to add your WCF server exe to the exception list: instead add the port number (8001 in this case) to the Inbound exception list, over TCP. Now it should work, you can verify by firing up the browser on the phone and entering the http address which should get you the html test page.

Note that if you are at work and running on a Active Directory Domain then this might not work untethered: I know it doesn’t here at Microsoft, likely due to some combination of IPSec, WiFi security and not using FQDN. Good luck with that.

Note that every time you change the exported interface in (ITest.cs) you must re-run the server and in the client project right click on the TestService icon and “Update Service Reference” then rebuild the client app. (Once you ship WCF has some versioning attributes you can use to keep old clients working against newer servers).

More WCF Stuff

There are lots of other things that you might need to do with your WCF app but I probably can’t help you with, as I am a newbie to this myself, plus feature-wise my requirements are pretty simple. I’d like to point you to some docs on the differences between WCF on desktop  Silverlight and WCF on the phone, but I haven’t found any yet. Things that you might need to know about that I can’t help you with include:

·         Security

·         Moving your WCF server to the real internet from your home LAN

·         Other WCF binding methods

However hopefully there is enough information here to get you going, and to at least prove that WCF on Windows Phone 7 is possible. And fun actually. Did I say that?