Mobile WCF

Write an IM App with the .NET Compact Framework

Andrew Arnott

This article discusses:
  • Mail transports in the .NET Compact Framework
  • Writing simple messaging applications
  • Inside the WCF messaging plumbing
  • Consuming a WCF Web service
This article uses the following technologies:
.NET Compact Framework 3.5, Visual Studio 2008

Code download available at:WCFCompactFramework2008_Launch.exe (185 KB)
Browse the Code Online

Contents

Mail Transports
Writing a Windows Mobile App
WCF Messaging Plumbing
Creating and Sending the Message
Writing the Desktop Application
Running the Sample
Calling WCF Services
Creating a Proxy Class
Using the WCF Service

An addressability problem with mobile devices has traditionally made it difficult to write Windows Mobile® applications that would receive data pushed down from servers. Small devices often do not have static IP addresses or dynamic DNS entries that follow them around. A common workaround has been for the device to send an HTTP request to the server when the device comes online, and the server then lets that request wait until it has something to push down to the device. The server then responds to that long-standing request with the update, and the device processes the update and sends out another request to wait for the next update.

This workaround can cause scalability problems on the server since it has to hang onto many requests at once instead of responding to them right away and closing the connections. It can also reduce the battery life of the device, which must stay on to maintain the connection. If the device isn't on when an update from the server is ready, the server has no way to send the update and either must drop the update or maintain state on which devices have which updates. Finally, the application has no way to send or receive messages if the network is not available.

Visual Studio® 2008 gives developers of Windows Mobile applications the ability to access a subset of Windows® Communication Foundation (WCF) functionality via the Microsoft® .NET Compact Framework 3.5, which solves many of these problems with two new binding elements to WCF that facilitate sending and receiving messages using e-mail as the transport. Since many devices already have e-mail sync functionality, these transports take advantage of the inherent queuing nature of e-mail and the availability of e-mail servers already set up on the Internet to create an addressable message queue that enables peer-to-peer, device-to-server, and server-to-device message-level communication in a true message-push fashion. In this article, I will outline the subset of WCF that is supported by the .NET Compact Framework 3.5 and how you can utilize these transports and tools in your mobile applications.

Mail Transports

.NET Compact Framework Resources

The .NET Compact Framework 3.5 includes WindowsMobileMailTransport for messaging on the Windows Mobile platform and ExchangeWebServiceMailTransport for messaging on the Windows desktop platform. The two transports interoperate so that communication among devices, among PCs, or between devices and PCs all works transparently. Since many applications may be using the mail transport at the same time for the same recipient, a mail channel name can be given to distinguish messages intended for your application from messages intended for other applications. This channel name is then included in the subject line of each message. As a result, the mail transport will only open messages with the subject name that corresponds to the channel name for which you are listening.

The mail transport for Windows Mobile devices that ships with the .NET Compact Framework 3.5 performs best if running on a Windows Mobile device that supports DirectPush, a feature that enables the associated Exchange server to push messages to Windows Mobile as soon as Exchange receives the message. To learn more about DirectPush (also known as Always-Up-To-Date) or to install it on your Windows Mobile device, visit msexchangeteam.com/archive/2004/04/26/120520.aspx.

If you are familiar with Microsoft Message Queuing (MSMQ), mail transport is very similar, except that it does not have the problem of device addressability. The mail stores on the device and on the Exchange server act as the message queues. The e-mail addresses of each device and server act as the queue's URI.

Note that while, theoretically, two devices using WindowsMobileMailTransport may be able to use any SMTP/POP3 servers to communicate, the only scenario that is currently supported in the .NET Compact Framework 3.5 uses Exchange Server 2007 as the mail server. Additionally, custom Windows CE devices are not supported.

To illustrate how the transports operate, I'm going to build a pair of small sample apps and use e-mail to send instant message-like messages between a Windows Mobile device and a PC. The mail transports only support one-way messaging (as opposed to request-reply), which is good for an instant message app.

The first two actions you should take are to install Visual Studio 2008 and to decide on your target device. This target device, whether physical or emulated, will need to be configured to access your Exchange Server 2007 mail account. Be sure to do this before you test your app. If you configure an emulated device, either leave it running or save its state before closing it—otherwise, it will revert to not having a mail account setup. If you forget, you'll probably find messages piling up in your device's outbox with nowhere to go.

Make sure you have Windows Mobile Device Center 6.1 or ActiveSync® 4.5 installed before you begin. You can download these from microsoft.com/activesync.

If you're running Windows Vista®, the easiest way to set up mail on your emulator is to open up the Device Emulator Manager and cradle the emulator. Windows Mobile Device Center (WMDC) should open when the device connects. If WMDC is installed but does not recognize a cradled emulator, use the Start menu to launch WMDC. Under Mobile Device Settings, check the Connection Settings and make sure that "Allow connections to one of the following:" is checked and that DMA is selected in the dropdown.

Click "Set up your device" and keep at least the E-mail option checked. Then, go ahead and provide the information to allow the device (or emulator) to connect directly to Exchange. This will allow your program to send and receive messages whenever there is a network available—even when it is not cradled. Finish setting up the partnership with the device or emulator.

If you decide to make use of the mail transport using your personal e-mail address, you may choose to set up an Exchange Rule to redirect all messages with a subject line starting with "SM": to another folder. The .NET Compact Framework 3.5 mail transports will only check two folders for WCF messages, Inbox and Service E-Mail, so be sure to keep your WCF messages in one of these two folders.

Writing a Windows Mobile App

You start writing a Windows Mobile application by creating a Visual C#® Smart Device project. Name the project DeviceMessagingApp and the solution MsdnMessagingSample, as in Figure 1. You will be taken to another dialog box, where you can choose your target platform and .NET Compact Framework version. Choose the Windows Mobile 6 Professional SDK platform and set the .NET Compact Framework version to 3.5. If you do not see an option for the Windows Mobile SDK, visit go.microsoft.com/fwlink/?LinkID=81684 and download the Windows Mobile SDK, then install it and work your way back to this step.

Figure 1 Setting Up Your Project

Figure 1** Setting Up Your Project **(Click the image for a larger view)

You should find yourself looking at the Visual Studio 2008 form designer with the Windows Mobile device and your empty main form displayed. To write a simple chat program, drop a few TextBox and Label controls on the form. You might set TextBox.ReadOnly = True on your history textbox to prevent unintentional entry. And be sure to set TextBox.Multiline = True for the history box. Add a Send button to the menu so it can be pressed using a hardware button. And now for my favorite: set Form.MinimizeBox to False so that the X in the corner turns to "ok"—this way, your app will exit instead of just being hidden when you press it. In the end, you should have something that looks like Figure 2.

Figure 2 The Application UI

Figure 2** The Application UI **

Since you'll be using the Compact Framework version of WCF, you'll need to add some assembly references. Right-click on the project's References folder within Solution Explorer and click Add Reference. From the .NET tab, select these assemblies:

  • System.ServiceModel.dll
  • System.Runtime.Serialization.dll
  • Microsoft.ServiceModel.Channels.Mail.dll
  • Microsoft.ServiceModel.Channels.Mail.WindowsMobile.dll

WCF relies on any implementation of the abstract class XmlObjectSerializer to fill the body of its messages. In the desktop version, WCF comes with DataContractSerializer, which derives from this class. Compact WCF does not include DataContractSerializer, so instead you'll use XmlSerializer. Since XmlSerializer does not derive from XmlObjectSerializer as required by WCF, you can write a small class that derives from it and simply calls on XmlSerializer to do the heavy lifting.

Add a new class called MessageSerializer, derived from XmlObjectSerializer, to your project. If you right-click on the base class name in your source file, Visual Studio will offer to stub out automatically all the functions you need to implement. All you need to do in this class is add a few lines of code, as shown in Figure 3. They define a constructor that initializes the XmlSerializer and implements the ReadObject and WriteObject methods. You'll need to write in the WriteObject method yourself.

Figure 3 XmlObjectSerializer

using System;
using System.Runtime.Serialization;
using System.Xml.Serialization;

class MessageSerializer : XmlObjectSerializer {
    XmlSerializer serializer;

    public MessageSerializer(Type type) {
        serializer = new XmlSerializer(type);
    }

    public override void WriteObject(
        System.Xml.XmlDictionaryWriter writer, object graph) {
        serializer.Serialize(writer, graph);
    }

    public override object ReadObject(
        System.Xml.XmlDictionaryReader reader, 
        bool verifyObjectName) {
        return serializer.Deserialize(reader);
    }

    // ... 
}

Those methods Visual Studio added to your class that I don't list here can simply throw NotImplementedException, as WCF will not call them anyway.

WCF Messaging Plumbing

To send and receive messages, you must initialize the WCF mail channel. Since the .NET Compact Framework 3.5 does not implement many of the Service Model classes included in desktop WCF, you must use the messaging API, which requires a bit more code. Be sure to write this code in a platform-neutral way by only calling the WCF API exposed by the .NET Compact Framework so that you can share it between your device and desktop applications, thus making each individual application small and simple.

Add a new class file called Messaging.cs. This will contain the Messaging class, which will include all the plumbing necessary to send and receive messages. In a real application, this class may be much larger for better thread safety and scalability, but I'll keep my example short to focus on the task in this sample.

I chose to make the Messaging class a generic Messaging<T> class where T is the type of data being passed back and forth between apps (see Figure 4). In a more complex app, you could pass multiple types of objects around and distinguish the messages using the message Action string, but I will not do that here. Any T will work for this class as long as it can be serialized using XmlSerializer.

Figure 4 Simple Messaging Class

using Microsoft.ServiceModel.Channels.Mail;

class Messaging<T> {
    public delegate void IncomingMessageCallback(T body);
    public const string DeviceSendChannel = "toDesktop";
    public const string DesktopSendChannel = "toDevice";

    public Messaging(MailBindingBase binding,
        string sendChannelName, string listenChannelName,
        IncomingMessageCallback incomingMessageCallback) {
        // ...
    }

    public void SendMessage(string recipient, T body) {
        // ...
    }

    public void Close() {
        // ...
    }
}

The Messaging<T> class will offer two methods for sending and receiving messages. Receiving messages will involve a background thread listening for messages and invoking a callback method whenever a message arrives. It will have to build the channel in the constructor and offer a Close method to tear down the channel when the application closes.

Since there are two mail binding classes (one for desktops and one for mobile devices), this class will take the binding to use as an argument to its constructor. So that you can use the same e-mail account for both desktop and device without the two apps reading each other's incoming messages, you'll need to use different channel names for the desktop and device—therefore, the constructor will take these as arguments, too.

So far, we've designed the skeletal structure for our class, as shown in Figure 4. With that skeletal code written, you can proceed with writing the rest of the device and desktop applications. The full implementation of Messaging.cs is included in the source code download for this article. Nearly all of it is ordinary WCF messaging layer code without any regard for a mail transport.

The next step is construction and teardown of the mail channel. Open the code behind the main form (Form1.cs). You'll need to initialize the mail channel in the constructor and tear it down when the form is closed. To start, add a field that will track the Messaging<T> instance. Since the app communicates using text messages, T will be a string:

Messaging<string> messaging;

You will initialize this field shortly. Since the Messaging<T> constructor requires a callback, first you define a compatible method:

void incomingMessage(string message) {
}

Now initialize the messaging field in the constructor. Be careful to order the two channel names so that the device app sends messages using the DeviceToDesktopChannel name and listens on the DesktopToDeviceChannel name. That way, the device will never receive messages it sends to the desktop, even if the desktop and device are sharing a single e-mail address:

messaging = new Messaging<string>(
    new WindowsMobileMailBinding(),
    Messaging<string>.DeviceToDesktopChannel,
    Messaging<string>.DesktopToDeviceChannel,
    (Messaging<string>.IncomingMessageCallback)incomingMessage);

Close the channel when the application exits in the Form.Closed event handler. The Form1_Closed method only has to call the Close method on the Messaging<T> object:

void Form1_Closed(object sender, EventArgs e) {
    messaging.Close();
}

Creating and Sending the Message

The device app is very nearly complete. All you need to do is send and respond to messages. You'll need to add a handler for the Send button. The event handler should call SendMessage on the Messaging<T> object and then clear the message textbox so the user knows the message was sent, like this:

void sendMenuItem_Click(object sender, EventArgs e) {
    messaging.SendMessage(toTextBox.Text, messageTextBox.Text);
    messageTextBox.Text = "";
}

That was easy. Now you just need to respond to incoming messages by implementing the incomingMessage method (see Figure 5). The catch here is that this callback is invoked from the background thread that listens for new messages, but you must add text to your conversation history textbox from the UI thread. Check the InvokeRequired property to see if you're on a background thread; if so, call Invoke to call the callback method on the UI thread. Once on the UI thread, simply append the incoming message to the conversation box and then make sure the history box is scrolled down far enough to read the newest message.

Figure 5 Responding to Incoming Messages

void incomingMessage(string message) {
    // Invoke ourselves on the UI thread
    if (InvokeRequired) {
        Invoke((Messaging<string>.IncomingMessageCallback)
            incomingMessage, message);
    } else {
        // append incoming message to message history
        historyTextBox.Text += message + Environment.NewLine;
        // scroll to end of history window
        historyTextBox.Select(historyTextBox.Text.Length, 0);
        historyTextBox.ScrollToCaret();
    }
}

That completes the device app. If you had different e-mail accounts on two different devices and set them up with the right channel names, this app would be all you would need to have an IM chat between devices. But let's continue assuming one device with one desktop counterpart and just one e-mail address.

Writing the Desktop Application

Add a new WPF application project to your solution. If you don't see that as an option, check that the dropdown in the upper-right corner of the Add New Project dialog box is set to .NET Framework 3.5. A Windows Forms Application would work equally well, but, for these examples, we'll use the newer technology. Set the project name to DesktopMessagingApp.

On the form designer, drag out the same controls that you did for the device application. If you haven't used WPF before, don't be intimidated by the change in appearance of the designer. Also, you don't need to set TextBox.Multiline for the history box in WPF.

You should end up with something looking like Figure 6. If you'd prefer to just enter the XAML for this form, it's included in the source code download.

Figure 6 Basic UI for the Desktop App

Figure 6** Basic UI for the Desktop App **(Click the image for a larger view)

Add several reference assemblies to the desktop project, similar to the way you did with your device project. The assemblies you need to reference are:

  • System.ServiceModel.dll
  • System.Runtime.Serialization.dll
  • Microsoft.ServiceModel.Channels.Mail.dll
  • Microsoft.ServiceModel.Channels.Mail.ExchangeWebService.dll

Next, link the MessageSerializer.cs and Messaging.cs files that you created in your device project into your desktop project. Right-click on DesktopMessagingApp in Solution Explorer, then click Add | Existing Item. Navigate to your device application directory. Select both MessageSerializer.cs and Messaging.cs, but don't click the Add button just yet. Instead, click the down arrow that is on the right edge of the button and select Add As Link. This allows you to share source files between the two projects.

The only catch here is that since you created the source files in the device project, the namespace around the shared classes matches the device project's namespace. If this bothers you too much, you can simply change the namespace for these classes to something more common. Additionally, you could put these two source files in a shared library project (being careful to not reference either the ExchangeWebService.dll or the WindowsMobile.dll assemblies).

Construction and teardown of the mail channel for the desktop app will be similar to the device application, except you will instantiate ExchangeWebServiceMailBinding instead of WindowsMobileMailBinding. Also worth noting is that while WindowsMobileMailBinding just used the mailbox on the device (or emulator), ExchangeWebServiceMailBinding needs network credentials. Assuming that the credentials are the same as your network login credentials, you can just pass null into the ExchangeWebServiceMailBinding constructor, and Exchange will authenticate using Windows Integrated Security. You can keep this pattern (which is more secure), or you can hardcode the user name and password, or you can prompt the user for credentials at run time before constructing the Messaging<T> instance.

One other difference between the two mail-binding classes: WindowsMobileMailBinding is notified immediately when ActiveSync brings down a new message, but ExchangeWebServiceMailBinding has to periodically call WebMethods to query for new messages, so you can either accept the default period of 30 seconds or set it yourself.

Start by adding the following two fields to the Window1.xaml.cs codebehind file. Change the Exchange server to your own Microsoft Outlook® Web Access URL:

Messaging<string> messaging;
readonly Uri exchangeUrl =  new Uri("https://mail.wingtiptoys.com");

Just like in the device app, create the message receiving method so you can pass it in as a callback to the Messaging<T> constructor:

void incomingMessage(string message) {
}

You can instantiate a Messaging<T> class in the Window1 constructor, passing it the Exchange mail binding (see Figure 7). This next step is very important: swap the order of the channel names from their order in the device app. By swapping the order, you allow the desktop to receive messages from the device and the device to receive messages from the desktop. If you fail to swap the channels, you have both sides speaking on the same channel and both listening on another channel—no messages would ever be received.

Figure 7 Starting the Mail Transport Binding

public Window1() {
    InitializeComponent();
    ExchangeWebServiceMailBinding binding = 
        new ExchangeWebServiceMailBinding(exchangeUrl, null);
    ((ExchangeWebServiceMailTransport)
        binding.Transport).ServerQueryInterval = 2000;

    messaging = new Messaging<string>(
        binding,
        Messaging<string>.DesktopToDeviceChannel,
        Messaging<string>.DeviceToDesktopChannel,
        incomingMessage);
}

And just so you do not have to wait half a minute to see any results, in this sample I force polling the Exchange server every two seconds. Be warned: setting the query interval to two seconds in a production app would probably introduce a scalability problem.

To close the channel, you will need to wire up a method to respond to the Window.Closed event. Open the XAML code in the Window1.xaml file and add the Closed="Window_Closed" attribute to your opening <Window> tag. It should look something like this:

<Window x:Class="DesktopMessagingApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Desktop Messaging App" Height="331" Width="440"
    Closed="Window_Closed">

Add a Window_Closed method to your Window1.xaml.cs codebehind file and then call the Close method on your Messaging<T> object, as shown here:

void Window_Closed(object sender, EventArgs e) {
    messaging.Close();
}

Once again, all you need to do is send and respond to messages. Add a handler for the Send menu button by double-clicking it in the designer. Call SendMessage and clear the textbox where the user typed her message so she can see that the message was sent:

void sendButton_Click(object sender, RoutedEventArgs e) {
    messaging.SendMessage(toBox.Text, messageBox.Text);
    messageBox.Clear();
}

To implement the incomingMessage method, once again keep in mind that it will be called from a background thread and you need to invoke it on the UI thread. The WPF way of doing this is slightly different than with Windows Forms. Instead of the InvokeRequired property, you call CheckAccess, and instead of calling Invoke on the control you call it on the control's Dispatcher object. Lastly, WPF has some handy methods on the TextBox control that do exactly what we want to do, so take advantage of them:

void incomingMessage(string message) {
    if (historyBox.CheckAccess()) {
        historyBox.AppendText(message + Environment.NewLine);
        historyBox.ScrollToEnd();
    } else {
        historyBox.Dispatcher.Invoke(DispatcherPriority.Normal, 
            (Messaging<string>.IncomingMessageCallback)incomingMessage, 
                message);
    }
}

And you're done! Let's run it!

Running the Sample

Build and deploy your device project. Be sure to deploy your device application to the same emulator or device that your mail account is configured on. Start each project individually. In Solution Explorer, right-click on the device project's item and select Debug | Start new instance. Repeat for the desktop project. Figure 8 shows what the device app should look like.

Figure 8 Sending a Message

Figure 8** Sending a Message **

Go ahead and enter your Exchange e-mail address into the To: field of each application. Type a message and click Send. While you're waiting for the message to appear on the other app, let me explain what is happening and how you can watch it.

When either app sends a message, the text string you send is wrapped inside a SOAP envelope and dropped in your outbox. Exchange picks it up and sends it to the recipient's inbox (your own in this case). Each app is polling your inbox looking for messages with a matching channel name. Once it notices the waiting message, the application will download the message, delete it from the server (or move it to your Deleted Items folder), and process it by deserializing the string out of the envelope's body tag and appending that string to the conversation history textbox.

If you're interested in how the SOAP-crafted e-mail looks, you can check your outbox or inbox (if you check before the app finds and deletes it) or look through your Deleted Items folder (if the app has already processed the message).

A number of things can go wrong as you write or configure this application. If messages do not show up where you expect them, check your Inbox and Outbox folders in Outlook or Pocket Outlook for messages to see if they are being sent and/or received. If messages are just sitting in the Pocket Outlook Outbox or the Outlook Inbox but not syncing, check that your device is connected to your Exchange mail account and force ActiveSync on your device to send and receive messages. If messages seem to be delivered to the appropriate Inboxes but your application just doesn't pick them up, verify that you have the channel names for sending and receiving messages aligned correctly.

Since the method that receives messages runs on a background thread, any exception that terminates the thread will not be visible to your UI. It's possible that something went wrong and your app is no longer listening for messages. Check your debugger's Output window for an exception stack trace to see if anything has gone wrong. You might use your debugger to see if you have a thread still waiting for messages. In a business app you would want to add exception handeling and perhaps diagnostic logging to this background thread.

Calling WCF Services

The .NET Compact Framework 3.5 does not ship with support for the WCF Service Model, which would support hosting and calling services without all the plumbing code for sending and receiving messages. But with the proper helper classes, calling WebMethods on ASP.NET Web services, WCF services, and other WSDL-compliant services using Compact WCF can be just as easy as calling a method.

The .NET Compact Framework 3.5 PowerToys ship with a tool, NetCFSvcUtil.exe, that generates these service proxy classes for you. Since NetCFSvcUtil.exe does not ship with Visual Studio 2008, you need to download the Power Toys for .NET Compact Framework 3.5 at msdn2.microsoft.com/aa497280.

Because the .NET Compact Framework 3.5 ships with a subset of the bindings that you'll find with WCF on the desktop, a service must expose an endpoint that uses bindings supported by the .NET Compact Framework. You can also write your own bindings using Compact WCF, but that is beyond the scope of this article.

Now let's construct a .NET Compact Framework-compatible WCF service on the desktop and write an application that calls into that service using a proxy class generated by NetCFSvcUtil.exe. For the service application, create a new WCF Service Web site in Visual Studio 2008, verifying that .NET Framework 3.5 is selected in the upper-right-hand corner of the New Web Site dialog. For brevity's sake, go ahead and accept the WCF service exactly as the Visual Studio 2008 template gives it to you. That gives you automatically created GetData and GetDataUsingDataContract methods.

Since the .NET Compact Framework 3.5 does not support wsHttpBinding, you need to either change the default endpoint Visual Studio created to basicHttpBinding or add a new endpoint of that type. Since WCF makes it so easy to expose multiple endpoints, and since wsHttpBinding adds security for those apps that will support it, let's leave it there and add a new endpoint that uses basicHttpBinding.

Open your WCF service's web.config file. Find the <endpoint> tags and add a new one with an address of basic and with the basicHttppBinding binding:

<!-- Service Endpoints -->
<endpoint address="" binding="wsHttpBinding" contract="IService"/>
<endpoint address="mex" binding="mexHttpBinding" 
    contract="IMetadataExchange"/>
<endpoint address="basic" binding="basicHttpBinding" 
    contract="IService"/>

Check your service by right-clicking your Web project's Service.svc project item in Solution Explorer and clicking View in Browser.

Note that with BasicHttpBinding comes a few limitations on what you can do with your service. For instance, duplex service contracts, transactions, and callbacks are not supported. If you try to use a BasicHttpBinding endpoint to expose a service that uses any of these features (or a few others), you will get a runtime error from WCF.

One additional limitation remains in your service beyond just using BasicHttpBinding: custom headers are not supported by NetCFSvcUtil.exe. If your service requires custom headers to be included in your messages, you will need to either modify the code emitted by the tool or write your own proxy class to add support for these custom headers.

Now add a.NET Compact Framework application to your solution. Create a new project using the Smart Device Project template and name it NetCFClient. Add the Compact WCF assemblies to your project references: System.ServiceModel.dll and System.Runtime.Serialization.dll. Next, add a single button onto your Form1.cs design surface and set its caption to "Call Service". Double-click it to create an event handler. Now you just need a proxy class to invoke the service.

Creating a Proxy Class

Run the NetCFSvcUtil.exe tool against the service to generate the proxy class to include in the device project (see Figure 9). You'll want the source files generated to go into the device application's source directory, so run the tool from there. The tool itself (once installed) should be found in %PROGRAMFILES%\Microsoft .NET\SDK\CompactFramework\v3.5\bin, although if you're running on a 64-bit machine, check the Program Files (x86) directory.

Figure 9 Generating the Proxy Class

C:\demos\MsdnCFServiceSample\NetCFClient>"\Program Files (x86)\Microsoft .NET\SDK\CompactFramework\v3.5\bin\NetCFSvcUtil.exe"
http://localhost:53222/Service/Service.svc?wsdl
Microsoft (R) .NET Compact Framework Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.5.0.0]
Copyright (c) Microsoft Corporation.  All rights reserved.

Attempting to download metadata from 'http://localhost:53222/Service/Service.svc?wsdl' using WS-Metadata Exchange or DISCO.
Generating files...
C:\demos\MsdnCFServiceSample\NetCFClient\Service.cs
C:\demos\MsdnCFServiceSample\NetCFClient\CFClientBase.cs

C:\demos\MsdnCFServiceSample\NetCFClient>

To run the tool, you only need to pass in the URL to the service metadata (the service must be running). You can add /language:vb if you want the tool to emit the proxy class for a Visual Basic® project. Note that the URL you pass to the tool on the command line will be the URL that the device uses to contact your service, so http://localhost may work to generate your proxy, but your device app will try to talk to itself rather than contact your desktop PC.

Also note that this tool did not generate an output.config file the way the desktop svcutil.exe tool would. The .NET Compact Framework 3.5 does not support configuring WCF through configuration files, so all the endpoint information for a service needs to be in code. The ASP.NET development server will not respond to device requests unless the device or emulator is cradled. What I have done here is use localhost to generate the proxy, then modify the generated Service.cs file by finding the EndpointAddress field defined in the ServiceClient class and changing the URL to use my hostname (the service address) rather than localhost. The code should look something like this:

public static System.ServiceModel.EndpointAddress EndpointAddress =
  new System.ServiceModel.EndpointAddress(
  "http://mycomputername:53222/Service/Service.svc/
  basic");

Also, notice the /basic suffix to the URL. This is how the client indicates to the service which of the three offered endpoints it wants to use. When you have the generated proxy class source files, simply add them to your device project in Visual Studio.

Using the WCF Service

Earlier you created an event handler for the single Call Service button. Let's implement that now. The code should look just like calling a WCF service from a WCF desktop application:

void callServiceButton_Click(object sender, EventArgs e) {
    ServiceClient client = new ServiceClient();
    MessageBox.Show(client.GetData(5));
}

Deploy and run your device app. Click the Call Service button. After a moment you should see a message box pop up with "You entered: 5" (see Figure 10).

Figure 10 Setting Up Your Project

Figure 10** Setting Up Your Project **

If you get an error that your host could not be reached, try cradling your device. If your device is an emulator, you can cradle the emulator via the Device Emulator Manager in Visual Studio.

The proxy class generated by the tool translates method calls into WCF messages and sends them using the .NET Compact Framework messaging layer. Response messages are then deserialized and passed back to your app via the method's return value.

The code generated by the NetCFSvcUtil tool will run equally well under the full version of WCF, in which case the proxy classes derive from a WCF class called ClientBase<T>. Since the .NET Compact Framework 3.5 does not ship with ClientBase<T>, the NetCFSvcUtil tool emits code for a CFClientBase<T> that must be included with your application. Although this class performs a similar function to the ClientBase<T> class found in WCF on the desktop, it is not exactly the same and may change in future versions.

Andrew Arnott has been programming using Microsoft technologies for 12 years, and his favorite pastime frameworks include WCF, WPF, and ASP.NET. He is currently working for Microsoft as a Software Development Engineer for the .NET Compact Framework. Andrew is married to his lovely wife, Cheryl, and has one son, who is less than a year old.