Compact Framework

  • Determining Form and Process Changes in Windows CE

    August 2008

    Christopher Tacke

    OpenNETCF Consulting, LLC

     

    Download the Source
      Download the PDF

    Introduction

    Windows CE, and especially Windows Mobile devices have a UI paradigm that’s quite a bit different than the desktop. As a general rule, only one application, and indeed only one Form is typically ever in front of all other applications because there usually isn’t enough real estate to show multiple forms and MDI layout isn’t supported in CE.

     

    One of the things that this paradigm pushes us toward is knowing either when the visible Form changes or when the user changes applications completely. We can use this for tracking state or even implementing an application authentication scheme.

     

    Unfortunately the Windows CE operating system simply doesn’t provide us any kind of event to tell us when a Form has changed or when the process has changed. Sure, within our application we can watch things like the Deactivate event on a Form, but when our Form gets deactivated, how do we determine what’s on top now? Is it another Form in our application, or another application altogether?

     

    In this article we’ll take a look at a simple utility class called WindowWatcher than can provide us feedback for these state changes through a very simple managed event interface.

    Window Handles and Process IDs

    The key to our solution is the fact that all Forms are Window and all Windows have a unique identifier called a Handle, or hWnd, and that all processed have a unique identifier called a process identifier, or PID. There are Win32 APIs to retrieve these identifiers, and while there are no corresponding managed-code methods that call them, a little P/Invoke gets us there easily.

     

    The Win32 APIs we’re going to be interested in are:

     

    Win32 Method

    Function

    GetForegroundWindow

    Gets the handle, or hWnd, of the current topmost, visible Window

    GetProcessID

    Gets the process identifier, or PID, of the calling process

    GetWindowThreadProcessId

    Gets the PID that owns a specific hWnd

     

    With just these three APIs we can determine when the foreground Window has changed because the return value from GetForegroundWindow will change, and we can determine if the new foreground Window is in a new process because the PID retrieved from GetWindowThreadProcessId will change.

    A Simple Solution

    So armed with these APIs, and a general idea of how we can determine the state changes we want, we can create a simple class that encapsulates our logic.

     

    First we need to define a couple delegates for our events:

     

    public delegate void WindowChangedHandler(bool processChanged, bool inMyProcess);

     

    An event of this type will be raised any time the foreground Window changes, and if the owner of the new Window is a new process, processChanged will be true. In either case inMyProcess will tell us if the new foreground Window is in the process that created our WindowWatcher class.

     

    Now we implement the logic. The core of the logic will be a background thread that periodically checks the current foreground window. To be nice, we’ll make the interval adjustable.

     

    public int Interval { get; set; }

     

    Next we define some variables that we’ll maintain at class scope to keep track of state between checks and initialize them in the class constructor plus define the event of our delegate type:

     

    private bool m_enabled;

    private bool m_stopThread;

    private IntPtr m_lastForeWindow;

    private uint m_lastPID;

    private uint m_myPID;

     

    public event WindowChangedHandler WindowChanged;

     

    public WindowWatcher()

    {

    m_enabled = false;

    m_lastForeWindow = IntPtr.Zero;

    m_lastPID = 0;

    // call the SDF because it's not a P/Invoke - it's a kcall and this is less code

    m_myPID = (uint)OpenNETCF.Diagnostics.ProcessHelper.GetCurrentProcessID();

     

    Interval = 100;

    }

     

    You’ll notice here that to get the current process ID I’m not just calling a P/Invoke, but instead calling into the Smart Device Framework (SDF). The reason for this is that GetCurrentProcessID isn’t a true API, but is instead a macro that’s defined in the CE headers, and the value it returns depends on the processor architecture of the device. Rather than lay out all of the code needed to determine this it’s a lot easier to just call a function in the SDF. Keeps the code smaller and less confusing.

     

    While we’re thinking about P/Invokes, we might as well define the other two we do need:

     

    [DllImport("coredll.dll", SetLastError = true)]

    private static extern IntPtr GetForegroundWindow();

     

    [DllImport("coredll.dll", SetLastError = true)]

    public static extern uint GetWindowThreadProcessId(

    IntPtr hwnd, out uint lpdwProcessId);

     

    To start and stop our watcher, we’ll expose a simple Enabled property. It’s job will be to start or stop our background worker thread.

     

    public bool Enabled

    {

    get { return m_enabled; }

    set

    {

    if (value == m_enabled) return;

     

    if (value)

    {

    Thread thread = new Thread(WatcherThreadProc);

    thread.IsBackground = true;

    thread.Start();

    }

    else

    {

    m_stopThread = true;

    }

    }

    }

     

    The only thing remaining is to implement the actual logic engine of this class that does all of the work, which is all in a single method running as the background thread:

     

    private void WatcherThreadProc()

    {

    // initialize some values

    m_stopThread = false;

    m_lastForeWindow = GetForegroundWindow();

    GetWindowThreadProcessId(m_lastForeWindow, out m_lastPID);

     

    while (!m_stopThread)

    {

    IntPtr currentForeWindow = GetForegroundWindow();

     

    if (currentForeWindow != m_lastForeWindow)

    {

    uint pid;

    GetWindowThreadProcessId(currentForeWindow, out pid);

     

    // window has changed - is it a new process?

    bool newProcess = false;

    if (pid != m_lastPID)

    {

    newProcess = true;

    m_lastPID = pid;

    }

    bool inMyProcess = (pid == m_myPID);

     

    if (WindowChanged != null)

    {

    WindowChanged(newProcess, inMyProcess);

    }

     

    m_lastForeWindow = currentForeWindow;

    }

     

    Thread.Sleep(Interval);

    }

    m_enabled = false;

    }

     

    You can see that it runs on the period determined by the Interval property. Ever Interval, it checks the foreground Window. If it has changed, it then determines if the new foreground Window is in the same process as the last. Next it determines if the process that owns the foreground Window is the same process that created the watcher class itself and finally it fires off an event of there are any subscribers.

     

    That’s all there is to it. Using the new class is very, very simple. It looks like this:

     

    WindowWatcher watcher = new WindowWatcher();

    watcher.WindowChanged += WindowChangedHandler;

    void WindowChangedHandler(bool processChanged, bool inMyProcess)

    {

    if(processChanged)

    {

    Debug.WriteLine(string.Format(

    "Window and Process changed - we {0} in our app",

    inMyProcess ? "are" : "are not"));

    }

    else

    {

    Debug.WriteLine(string.Format(

    "Window changed - we {0} in our app",

    inMyProcess ? "are" : "are not"));

    }

    }

     

    Conclusion

    As Compact Framework developers, and really even as general mobile and embedded device developers, we have to get used to the fact that Microsoft can’t reasonably think of every possible use-case or business challenge we might face. What’s important is that they’ve generally provided us the tools to implement solutions to just about any problem we run into.

     

    In this case we needed to know information about not just application state, but overall device state, and in less than 100 lines of code we came up with a reasonable solution that didn’t require any onerous coding or framework that we’d have to inject into all of our Forms. As with many problems like this, understanding the Win32 API and thinking outside the managed-code box can usually get you a fairly simple solution.

  • Exchanging Data using Windows Mobile, Windows Communication Foundation, .NET Compact Framework and Exchange 2007

    Mark Arteaga

    OpenNETCF Consulting

    June 2008

     

    Download the Source
      Download the PDF

    Introduction

    Several new features have been added to .NET Compact Framework 3.5that you as a developer can take advantage of in your applications. An overview of what is new is available on MSDN. One such feature is Windows Communication Foundation (WCF). For an introduction to Windows Communication Foundation, you should read Chris Tacke’s article. This article will focus on “Store and Forward messaging” using Exchange Server 2007.

    There are two sample applications accompanying this article. The first sample is a typical Line of Business (LOB) application where a central dispatcher notifies field workers of a new customer request. The second sample is a peer-to-peer application where users can share photos they have taken with their Windows Mobile device. The techniques used in the second example could easily be used to share documents and other payloads in an enterprise scenario.

     

    Prerequisites

    • Visual Studio 2008
    •  Exchange 2007

    For the device applications:

    • .NET Compact Framework 3.5
    • Windows Mobile 5.0 or later

    For the desktop application:

    • .NET Framework 3.5
    • .NET Compact Framework 3.5 SDK

     

    Why E-Mail?

    You are probably wondering “why use email to transport data? Why not use an ASP.NET Web service and have the device connect to that Web service?”The simple answer is addressability. Mobile devices are just that – mobile. They cannot guarantee a constant connection unless you are within the walled garden of a corporate network, even then there may well be connectivity black spots. Also, many enterprise scenarios leverage public cellular networks, where not only is connectivity not guaranteed, but you face the bigger challenges of uncontrollable DHCP leases and carrier network address translation (NAT). In light of this, e-mail is a very compelling transport for a number of reasons. Most people already use email and many enterprises have already deployed Direct Push Email with Exchange so leveraging this mechanism for custom applications requires no changes to existing infrastructure. You can assume that application data will be delivered to the user’s device using Exchange Direct Push Email whether the user is behind the firewall or outside the firewall.

     

    Getting Things Done

    To use WCF with Exchange, all we do is add references to the assemblies listed below to our device and desktop projects:

    • Device Project
    1. Microsoft.ServiceModel.Channels.Mail (.NET Compact Framework version)
    2. Microsoft.ServiceModel.Channels.Mail.WindowsMobile
    3. System.Runtime.Serialization
    4. System.ServiceModel
    • Desktop Project
    1. Microsoft.ServiceModel.Channels.Mail (.NET Framework version)
    2. Microsoft.ServiceModel.Channels.Mail.ExchangeWebService
    3. System.Runtime.Serialization
    4. System.ServiceModel

     

    One thing to note is that the Microsoft.ServiceModel.Channels.Mail.dll assemblies in the above lists are two separate assemblies - one for the device and one the desktop. We discuss this a bit more in the Sharing Code Between Platforms section below.

    We have also created two helper classes to make it easier to use the mail channel to be able to send data between devices and desktop. These two classes are WCFMessagingManager and XmlSerializerWrapper.

     

    XmlSerializerWrapper

    To serialize and de-serialize your data, WCF requires an object inherit from XmlObjectSerializer. Desktop developers can use DataContractSerializer however this class is not available in the .NET Compact Framework. Instead, we will simply create an XmlSerializerWrapper class which inherits from XmlObjectSerializer and has an internal XmlSerialzier member variable:

     

         public class XmlSerializerWrapper : XmlObjectSerializer

        {

        XmlSerializer m_serializer;

        ....

        }

     

    In the constructor, we create a new XmlSerializer of the type we want to serialize as follows:

     

        public XmlSerializerWrapper(Type type)

        {

        m_serializer = newXmlSerializer(type);

        }

     

    We also override the WriteObject() and ReadObject() methods which WCF calls to serialize and de-serialize our object as follows:

     

        public override void WriteObject(XmlDictionaryWriter writer, object graph)

        {

        m_serializer.Serialize(writer, graph);

        }

     

    And to de-serialize as follows:

     

    public override object ReadObject(XmlDictionaryReader reader)

    {

        return m_serializer.Deserialize(reader);

    }

     

    If you want more information on this take a look at the following article Using the XmlSerializer as an XmlObjectSerializer with WCF.

     

     

    WCFMessagingManager

     

    WCFMessagingManager is a class that makes it easier for developers to send and listen for messages using WCF. It derives from the Messaging class available in this MSDN article, which outlines creating a mobile chat system using the Compact Framework and Windows Mobile.

    The WCFMessagingManager class is a generic class that provides the flexibility to accept any type of object to send. With the Dispatch Application we use the DispatchMessage type and with the Picture Sharing application we use the PhotoData object. We will discuss these classes in more detail in the Dispatch Application and Picture Sharing Application sections.

     

    Creating a WCFMessagingManager

     

    To send our data objects we need to create our mail binding objects. On the desktop we create and ExchangeWebServiceMailBinding as follows:

     

        ExchangeWebServiceMailBinding binding = newExchangeWebServiceMailBinding(newUri(Properties.Settings.Default.ExchangeServer), newNetworkCredential(Email, Password));

     


    And on the device we create a WindowsMobileMainBinding object as follows:

     

        WindowsMobileMailBinding binding = newWindowsMobileMailBinding();

     

    You will notice that on the desktop, you have to specify an Exchange server address and your network credentials for the email account. This data is used to poll the mail box for any messages sent with the appropriate channel names. On the Windows Mobile side you don’t have to provide credentials since WindowsMobileMailBindingmonitors the mail in the Outlook inbox on the local device.

    To use the WCFMessagingManager class we create a new instance of the object and pass in a few parameters as follows:

     

        WCFMessagingManager<PhotoData> m_messagingManager;

        m_messagingManager = newWCFMessagingManager<PhotoData>(binding,Settings.Default.IncomingChannel);

     

    When we create the WCFMessagingManager, we explicitly set PhotoData as the type for the generic class and the for constructor parameters we provide the MailBindingBase (which both WindowsMobileMailBinding and ExchangeWebServiceMailBinding both inherit from) and the channel we would like to listen on.The channel must be unique to every user using the application, but you can share the same email address for sending messages.

    In the constructor of WCFMessagingManager we first build an IChannelFactory using the mail bindings as follows:

     

        //Create a param collection for both the input channel and output channel

        BindingParameterCollection param = newBindingParameterCollection();

        //Build the channel factory

        m_channelFactory = m_mailBinding.BuildChannelFactory<IOutputChannel>(param);

        m_channelFactory.Open();

     

     

    We build the IChannelFactory using the MailBindingBase object passed in and open the IChannelFactory.

    Once the IChannelFactory is created, we create a listener channel so we can listen to incoming messages.

     

        //Build the channel listeners

        m_channelListener = m_mailBinding.BuildChannelListener<IInputChannel>(

        MailUriHelper.CreateUri(m_channelNameListen, ""), param);

     

    The channel name used is the channelNameListen parameter passed in the constructor.

     

     

    Sending Messages

    WCFMessagingManager exposes a method to send data using e-mail as the transport, which is defined as follows:

     

        ///<summary>

        /// Sends a message to the receiving end

        ///</summary>

        ///<param name="recipient"></param>

        ///<param name="body"></param>

        public void SendMessage(string recipient,string channel, T body)

        {...}

     

     

    The SendMessage method is fairly straightforward,requiring three parameters. The first one, recipient, is the destination email address that the message will be going to. The channel parameter is the channel the destination will be listening on. The body parameter is the object that will be sent to the destination. Notice that the body parameter is of a generic type, so in the case of Photo Sharing application, we will be sending a PhotoData object.

     

    The implementation of SendMessage is also fairly straightforward:

     

     

        //Use the channel factory to create an output channel

        IOutputChannel outputChannel = m_channelFactory.CreateChannel(

        new EndpointAddress(MailUriHelper.Create(channel, recipient)));

         

        //Open the output channel

        outputChannel.Open();

         

        //Create a message to send via the channel

        Message message = Message.CreateMessage(

            MessageVersion.Default, UrnInternal,

            body, m_xmlSerializerWrapper);

         

        //Send the message

        outputChannel.Send(message);

         

        //Close the oubput channel

        outputChannel.Close();

     

    Using the IChannelFactory we created in the constructor, we create a new IOutputChannel specifying an EndpointAddress. The EndpointAddress requires an output channel (or the listening channel of the destination) and the destination email address of the recipient.

    Once the IOutputChannel is created, we create a Message (defined in System.ServiceModel.Channels) using the static Message.CreateMessage method. Here we pass in a MessageVersion (which we set to default), a Urn, the T body, and the XmlSerializerWrapper which will handle serializing and deserializing our body.

    When a message is sent, you will get a message in your mail box with a cryptic subject as follows:

     

    SM:v=3.5;CN=treochannel;ID=cbb5d3c798c84af3b1daf7615b780fb3;SD=633472436040000000;

     

    And the actual message will look something like this

     

        <s:Envelopexmlns:a="http://www.w3.org/2005/08/addressing"xmlns:s="http://www.w3.org/2003/05/soap-envelope">

            <s:Header>

                <a:Actions:mustUnderstand="1">urn:photoMessage</a:Action>

                <a:Tos:mustUnderstand="1">net.mail://treochannel/#marteaga@opennetcf.com</a:To>

            </s:Header>

            <s:Body>

            </s:Body>

        </s:Envelope>

     

    This XML is automatically created by the System.ServiceModel.Channels.Message class. In the case of a PhotoData object it will be serialized and added in between the body tags:

     

        <PhotoDataxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">

            <Id>9873fd22-189f-4525-97a5-4572dbcd7ad0</Id>

            <FileName>/cf/200806/WCF Windows Mobile Exchange and NETCF_files/img002.jpg</FileName>

            <Latitude>43.6919</Latitude>

            <Longitude>-79.5782</Longitude>

            <FileSize>6776</FileSize>

            <Base64Data>...</Base64Data>

        </PhotoData>

     

     

    Listening for Incoming Messages

     

    WCFMessagingManagerexposes a BeginListening method which starts a separate thread that listens for incoming messages in the background. The core implementation of the listening thread is as follows:

     

        //Open the listener channel

        m_channelListener.Open();

         

        //Accept and open the inputCHannel

        m_inputChannel = m_channelListener.AcceptChannel();

        m_inputChannel.Open();

         

        Message message;

        //Calling IInputChannel.Receive will block until a message is receive or until the inputChannel is closed

        while (true)

        {

            message = m_inputChannel.Receive();

            if (message == null)

                break;

            try

            {

                T body = message.GetBody<T>(m_xmlSerializerWrapper);

                OnIncomingMessage(body);

            }

            catch (Exception e)

            {

                //Raise the exception handler

                OnListenException(e);

            }

        }

     

    The first thing we do is open the IChannelListener we created in the WCFMessagingManager constructor. Using the IChannelListener we create an IInputChannel using the AcceptChannel() method and then Open() the IInputChannel. Once all this is setup, we step into the while() loop and call Receive() on the IInputChannel. IInputChannel.Receive is a blocking call and will not return until an incoming message is received or the IInputChannel.Close() method is called.

    When a message is received, we get the Body of the message using the GetBody method (passing in our XmlSerializerWrapper) and then raise the IncomingMessage event. If there is an error receiving the message, the ListenException event is raised to notify the user that something has gone wrong.

    To stop listening for incoming messages, WCFMessagingManager exposes a StopListening method which will shut down all channels and threads.

     

    WCFMessagingManager Events

     

    WCFMessagingManager also comes with two events so a user can be notified when a new message is received on the device or desktop application. These events are:

     

        ///<summary>

        /// Occurs when an incoming message is received

        ///</summary>

        public event IncomingMessageEventHandler IncomingMessage;

     

        ///<summary>

        /// Occurs when an error occurs in the listen thread

        ///</summary>

        public event ExceptionEventHandler ListenException;

     

    These events are primarily called from the listening thread discussed above. These events are self explanatory and we will use them in the sample applications discussed below.

     

    ­Sharing Code Between Platforms

     

    A common way to share code between Windows Mobile and Desktop applications is to build an assembly targeting the .NET Compact Framework. Since .NET Compact Framework assemblies are re-targetable, they can be used in full .NET Framework applications without being recompiled. Unfortunately this is not the case with WCF because the .NET Framework implementation of Microsoft.ServiceModel.Channels.Mail.dll is vastly different to its .NET Compact Framework counterpart.

    Luckily, the main source code files can be shared between the desktop and device projects by adding the files as ‘Links’. The following image shows the files shared for the Photo Sharing application:

     

    Dispatch Application Scenario

     

    The Dispatch Application is used where a central dispatcher sends out new requests to field service workers. The solution consists of a desktop application sending messages to and receiving messages from a Windows Mobile application using Exchange Server 2007.

    Desktop Implementation

     

    The desktop application is the central dispatch controller and sends new requests to the devices in the field. The user interface is a very simple implementation:

    To send a message to and from the device we define a class that will represent the data we are sending back and forth. We define the business object using the DispatchMessage class as follows:

     

        ///<summary>

        /// Defines a dispatch message sent to field service workers

        ///</summary>

        public class DispatchMessage

        {

            ///<summary>

            /// A unique identifier

            ///</summary>

            public Guid Id { get; set; }

         

            ///<summary>

            /// The current Status of the message

            ///</summary>

            public DispatchStatus DispatchStatus { get; set; }

         

            ///<summary>

            /// The customer name for the request

            ///</summary>

            public string CustomerName { get; set; }

         

            ///<summary>

            /// A description of the request

            ///</summary>

            public string Request { get; set; }

         

            ///<summary>

            /// The destination address of the request

            ///</summary>

            public string Address { get; set; }

        }

     

    The DispatchMessage object will be added in between the Body tags of the WCF message when sent and the XmlSerializerWrapper will handle serializing and de-serializing the object.

    When the application loads, it creates a new WCFMessagingManager and starts listening for messages. When a new message is required, the user fills in the appropriate fields and sends it off to the destination by clicking on the “New Dispatch Message” button. The code then dispatches the message using the following code:

     

    //Create the dispatch message

    DispatchMessage dm = newDispatchMessage();

    dm.Address = txtAddress.Text;

    dm.CustomerName = txtCustomerName.Text;

    dm.DispatchStatus = DispatchStatus.SentToServiceRep;

    dm.Id = Guid.NewGuid();

    dm.Request = txtRequest.Text;

     

    //Add the dispatch message to the list

    m_messagesSent.Add(dm);

    UpdateListbox();

     

    //Send the message

    m_messagingManager.SendMessage(txtServiceRep.Text, ChannelNames.ServerChannelName, dm);

     

    When the IncomingMessage event is raised, we update the listbox on the main form with an update status from the device using the following:

       

    void m_messagingManager_IncomingMessage(DispatchMessage body)

        {

            this.Invoke(newEventHandler(delegate(object sender, EventArgs ea)

            {

                //find the message in the internal list

                var message = from tmsg in m_messagesSent

                where tmsg.Id.Equals(body.Id)

                select tmsg;

             

                if (message.Count() == 1)

                {

                    //Update the dispatch status

                    message.First().DispatchStatus = body.DispatchStatus;

                }

                else

                {

                    //add the item to the list

                    m_messagesSent.Add(body);

                }  

         
                UpdateListbox();

            }));

        }

     

    Windows Mobile Implementation

    The Windows Mobile application listens for new customer dispatch requests while out in the field. The UI implementation again is very simple and allows for the DispatchMessage object details to be displayed.

    When a message first arrives on the device, it will automatically respond with a ‘DeviceConfirmReceipt’ status:

     

        void m_messagingManager_IncomingMessage(DispatchMessage body)

        {

            this.Invoke(newEventHandler(delegate(object sender, EventArgs ea)

            {

                this.statusBar1.Text = string.Format("New Message Received for {0}",body.CustomerName);

                Application.DoEvents();

                this.txtAddress.Text = body.Address;

                this.txtCustomerName.Text = body.CustomerName;

                this.txtRequest.Text = body.Request;

                this.cmbStatus.Text = body.DispatchStatus.ToString();

                this.txtId.Text = body.Id.ToString();

                

                if (body.DispatchStatus == DispatchStatus.SentToServiceRep)

                {

                    //Respond with a confirm receipt

                     CreateAndSendMessage(txtAddress.Text, txtCustomerName.Text, DispatchStatus.DeviceConfirmReceipt, txtRequest.Text, txtId.Text);

                }

            }));

        }

     

    This will be displayed on the desktop application:

    The field service rep will also have the option to change the status to the following:

    This way the central location will always know what the current status of the job is.

     

    Enhancements

     

    The sample dispatch application could be enhanced to better fit into an enterprise scenario. For example, the addition of SQL Server to store the dispatch requests, a list of field service reps currently in the field and integration with GPS data. You can also use SQL Replication to actually store the data and have it synchronize the data, and WCF can be used notify the application to initiate a sync.

     

    Picture Sharing Application Scenario

     

    The Picture Sharing application allows users to share recently taken photos with their friends by leveraging WCF and Exchange to send the photos. The sample consists of a Windows Mobile application running on two separate devices and a desktop application.

    In this sample we are assuming that the application will be used between friends that want to share recently taken pictures, but the same concept can be used in a LOB type application. For example, a field worker wanting to get some advice on a component while out in the field could use an application of similar architecture. The worker can snap a picture, send it back to the central dispatch location and the central dispatch location will automatically call back to provide assistance.

     

    Windows Mobile Implementation

     

    In the previous example we had the desktop application initiating the messages to send to the devices. In this scenario, the devices will be initiating and will be sending the messages to a desktop application as well as a secondary device.

    The Windows Mobile application consists of two forms and uses the WCFMessagingManager and XmlSerializerWrapper classes.

    The use of WCFMessagingManager and XmlSerializerWrapper is similar to the DispatchSample except in this sample we are sending a PhotoData object instead of DispatchMessage object.

    We create our WCFMessagingManager as follows:

     

        WindowsMobileMailBinding binding = newWindowsMobileMailBinding();
        m_messagingManager = newWCFMessagingManager<PhotoData>(binding, m_config.IncomingChannel);

     

    The PhotoData class is defined as follows:

     

        public class PhotoData

        {

     

    privateBitmap m_bitmap = null;

     

    public PhotoData(string filename)

    {

        //Hardcoded to the toronto Congress Center

        this.Latitude = 43.6919;

        this.Longitude = -79.5782;

     

        //Set the file name

        FileName = filename;

        SetProperties();

     

        //Set the id

        Id = Guid.NewGuid();

    }

     

    public PhotoData()

    {

    }

     

    public string Comment { get; set; }

     

    public Guid Id { get; set; }

     

    public string FileName { get; set; }

     

    public double Latitude { get; set; }

     

    public double Longitude { get; set; }

     

    public int FileSize { get; set; }

     

    public string Base64Data { get; set; }

     

    ///<returns></returns>

    public Bitmap GetBitmapImage()

    { ... }

     

    private void SetProperties()

    { ... }

     

    }

     

    Here you can see that we have various properties such as Comment, FileName, Latitude/Longitude of where the picture was taken, FileSize and Base64Data.

    When sending the message over the wire using WCF, we want to make sure to convert the actual image to Base64 string. We accomplish this with the following helper method within PhotoData class:

     

        private void SetProperties()

        {

            if (File.Exists(FileName))

            {

                //Grab the byte data from the file

                FileStream fs = File.Open(FileName, FileMode.Open);

                byte[] data = newbyte[fs.Length];

                fs.Read(data, 0, data.Length);

                fs.Close();

     

                //Set the file size

                FileSize = data.Length;

                

                //convert the byte[] to base64

                Base64Data = Convert.ToBase64String(data);

            }

        }

     

    This method is called from the PhotoData(string) constructor and the constructor automatically sets the appropriate properties within the class.

    You may have noticed that PhotoData has two constructors, the first taking a string parameter and the second taking no parameters. The reason for this is that XmlSerializer (used in XmlSerializerWrapper) requires a parameter-less constructor to de-serialize the XML data. So when an incoming PhotoData object is received, XmlSerializer will automatically de-serialize the data.

    There is also a public method in the class which returns a Bitmap object to display on the destination.

     

        public Bitmap GetBitmapImage()

        {

            if (m_bitmap == null)

            {

                if (Base64Data != null)

                {

                    byte[] imageData = Convert.FromBase64String(Base64Data);

                    m_bitmap = newBitmap(newMemoryStream(imageData));

                }

            }

            return m_bitmap;

        }

     

    So on an incoming PhotoData object, displaying the image is as simple as calling GetBitmapImage.

    The UI of the Windows Mobile application is straight forward. Main form, is a very simple form using a PictureBox and two menu items to take a new picture and to send the pictures.

    To take the pictures we use the Microsoft.WindowsMobile.Forms.CameraCaptureDialog which is available with Windows Mobile 5 and later devices as follows:

     

        using (CameraCaptureDialog ccd = newCameraCaptureDialog())

        {

            if (ccd.ShowDialog() == DialogResult.OK)

            {

                CurrentPhotoData = newPhotoData(ccd.FileName);

                CurrentPhotoData.FileName = ccd.FileName;

                pictureBox1.Image = newBitmap(CurrentPhotoData.FileName);

            }

        }

     

    When the picture is taken, we set the PictureBox to display the currently displayed image.

    Sending a PhotoData object is also straight forward and we simply need to call WCFMessageingManager.SendMessage as follows:

     

        //Send the message to the device

        m_messagingManager.SendMessage(m_config.SendToEmail, m_config.OutgoingChannel, CurrentPhotoData);

        //Send the message to the desktop

        m_messagingManager.SendMessage(m_config.SendToEmail, m_config.OutgoingChannelDesktop, CurrentPhotoData);

     

    WCFMessagingManager will handle sending the PhotoData objects to the appropriate email address and the corresponding channels those emails should be listening to.

     

    Desktop Implementation

     

    The desktop implementation primarily just listens for incoming PhotoData objects and displays them. The user interface consists of a ListBox to display the current PhotoData objects in memory, a PictureBox to display the image and a WebBrowser control to display a Virtual Earth map and a push pin of where the picture was taken.

    The following is a screen shot of the user interface:

    When the WCFMessagingManager.IncomingMessage event is handled by the main UI we want to update our UI appropriately as follows:

     

        this.BeginInvoke(newEventHandler(delegate(object sender, EventArgs ea)

        {

            //find the message in the internal list

            var message = from tmsg in m_messagesReceived

            where tmsg.Id.Equals(body.Id)

            select tmsg;

         

            if (message.Count() == 0)

            {

                //add the item to the list

                m_messagesReceived.Add(body);

            }

     

            UpdateListbox();

        }));

     

    The m_messagesRecevied variable is of type List<string>and is used as the DataSource for the ListBox as follows:

     

        listBox1.DataSource = m_messagesReceived;

     

    On the ListBox.SelectedIndexChanged event, we display the picture and a push pin within the Virtual Earth map to give a visual location of where the image was taken. We accomplish this using the following:

     

        if (listBox1.SelectedIndex >= 0 && listBox1.SelectedIndex < m_messagesReceived.Count)

        {

            PhotoData pd = m_messagesReceived[listBox1.SelectedIndex];

            //Grab the photo data object and display the image

            this.pictureBox1.Image = pd.GetBitmapImage();

     

            //Show a push pin on the map of where the image was taken

            executeScript(ClearPushpinsScript);

         

            executeScript(AddPushpinScript, pd.Id.ToString(),

            (pd.Comment == null ? "No Comments" : pd.Comment.ToString()),

            pd.Latitude, pd.Longitude);

            //center on the push pin

            executeScript(SetCenter, pd.Latitude, pd.Longitude);

            //zoom in on the push pin

            executeScript(Zoom, 18);

        }

     

    Final Results

     

    The following are screen shots of a test run. Here I took a picture using a Palm Treo 700wx running Windows Mobile 5.0, sent it to the desktop application as well as a Windows Mobile 6.1 Professional Emulator.

     

    Virtual Earth Integration on the Desktop

     

    Integration with Virtual Earth came up as the PhotoData object contained Latitude and Longitude values. To integrate Virtual Earth with the WinForms application, WinForms Earth v2 from Via Virtual Earth was leveraged. A few updates had to be done to get things to work as the original code was extremely out date.

    To get a Virtual Earth map to load within a WinForms application and to communicate with the Virtual Earth map through code, there are a few things that need to be done.

    1. An local HTMLpage is required that will load up the Virtual Earth Map. This HTMLfile will also contain JavaScript functions that will be called from the .NET application
    2. ComVisible(true) Attribute must be added to your form. This can also be added to a class that will handle the communication but between the .NET code and the HTMLdocument but for simplicity we added it to the Form.
    3. Interaction from .NET to the HTMLdocument JavaScript code is done by calling WebBrowser.Document.InvokeScript method which accepts the javascript method name and the parameters for the method
    4. Interaction from HTMLdocument to .NET is done by using the JavaScript function window.external.X where X represents your method name in your .NET Code. This method must be public. See the original source for WinForms Earth V2 as this has the sample there.

     

    Enhancements

     

    Some enhancements that can be done to the application would be integrating GPS functionality with the Windows Mobile application (see the GPS Sample application available with Windows Mobile SDK). On the desktop side, you can have the PhotoData objects stored in a SQLServer Compact Database and also integrate any incoming data with Flickr or Facebook Photos.

     

    Conclusion

     

    E-mail has been around for a long time, and e-mail was one of the first applications implemented on mobile devices. With Windows Mobile, now that we have direct push email and .NET Compact Framework 3.5 we can leverage email and the direct push functionality in our Windows Mobile applications. As developers it allows us to focus on our application and not worry about how we are going to get data from point A to point B,C,E etc. If using Exchange is not possible with your scenario, you can always extend the WCF functionality with something like XMPP, but that’s an entire article on its own!

  • Native vs. Managed Code: GDI Performance

    May 2008

    Chris Tacke

    OpenNETCF Consulting, LLC

     

    Download the Source
      Download the PDF

    Introduction

    I recently saw a post in the public Compact Framework newsgroup[1] that is fairly typical. The poster stated that if you want speed and performance for graphics, you should use native (meaning C or C++) code. It’s a general sentiment that I often read or hear – native code is faster than managed code for XYZ operation, so don’t use managed code. In most cases these claims are being made by developers with little experience in managed development and they rarely have any sort of empirical data to back up their statement.

     

    I knew that I already had some code for measuring GDI performance in native code that I wrote several years ago for testing and tuning a Windows CE display driver, so I decided I’d dig it up and then create a managed application that did the same operations and then compare the results of the two with real evidence and put to rest this silly myth once again.

     

    Native Code Device Test

    Before diving into managed code to see how it would perform, I needed to get the baseline test running in native code. The code I had was a pretty simple and straightforward test. It creates an off-screen square bitmap and device context (DC), fills it with a varying shade of grey, and then draws two intersecting green ellipses centered in that square (see Figure 1). The test then repeats the fill and ellipse drawing 10,000 times, each time changing the fill color and the ellipse boundaries to prevent the driver from being able to cache anything and to provide a nice psychedelic pattern to watch as the test runs. The general code loop can be seen in Listing 1 (the full code accompanies this article).

     

    ...

    screenDC = GetWindowDC(NULL);

    bufferDC = CreateCompatibleDC(screenDC);

    oldPen = (HPEN)SelectObject(bufferDC, CreatePen(PS_NULL, 0, NULL));

    oldBrush = (HBRUSH)SelectObject(bufferDC, CreateSolidBrush(RGB(0,255,0)));

    hBufferBmp = CreateCompatibleBitmap(screenDC, BOX_WIDTH, BOX_HEIGHT);

    oldObject = SelectObject(bufferDC, hBufferBmp);

     

    rect.top = 0;

    rect.left = 0;

    rect.right = BOX_WIDTH;

    rect.bottom = BOX_HEIGHT;

     

    for(i = 0 ; i < GDI_ITERATIONS_PER_REPORT ; i++)

    {

    width += xop;

    height+= yop;

    if((width >= BOX_WIDTH) || (width <= 0))

    xop *= -1;

     

    if((height >= BOX_HEIGHT) || (height <= 0))

    yop *= -1;

     

    r += rop;

    g += gop;

    b += bop;

     

    if((r > 254) || (r< 1))

    rop *= -1;

     

    if((g > 254) || (g< 1))

    gop *= -1;

     

    if((b> 254) || (b< 1))

    bop *= -1;

     

    boxBrush = CreateSolidBrush(RGB(r, g, b));

    FillRect(bufferDC, &rect, boxBrush);

    DeleteObject(boxBrush);

     

    Ellipse(bufferDC, width, height, BOX_WIDTH - width, BOX_HEIGHT - height);

    Ellipse(bufferDC, height, width, BOX_WIDTH - height, BOX_HEIGHT - width);

     

    BitBlt(screenDC, left, top, left + BOX_WIDTH, top + BOX_HEIGHT,

    bufferDC, 0, 0, SRCCOPY);

     

    }

    ...

     

    In the tests I ran, the loop executed for 10,000 iterations. On the Windows Mobile 5.0 emulator that ships with Visual Studio 2008 running on my desktop PC this test executes at about 490 iterations per second (one iteration being a fill, two ellipse draws and a blit to the screen). On a Windows Mobile 5.0 Dell Axim x51 (PXA270 processor) it achieves about 250 iterations per second.

     

    Compact Framework Device Test

    The next step was to “port” the native code to managed code. The code is straightforward and most of the concepts in the native application have direct parallels in managed code. Listing 2 shows the essence of the managed version of the loop code (again, the full source accompanies this article).

     

    ...

    Bitmap backBuffer = new Bitmap(BOX_WIDTH, BOX_HEIGHT);

    Brush boxBrush;

    Brush ellipseBrush = new SolidBrush(Color.Green);

    Graphics screenGraphics = f.CreateGraphics(); // Graphics.FromHdc(screenDC);

    Graphics backGraphics = Graphics.FromImage(backBuffer);

     

    for (i = 0; i < GDI_ITERATIONS_PER_REPORT; i++)

    {

    width += xop;

    height += yop;

     

    if ((width >= BOX_WIDTH) || (width <= 0))

    xop *= -1;

    if ((height >= BOX_HEIGHT) || (height <= 0))

    yop *= -1;

     

    r += rop;

    g += gop;

    b += bop;

     

    if ((r > 254) || (r < 1))

    rop *= -1;

     

    if ((g > 254) || (g < 1))

    gop *= -1;

     

    if ((b > 254) || (b < 1))

    bop *= -1;

     

    boxBrush = new SolidBrush(Color.FromArgb(r, g, b));

    backGraphics.FillRectangle(boxBrush, 0, 0, BOX_WIDTH, BOX_HEIGHT);

    boxBrush.Dispose();

     

    backGraphics.FillEllipse(ellipseBrush, width, height,

    BOX_WIDTH - 2 * width, BOX_HEIGHT - 2 * height);

    backGraphics.FillEllipse(ellipseBrush, height, width,

    BOX_WIDTH - 2 * height, BOX_HEIGHT - 2 * width);

     

    screenGraphics.DrawImage(backBuffer, 0, 0);

    }

     

     

    When I ran this test against Compact Framework 3.5 is was rather surprised to see that on the Windows Mobile 5.0 emulator it only achieved 425 iterations per second (15% slower than the native code) and on the Axim it only achieved 210 iterations per second (19% slower than native code).

     

    Testing on the Desktop

    To say the least, the results from the device tests surprised me. In my gut I had expected the managed code performance to be within 5% of the native code – 10% at the outside – but to see 15-20% was amazing. I then wondered if it was maybe something specific to the Compact Framework and that maybe we’d not see such a large disparity on the desktop. If I were asked to guess at the desktop results before seeing the device results I would have almost certainly said that the managed code results would be within 5% of the native, but after seeing the results from the device I had to actually run it and see.

     

    I modified both the device and desktop code slightly to get single code bases for both languages that would compile for either target and then re-ran the tests. The managed code test achieved 825 iterations per second on the same PC that the emulator was running on – so roughly twice as fast. The native code, however, surprised me again and this time it was more “shock and awe” than mere surprise. The native desktop version of the test achieved 6200 iterations per second. Yes, 625% faster than the managed code on the same target.

     

    Updating the Managed code

    The managed code performed substantially worse than I expected (especially on the desktop). The question that still remains is “why?” What would account for this performance degradation? I would suspect that something like a call to Graphics.FillRectangle would just be a thin wrapper around a P/Invoke to the Win32 FillRect call so does that mean the performance problems we see are purely an artifact of the performance penalty of crossing the P/Invoke boundary[2]? If so we should be able to remove the calls to the framework Drawing classes, directly P/Invoke the GDI functions and get the same, or at least similar results.

     

    I went back and reworked the managed code to be able to run using only framework-provided drawing operations within the test loop or to P/Invoke all of them. I then re-ran the test on the Windows Mobile 5.0 Emulator and the Axim and got 455 and 225 iterations per second respectively. That’s a 7% speed improvement over using purely the framework-provided GDI calls, and generally cut the difference between the native and managed tests in half.

     

    This means that the penalty for crossing the P/Invoke boundary is definitely part of the performance difference between native and managed code in our tests, but it only accounts for about half of it. I can’t readily account for the other half, but if I were to make an educated guess it’s likely because the framework is doing parameter and bounds checking on all calls, which my P/Invokes aren’t doing, and it may be doing something different for data marshaling to the native side as well.

     

    To be complete I then updated the code again so it would compile and run on the desktop as well as the device (the P/Invoke declarations are different between the two platforms). What really surprised me here was that using the P/Invokes on the desktop resulted in the managed test achieving 6150 iterations per second, or within 1% of the native test.

     

    Obviously there’s something I don’t fully understand about GDI under the full framework, but the updated tests show that managed code certainly can perform just as fast as native code. Since we at OpenNETCF tend to focus on Windows CE and Windows Mobile, I decided to leave investigating the huge disparity on the desktop to a later time.

     

    Conclusion

    So I set out to gather some hard data and debunk a myth about managed code being slow but I ended up confirming that managed code is indeed slower, sometime a lot slower, than native code - at least for basic GDI operations. Below is a summary table of my results as well as results from another tech reviewer that ran tests on his devices for me (thanks to Peter Nowak).


     

     

    Managed: Framework Calls

    Managed: P/Invoke Calls

    Native

    Native is n% Faster (framework / P/invoke)

    Desktop

     

     

     

     

    2.66GHz Intel Core2 Duo

    890

    6150

    6200

    597% / 1%

     

     

     

     

     

    Device

     

     

     

     

    Windows Mobile 5.0 Emulator

    425

    455

    490

    15% / 8%

    WinMo 5.0

    Dell Axim x51

    PXA270

    210

    225

    250

    19% / 11%

    HTC TyTn*

    Samsung 2442A

    70

    74

    75

    7% / 1%

    HTC P6300*

    Samsung 2442

    73

    76

    77

    6% / 1%

    HTC Athena*

    PXA270

    118

    119

    128

    9% / 8%

    HTC TyTn II*

    Qualcomm 7200

    155

    168

    178

    15% / 6%

    HTC Sedna*

    Qualcomm 7200

    121

    129

    131

    9% / 2%

    HTC Charmer*

    OMAP850

    134

    151

    154

    15% / 2%

    * = Results from testing 1,000 iterations instead of 10,000

     

    We can see that across the board managed code is always slower, but that with a little extra effort and code we can squeeze a bit more performance out of the managed code. If you really need that extra speed it’s probably worth the effort. Making the changes on the desktop yields an enormous improvement, but again there could be something fundamentally wrong with the desktop test, so do some more investigation before just running wild with the numbers I came up with.

     

    So does this mean that native code is “superior” to managed code and that you shouldn’t still consider using managed code for your development projects? I’m still going to adamantly answer “no.” Managed code is not the answer to all development problems, but neither is native code. Each has their place, their strengths and their weaknesses.

     

    Consider the rapid development nature of managed code and the nice variety of test and continuous integration tools available for managed code developers. It’s very difficult to make a business case that something like a typical enterprise data collection and reporting application should be developed using anything but managed code. It will get you to market faster, with lower cost, more features and less support headache. What’s not to like?

     

    Similarly you’d be hard pressed to make a reasonable case that something like an Ethernet driver should be written in anything but native code. The more difficult decisions are in the grey area between. In these cases you have to weigh the application requirements against your schedule, budget and available resources. Sure, the simple tests I ran here show that for when it comes to raw throughput for simple GDI operations, native code is faster but the human eye doesn’t really need anything faster than 30 frames per second to see it as smooth – so is it really a problem? What else is your application going to do?

     

    If you’re making a flight simulator, then your app is all about drawing and sure – native code would be the choice, but what about a card game? How about a drawing program or a text reader? The point is that only you can decide which is better for what you need to get done. Hopefully this article, instead of providing opinion and conjecture, provides you another piece of real information with hard data that can help you make that decision.


    [1] microsoft.public.framework.compactframework

    [2] See my January 2008 article entitled “Performance Implication of Crossing the P/Invoke Boundary”

    http://community.opennetcf.com/articles/cf/archive/2008/01/31/performance-implications-of-crossing-the-p-invoke-boundary.aspx

  • Performance Implications of Crossing the P/Invoke Boundary


    January 2008

    Christopher Tacke

    OpenNETCF Consulting, LLC

     

    Download the Source
      Download the PDF

    Introduction

    A few months ago a colleague of mine and I were invited to visit a potential customer to give them our thoughts and a expertise in the design of a new control system. The client makes machines that folds and fills cartons – think of cereal boxes and the like – at amazingly high speeds.

    They had been using a proprietary system that they has designed with no OS and they were reaching the limits of just how fast they could run and how many sensors and the like that they could put on the Sercos bus so they figured they could leverage Windows CE (readily available for the controllers they already had) to bring out a new generation of machines with better throughput, faster reset times and all of the sorts of things their customers would love.

    After a full day of walking through wonderful UML diagrams that detailed every last bit of how the process for their new system would work a few things were very clear.

    1.       They definitely knew how to build boxes. Seriously – it’s far more complex than I imagined, and if I ever need a machine that folds cartons, these guys would have my business.

    2.       They spent a large amount of time thinking through a whole lot of the details of how this new system architecture would work. And when I say a large amount of time, I mean probably more time than I’ve spent developing entire products. They had done serious due diligence here.

    3.       They had no idea how managed code worked and a large majority of what they had done was going to be unusable.

    True story. They had designed a large, very flexible, pluggable, extensible, deeply-abstracted architecture for a machine that should be able to fold several hundred or more cartons a minute and it had all been designed around a managed code foundation. These machines have motors, servos, actuators, conveyors and all sorts of things that have to run in a very finely tuned and orchestrated dance at a very high speed, and if any one item is in the wrong place at the wrong time then the best case is that something gets jammed or broken and worst case is a machine operator gets seriously injured.

    The problem with their design was rooted in two fundamental issues; the first being that managed code is not deterministic. That means that when any action happens (like a sensor sees a moving box) then the time for a reaction to occur (an arm moves to make a fold) is unbounded. If you’re trying to make boxes that are all the same size and shape at any reasonable rate of speed, you can imagine that having a fold in the same spot every time might be fairly important.

    Often you might hear the term “real-time” but the problem with that term is the definition depends on the system you’re looking at. A system is real-time if the maximum latency (or jitter) of a reaction is less than what you need for your system to operate. If you’re calculating trajectories for an air-to-air missile your definition of real-time is a whole lot different than if you’re trying to measure the movement of a tectonic plate in “real-time.”

    At any rate, we know that any environment with a garbage collector, whether it’s C#, VB.NET running on Windows or Java running on Linux, is not deterministic by its very nature, so there’s no point in beating on this point too heavily. If you want to know about the deterministic behaviors of either Windows CE or managed code, there are good resources on the web to get you well acquainted (and you’ll be seeing more from us in the coming months as well).

    The other problem with their design using managed code, however, is not so obvious (unless of course you read the title of this paper). If C# were somehow made deterministic, would it then be a good choice for them? We know that a large amount of what they need to do is going to require Platform Invokes (or P/Invokes) and I instinctively felt that there was going to be a problem with performance because of that. Of course I had no data to back that up and I knew of no specific papers or articles that specifically looked at what the performance impacts of P/Invoke calls were, but I knew in my gut that they were going to be a problem, so we went back to the hotel, had dinner and a couple drinks, and I proceeded to put together some test code to prove out what I knew deep-down just had to be true.

    Test Theory

    My original test code was far more complex that what we’ll be looking at for this article. In it I tried to do more realistic simulations of what they would be doing in practice, including a lot of thread interactions and driver calls. For the most part a lot of those tests would just add complexity and confusion to these tests so for this article I distilled the problem down to just a few test points:

    1. How long does it take for a call originating in managed code to arrive in the called native library?
    2. How long does it take for the call to return back to the managed caller?
    3. Does a callback perform any better than a returned value or parameter?

     

    To measure the first I decided that I would get the current value of the performance counter right before the P/Invoke call and then in the native library I would grab the value again as soon as the method was entered. To make the second and third tests easier, I would simply return the performance counter value collected in the native method back to the managed caller either directly as a return value or through a callback. The managed caller could then do some basic math and arrive at answers for all three questions.

    Of course the raw answers themselves are pretty useless. If you want better performance, all you need is a faster processor. To really make any use of these numbers, we need to do the same test in pure native code doing the exact same steps and then do a comparison of the native and managed results. What is important is not the hard numbers, but the ratios or percentages of a managed test to an analogous native test.

    The Native Callee

    Step one was to create the native library that would get called by both the native and the managed clients. Using the same library for both tests eliminates it as a source of variability, and in this case is was really simple. I simply exported two functions – one that would return the time it received a call through an output parameter and the other through a callback. The code is really simple:

     

     

    typedef void (*NATIVE_CALLBACK)(LARGE_INTEGER timeReceived);

     

    static LARGE_INTEGER g_li;

     

    extern "C"

    __declspec(dllexport) void DirectCall(LARGE_INTEGER *timeReceived)

    {

    // get the time

    QueryPerformanceCounter(timeReceived);

    }

     

    extern "C"

    __declspec(dllexport) void CallBack(NATIVE_CALLBACK callback)

    {

    // get the time

    QueryPerformanceCounter(&g_li);

    callback(g_li);

    }

     


     

    Collecting Control Data

    Step two was to gather some data to use as the “control” for the experiment by writing a native client EXE that calls the native DLL and receives back a value through both a return value and a callback. TO make sure I had a decent amount of data I ran 10,000 iterations of the test and output the results to a CSV file so I could plot and analyze the data in Excel. Again, the code is very simple. I opted to load the library dynamically to replicate as close as possible what managed code would be doing:

     

     

    static LARGE_INTEGER li1;

    static LARGE_INTEGER li2;

    static HANDLE hFile;

    static char line[500];

     

    typedef void (*NATIVE_CALLBACK)(LARGE_INTEGER timeReceived);

    typedef void (*DIRECTCALL)(LARGE_INTEGER *timeReceived);

    typedef void (*CALLBACKFCN)(NATIVE_CALLBACK callback);

     

    void CallbackProc(LARGE_INTEGER timeReceived);

     

    int _tmain(int argc, _TCHAR* argv[])

    {

    HINSTANCE hLib = LoadLibrary(_T("NativeDLL.dll"));

    DIRECTCALL DirectCall = (DIRECTCALL)GetProcAddress(hLib,

    _T("DirectCall"));

    CALLBACKFCN CallBack = (CALLBACKFCN)GetProcAddress(hLib,

    _T("CallBack"));

    OpenLog();

     

    LARGE_INTEGER timeReceived;

     

    WriteLog("point#, outDirect, backDirect, outCallback, backCallback\r\n");

     

    for(int i = 0 ; i < 10000; i++)

    {

    QueryPerformanceCounter(&li1);

    DirectCall(&timeReceived);

    QueryPerformanceCounter(&li2);

     

    sprintf(line, "%d, %d, %d,", i,

    timeReceived.LowPart - li1.LowPart,

    li2.LowPart - timeReceived.LowPart);

    WriteLog(line);

     

    QueryPerformanceCounter(&li1);

    CallBack(&CallbackProc);

    }

     

    CloseLog();

    FreeLibrary(hLib);

     

    return 0;

    }

     

    void CallbackProc(LARGE_INTEGER timeReceived)

    {

    QueryPerformanceCounter(&li2);

     

    sprintf(line, "%d, %d\r\n", timeReceived.LowPart - li1.LowPart,

    li2.LowPart - timeReceived.LowPart);

    WriteLog(line);

    }

     

    Collecting Compact Framework Data

    The code for collecting the data from the managed application is obviously the same basic idea as the native application. Each of the native APIs are called and the managed application calculates the times and logs them to a file. The app runs this set 10,000 times to get a good sampling. The meat of the logic is below – the full source is in the download for this article.

     

     

    public delegate void CallbackDelegate(ulong timeReceived);

     

    public class Test

    {

    ulong start;

    ulong recv;

    ulong stop;

     

    CallbackDelegate callbackDelegate;

    IntPtr callbackPtr;

     

    public void Run()

    {

    int outDirect;

    int outCallback;

    int backDirect;

    int backCallback;

     

    callbackDelegate = new CallbackDelegate(CallbackProc);

    callbackPtr = Marshal.GetFunctionPointerForDelegate(callbackDelegate);

     

    OpenLog("\\managedNoGC.csv");

    WriteLog("point#, outDirect, backDirect, outCallback, backCallback\r\n");

     

    for (int i = 0; i < 10000; i++)

    {

    RunOne(out outDirect, out backDirect, out outCallback,

    out backCallback);

    WriteLog(string.Format("{0},{1},{2},{3},{4}\r\n", i, outDirect,

    backDirect, outCallback, backCallback));

    }

    CloseLog();

    }

     

    private void CallbackProc(ulong timeReceived)

    {

    QueryPerformanceCounter(out stop);

    recv = timeReceived;

    }

     

    private void RunOne(out int timeOutDirect, out int timeBackDirect,

    out int timeOutCallback, out int timeBackCallback)

    {

    QueryPerformanceCounter(out start);

    DirectCall(out recv);

    QueryPerformanceCounter(out stop);

     

    timeOutDirect = (int)(recv - start);

    timeBackDirect = (int)(stop - recv);

     

    QueryPerformanceCounter(out start);

    CallBack(callbackPtr);

     

    timeOutCallback = (int)(recv - start);

    timeBackCallback = (int)(stop - recv);

    }

    }

     

     

    Since managed code also has the huge variable of the GC, I also decided to rerun the test with a background thread continually allocating and throwing away data.

     

     

    void ThreadProc()

    {

    while (running)

    {

    garbage = new byte[63000];

    }

    }

     

     

    Now if you’re paying close attention you’ll notice that our timing code here is not identical to that in the managed code. QueryPerformanceCounter itself is a P/Invoke, so we’re inherently adding the time for the return from that P/Invoke to our results. Eliminating this effect would take a bit of work that would make this test setup a lot more convoluted, and we’re trying to keep this simple, but keep it in mind when looking at the results later.

    Results

    Once I’d run all the tests, I imported the data files into an Excel spreadsheet for analysis and plotting. The following plots provide comparative views of the times for each “segment” of the test (i.e. each of our three initial questions. Each plot has three data sets – the control (native) data and then managed data run with and then without a background thread generating garbage (and therefore garbage collection). The plots show performance counter ticks (elapsed time) on the Y axis and the sample number of the X axis.

     

    NOTE

    Unless otherwise noted, the results reported in the charts in this section are in performance counter ticks. The test device used was an iCOP eBox-2300 with a 200MHz Vortex86 processor that reports a performance counter frequency of 1,193,180 ticks per second.

    Outgoing Direct Call

    In Figure 1 we see the times required to make a direct call into the DirectCall method. We can see that a vast majority of the times for all three data sets appear to be about the same and very small. We’ll look at the actual mean values later, but this plot shows some important points:

    1. The managed calls occasionally have gigantic outliers. We have a couple calls that took over 15,000 ticks (over 12 milliseconds on the test device).
    2. Even the native code has some jitter (we see a maximum of 1024 ticks, or about 860 microseconds) but the jitter, or outlier variation from the mean, is much less pronounced than the managed code.
    3. We see the same outlier frequency whether or not we have GC occurring.

     

    So from this plot we see that managed code appears to inherently increase the overall jitter of the system by over an order of magnitude. When you’re designing a system that must perform a task within a certain time boundary, you must look at the worst-case scenarios for your maximum speed, so just on this small sample of raw data we would say that we could safely run native code over 15 times faster than managed (15955 / 1024). Of course this is assuming that the P/Invoke call is part of the time-critical logic.

    Figure 1


     

    Figure 2 is shows the same data but with the Y-axis rescaled to show just the low-end mean data. The interesting point that we see here is that the average speed to make the call from managed code is about half the speed of the native call.

    Figure 2


     

    Direct Call Return

    In Figure 3 we look at the time required to return from a method. Again, it looks like the mean for the native and the managed tests are about the same and very small, but we still see some critical points:

    1. Both the managed and native code have occasional outliers
    2. The managed outliers are an order of magnitude larger than those from the native code.

     

    Figure 3


     

    Figure 4 again is the same data as Figure 3, but with the Y-Axis rescaled. You can see that the managed code appears to be slightly faster on average (it looks like the “blue” area extends about 1 tick below the green) but all three data sets seem to be about the same.

    Figure 4

     


     

    Returning Through a Callback

    Figures 5 and 6 show the results for the callback times and are the most interesting of the group. Figure 5 is simply the same data as Figure 3 but with the Y-Axis scaled to show just data with times below 600 ticks.

    Right away we see the same trend as the earlier plots – that the managed code produces outliers much greater in magnitude than the native code. What’s different here, though, is the frequency of those outliers. Instread of just a couple over the 10,000 sample data set, we see a dozen very large outliers (with the GC data set) and still a lot more low-end variation (all the spikes above the average but below around 3000 ticks).

    Figure 5


     

    If we zoom in on the low-end data, Figure 4 show some interesting points as well.

    1. The average for the managed callbacks is substantially higher (turns out to be over 10 times slower as we’ll see later) than native callbacks.
    2. Managed callbacks are never as fast as a native callback
    3. GC appears to have a larger impact on callbacks than anything else we’ve looked at

     

    Figure 6


     

    Summary Data

    Below are tables that summarize the collected data. The upper pair show statistics for the direct call and return. The lower pair show the callback (call and return statistics). You can see that the number solidify the perceptions we got from looking at the plots. Making a P/Invoke call does indeed take almost twice as long as the comparable native call. And the return is even 20-30% slower.


    outDirect

    backDirect


    Native

    Managed- No GC

    Managed - with GC

    Native

    Managed- No GC

    Managed - with GC

    Min

    16

    20

    22

    13

    11

    11

    Max

    16

    15761

    15955

    12

    544

    16117

    Mean

    17

    33

    32

    11

    14

    16

    Median

    17

    28

    28

    11

    13

    13

    Mode

    17

    28

    28

    13

    13

    13

    % of Native

    100.0%

    51.5%

    53.2%

    100.0%

    79.9%

    69.8%
















    outCallback

    backCallback


    Native

    Managed- No GC

    Managed - with GC

    Native

    Managed- No GC

    Managed - with GC

    Min

    15

    13

    13

    12

    135

    137

    Max

    907

    15207

    870

    506

    16295

    16779

    Mean

    18

    19

    16

    14

    190

    271

    Median

    18

    15

    15

    14

    165

    245

    Mode

    18

    16

    16

    14

    164

    243

    % of Native

    100.0%

    97.4%

    116.3%

    100.0%

    7.5%

    5.2%

    Analyzing the Data

    So now we have a whole lot of data. Data is fine, but what we really need is information. Looking at these results raises more than a couple questions that we can’t readily answer without access to the CLR itself. I don’t have access to that kind of information, but I do have access to people who do, so I sent off some questions to the developer who owns the marshalling code for the Compact Framework to see if we could get some answers.

    To his credit he did a lot of footwork on this, injecting stubs into the CLR and looking at outputs from the JIT compiler. He sent me several excellent explanations for several question complete with assembly code. As a managed developer (and even as a native developer) looking at assembly is like looking at a gorgon and we really only need to know the general answers, so I’ll save you the petrification and summarize in somewhat less technical terms.

    Why is the P/Invoke call half the speed of the native call?

    The managed call doesn't directly call the native method. Instead it calls into a JITted stub method that must perform some overhead routines such as calls to determine GC Preemption status (to determine if a GC is pending and we need to wait). It is also possible that some marshalling code will get JITted into the stub as well. This all takes time. Also keep in mind (as pointed out back in the “Collecting Compact Framework Data” section earlier) that the managed test itself is actually timing the return from the QueryPerformanceCounter (QPC) P/Invoke as well.

    Why is the direct return 20-30% slower in managed?

    Just like for the outbound call, the return in managed code also includes a JITted stub that does some GC housekeeping to see if a GC is pending plus we still have our QPC return time which slows the managed test.

    Why are callbacks so crazy expensive in managed code?

    The deep answer to this is a complex answer complete with a lot of assembly code that no reasonable developer really wants to read. What it boils down to, though, is this:

    The IntPtr returned by GetFunctionPointerForDelegate is not really the pointer for the JITted callback. It's actually a pointer to a stub that does some additional marshaling. That stub in turn calls several different functions down in mscoree, so the route is not straight and your call stack for the callback itself is fairly deep.

    These calls check to see if we're marshaling managed types, packages and loads any arguments into buffers, makes the call and then packages and marshals any return value. Throughout this process the CLR is also doing bookkeeping on the thread states, checking and adjusting GC preemption status, looking for events and exceptions and potentially sending out debugger and/or profiler information. During all of this locks are getting acquired and released and memory is getting allocated, so a GC can potentially be triggered during the process as well.

    So a whole lot more is going on that simply jumping execution to the callback function address as happens in native code.

    Why is the directcall in 2x slower than native, but the call in for the callback is about the same as native?

    This one is baffling and without some really deep investigation with a native profiler it difficult to say exactly what is happing. We would have expected the results to be the same. There may be some issues with code alignment, memory controller caching or something else that isn’t obvious that is affecting this result. For now it remains a mystery but it’s likely to be an artifact of the environment, not the code itself.

    What accounts for the diff between the two managed callbacks?

    We see that individually they have a pretty solid mean, yet they differ by 40%. Why? And what’s with the “steps” in the data? The start the same – is the “higher” level due to GC activity. Does that make sense that the “with GC” line would hop up after a short period and stay, since we’re creating garbage as fast as we can? If that’s so, then why does the “no GC” hop up to about the same level and then stay there for a period before dropping back down?

    These are all reasonable questions and unfortunately without a ton of work with both native and managed profilers the only thing we can do is make educated guesses. My guess is that the difference is that the additional thread doing lots of allocation is contending for the same memory allocation locks that the callback "goo" is using, and it's the contention for these locks that's causing the callback to take longer. We might have some thread context switching that is being done in one case and not the other. Still your guess is probably as good as mine, and it’s not likely that we’ll have a definitive answer soon (if ever), but I don’t think we really need one. The lesson we’re really interested is that the managed callbacks are an order of magnitude slower than native callbacks. A small jitter on exactly how much isn’t terribly important in the overall scheme of things.

    Conclusion

    So what have we learned here? I’d say we can sum it up in a few points:

    1.       Managed callbacks, while extremely useful in some cases, are an order of magnitude slower than native callbacks.
    It’s probably advisable to not rely heavily on them or purposely architect a solution to use callbacks if it can be avoided.

    2.       Managed P/Invoke calls are slower than native method calls.
    While the exact amount can vary, and the difference is small, the fact remains that a P/Invoke is slower. If you’re doing work where time is critical it’s important to keep this in mind.

    3.       Managed code has a lot of jitter that must be considered in any solution.
    The nature of managed code means that we have a garbage collector doing stuff for us in the background, and when it does it’s work, the performance of other threads is impacted and impacted severely. This isn’t just true for C# or VB.NET. Any runtime with a collection mechanism (which includes Java) is going to have this problem. This means that if you have deterministic constraints in your solution, they either need to have a lot of room for sway or you need to be using native code.

    I specifically not using the term “real-time” here because almost all code needs some level of determinism. When a user clicks a button in your app, if it takes 30 seconds to respond that’s still unacceptable, so you do have some deterministic requirement. If you’re measuring the real-time speed of continental drift, a few seconds probably isn’t going to matter much. However if you’re trying to run a precision machine tool in a factory, a hundred milliseconds can easily mean a ruined part or broken tool.

    The point is that you need to understand your solutions requirements and understand the limitations of the code you’re writing. Don’t dismiss managed code out-of-hand because of some general (often incorrect) perception that it’s slow and likewise don’t architect it into a solution with tight time requirements just because it’s the environment you know. Both good and bad code can be written in any language and the difference is usually knowledge and experience. The hope is that now you have a little more of that and we can all try not to add to the large pool of bad code out there.

     

  • An Introduction to WCF for Device Developers

    An Introduction to WCF for Device Developers

     

    Chris Tacke

    OpenNETCF Consulting, LLC

    November, 2007

     

    Download the Source Code
      Download the PDF

     

    Introduction

     

    With the release of Visual Studio 2008 to MSDN subscribers last week, I decided that it was probably safe enough to install the tool and start using it, plus I’m working on a large project where we’re soon going to need to be able to communicate between a PC and a CE device.

     

    Conceptually (and at only a very high level) I had an idea what Windows Communication Foundation (WCF) is and my guess was that it could be used to help solve the problem at hand, which is to generate some form of remoting capability.

     

    Of course I’d never actually created anything that used WCF, nor even looked at WCF code except in some conference sessions, so the first step was to head to the bookstore. I picked up Inside Windows Communication Foundation from Microsoft Press and tried to give it a read. As is typical for me, I found most of it analogous to listening to a white noise machine. It did little to further my understanding of the subject, but left me annoyed and tired.

     

    In all fairness I can’t say it’s a bad book, I just learn far better from concrete examples and hand-on work than I do from reading about doing something. Chapters 6 and 7 which cover Channels actually was interesting and useful and I actually read them rather than skimming. Chapter 6 gave me enough to say that the $40 I spent on it was worth the investment.

     

    So once I was done with the book and a couple beers, I decided it was time to roll up my sleeved and start searching the web for some concrete examples so I could walk through some code in a debugger and learn by breaking stuff.

     

    Little did I know that while there’s a lot of talk about WCF, finding a simple example that comes complete with source code for both ends is not a trivial task. This is especially true when it comes to WCF for devices – there simply is no full sample (that I could find anyway) that explained how to go from nothing but Studio to two devices talking to one another, step by step, with all the code and directions needed.

     

    So after a day and a half of stumbling around and cussing, I finally ended up with something worked and that probably should have taken only an hour to develop. This white paper is a collection of the knowledge I gained, and that single source of a WCF sample that some other developer is now, or will be looking for in the near future.

     

    Requirements

     

    Before you get started, make sure that you have everything tool-wise that you’re going to need.

     

    1.       Visual Studio 2008 (used to be called Orcas) RTM. I downloaded mine from MSDN. If you got yours elsewhere, then your mileage may vary through this article.

    2.       Power Toys for .NET Compact Framework 3.5 CTP (September 2007). This one wasn’t obvious until I’d done a fair bit of searching. You *will* need it, so download and install it now. The current link is http://www.microsoft.com/downloads/details.aspx?FamilyID=C8174C14-A27D-4148-BF01-86C2E0953EAB&displaylang=en but if you’ve been browsing Microsoft’s site for years like I have, you know that that link may be dead next week, so search for title text if it fails.

    3.       A Windows Mobile 5.0 device. In theory you should be able to use any device that is supported by CF 3.5 (Windows CE 5.0 or 6.0 or WM 5.0 or 6.0), but I used an Axim x51. I can say that I tried for some time to get them WM 5.0 emulator working and failed, so you should at least start with a physical device to remove that variable.

    4.       Windows XP development environment. While this isn’t necessarily a requirement, I’m running XP, not Vista, so if there are any UAC steps that are needed to get this working you’re not going to see them in this article. If you’re using Vista, I wish you luck. Please post any necessary deviations as comments.

     

    The PC-side WCFServer application

     

    To make things fairly simple we’re going to use a transport shipped with WCF for devices and set up the server application to run on the PC (sure I need the reverse in my target solution, but as Bill Murray said in What About Bob – baby steps).

     

    So the first thing we need is to create a service that does something on the desktop. We’re not going to go overboard and make it complex, we’re going to have a single function that takes in two integers and returns the sum of them. My thinking is that once you see that working, getting more complex stuff working is simply additional baby steps on top of that.

     

    So first, create a desktop Console application (if you were in a rush and already created a WinForms app before you got this far in the article, delete the Form and change the project type in the Project Properties just like I ended up doing when I was creating this app).

     

    Add the following reference to the project:

    System.ServiceModel

     

    Now add a new Class (well it will be an interface, but using the Add Class menu item and then changing the code is less clicking) document named ICalculator and replace its code with the code below. This is the interface that defines the contract our service exposes. It should all be self explanatory, so I won’t bore you with a walk-through.

     

    using System;

    using System.ServiceModel;

     

    namespace OpenNETCF.WCF.Sample

    {

        [ServiceContract(Namespace = "http://opennetcf.wcf.sample")]

        public interface ICalculator

        {

            [OperationContract]

            int Add(int a, int b);

        }

    }

     

    Now we need to create an implementation of that interface to expose as our actual service. Add another Class to the project called CalculatorService and overwrite the generated code in the document with the code below. Again, it’s not rocket science, so we’re not going to cover what it does.

     

    using System;

     

    namespace OpenNETCF.WCF.Sample

    {

        public class CalculatorService : ICalculator

        {

            public int Add(int a, int b)

            {

                Console.WriteLine(string.Format(

                    "Received 'Add({0}, {1})' returning {2}", a, b, a + b));

                return a + b;

            }

        }

    }

     

    The last remaining piece of our service app is the logic that actually creates an instance of our CalculatorService implementation and exposes it. This code is a little more complex (though not a whole lot), so we will look at what it’s doing, but first, overwrite all the code in your existing Program.cs file with the following code:

     

    using System;

    using System.ServiceModel;

    using System.ServiceModel.Description;

    using System.Net;

     

    namespace OpenNETCF.WCF.Sample

    {

        class Server

        {

            static void Main(string[] args)

            {

     

                string hostIP = Dns.GetHostEntry(

                Dns.GetHostName()).AddressList[0].ToString();

     

    #if CLIENT_DISCOVERY_BUILD

                Uri address = new Uri(string.Format(

                    "http://localhost:8000/calculator", hostIP));

    #else

                Uri address = new Uri(

                    string.Format("http://{0}:8000/calculator", hostIP));

    #endif

                ServiceHost serviceHost = new ServiceHost(

                    typeof(CalculatorService), address);

     

                try

                {

                    // Add a service endpoint

                    serviceHost.AddServiceEndpoint(

                        typeof(ICalculator),

                        new BasicHttpBinding(),

                        "Calculator");

     

    #if CLIENT_DISCOVERY_BUILD

                    // Enable metadata exchange

                    // this is needed for NetCfSvcUtil to discover us

                    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();

                    smb.HttpGetEnabled = true;

                    serviceHost.Description.Behaviors.Add(smb);

    #endif

     

                    serviceHost.Open();

     

                    Console.WriteLine(

                        "CalculatorService is running at " + address.ToString());

                    Console.WriteLine("Press <ENTER> to terminate");

                    Console.ReadLine();

     

                    // Close the ServiceHostBase to shutdown the service.

                    serviceHost.Close();

                }

                catch (CommunicationException ce)

                {

                    Console.WriteLine("An exception occured: {0}", ce.Message);

                    serviceHost.Abort();

                }

            }

        }

    }

     

    The first thing you’ll probably notice is my use of a precompiler directive. The reason for this is that we need to run this service once and then have a tool (that we’ll discuss shortly) connect to it and generate some code for us. Once that’s been done we want to remove and change some stuff, but if you extend this example you’re going to have to go back and have that tool do its work again, so it’s handy to keep the old code around. What I did (and you can see this in the code download) is add another configuration based on the Debug configuration to the solution called “Discovery” that sets CLIENT_DISCOVERY_BUILD, so turning this on or off is a simply matter of changing the target configuration.

     

    So what does this code do exactly?

     

    First it gets the local PC’s IP address (which we then use later on. Next it creates an address URI for our service. You’ll see that I use localhost for the discovery run. The reason for this is that the tool (that we’ll discuss shortly – be patient) actually hard-codes this into some other code. We’re going to use string substitution in our client and replacing “localhost” is way easier than searching through a string for some IP that may change between tool runs.

     

    Next we create the ServiceHost and add an endpoint. The important pieces to note here are that we’re using BasicHttpBinding, which is one of only 2 bindings that the CF version of WCF supports (the other is email through Exchange ‘07), and that we give a name to the service, which gets used later, but through generated code.

     

    Next we add the ability to exchange metadata only if we’re in the Discovery configuration. The reason for this is that the tool (wait for it!) can only connect to our service and generate code if this is turned on. I turn it off otherwise simply because it seems like a potential security hole if you leave something like that on in the wild. Maybe not, but we’ll play it safe until experience (or someone who knows more about WCF) tells us that we should do otherwise.

    Finally, we open the service and output some stuff on the Console, waiting for the user to hit Enter to end the app.

     

    That’s all there is to it, so make sure that CLIENT_DISCOVERY_BUILD is set and run your app, and you’ll get something exciting like the screen shot below.

     

     

     

    Now let’s do a quick sanity check to make sure it’s actually exposing itself as a service. Open a browser on your PC and browse to the address that the service reported (http://localhost:8000/calculator). You should see something like this:

     

     

     

    Tell everyone around you to stand in awe – you have created the mythical WCF Service.

     

    The NetCFSvcUtil.exe Tool

     

    Yes, now we’re going to actually talk about the tool that we talked about. The Power Toys for .NET Compact Framework 3.5 ship with a tool called NetCFSvcUtil.exe which is a lot like its desktop counterpart (that you won’t be using here – believe me, I tried). This tool will connect to your service using the metadata exchange that you enabled in your code and output some code for your CF client application to use.

     

    Now I’m not big on code generators – having seen them evolve for many years, one thing that has typically been a truism is that they produce less-than-optimal code, but a quick glance at what this one generates looks reasonable and besides, we’re here to create a WCF app, not criticize code optimization.

     

    Open a command windows and browse to the folder that contains NetCFSvcUtil.exe. The default installation of the Power Toys will put it at C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin. Next, run the following command (again, the service must be running when you do this)

     

    netcfSvcUtil.exe /language:cs http://localhost:8000/calculator

     

    You should get an output that looks like this:

     

     

    C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin>netcfSvcUtil.exe /l

    anguage:cs http://localhost:8000/calculator

    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:8000/calculator' usi

    ng WS-Metadata Exchange or DISCO.

    Generating files...

    C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin\CalculatorService.cs

    C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin\CFClientBase.cs

     

    C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin>

     

     

    And 2 files will be generated in the same folder as NetCFSvcUtil.exe: CalculatorService.cs and CFClientBase.cs. You’ll be using these files in the next step.

     

    The CF Client Application

     

    Now create a Smart Device Windows application named WCFClient and add the following references (failure to add these references will lead to lots of colorful language when the code previously generated by NetCFSvcUtil.exe fails to compile):

    -          System.ServiceModel

    -          System.Runtime.Serialization

     

    Next add the two files created by NetCFSvcUtil.exe to the project. It’s probably best to copy them to the project folder first to prevent cluttering the SDK bin directory with all of your generated source.

     

    Next update Form1 to look something like the figure below. The names of the controls are on the right and if you want to just copy and paste code, you’ll need your control names to match these.

     

     

     

    NOTE

    I was never able to get the emulator to connect to service. I was able to ping the emulator from the PC and it was able to browse to a web site, but it wouldn’t connect to the service. I then tried it on a real device and it worked, so I never bothered to go back and try to figure out why the emulator didn’t work. I highly suggest that you do run this sample on a real device.

     

    Now let’s look at implementing the client. The first step I did was to display the device’s local IP address in the Form constructor. I did this when I was debugging because the emulator would not connect to the service and I was wondering if it had a valid IP address.

     

    Update your Form’s constructor code to look like this:

     

    public Form1()

    {

        InitializeComponent();

     

        try

        {

            localIP.Text = Dns.GetHostEntry(

                Dns.GetHostName()).AddressList[0].ToString();

        }

        catch (Exception ex)

        {

            MessageBox.Show("No NIC found?");

        }

    }

     

    Now we add all of the service “work” in a Click event handler for the calculate button. Add a handler to the button and add the following code to the handler:

     

    private void calculate_Click(object sender, EventArgs e)

    {

        int a = 0;

        int b = 0;

     

        try

        {

            a = int.Parse(first.Text);

            b = int.Parse(second.Text);

        }

        catch

        {

            // parsing failed, just bail out

            return;

        }

     

        SMC.Binding binding = CalculatorClient.CreateDefaultBinding();

        string remoteAddress = CalculatorClient.EndpointAddress.Uri.ToString();

        remoteAddress = remoteAddress.Replace("localhost", serviceAddress.Text);

        EndpointAddress endpoint = new EndpointAddress(remoteAddress);

     

        CalculatorClient client = new CalculatorClient(binding, endpoint);

     

        try

        {

            answer.Text = client.Add(a, b).ToString();

        }

        catch (Exception ex)

        {

            MessageBox.Show(ex.Message);

        }

    }

     

    The important point in this code are that we don’t use the default constructor for the CalculatorClient, because that would end up using a remoteAddress with “localhost” in it, because that’s what the code generator hard-codes it to be. Instead, we extract that value (it’s a static field of the generated CalculatorClient) and then use string substitution to replace “localhost” with the IP address of the PC running the service.

     

    Once we’ve created the CalculatorClient instance, calling the service is trivially simple.

     

    Conclusion

     

    So there you have it - a complete, working, end-to-end example of how to create a WCF service running on a PC and how to consume it using a Compact Framework 3.5 application. As you’ve seen, there isn’t a lot of code to it, and it’s not overly complex to get set up – it’s just not very well documented in any one place.

     

    Now where might you use something like this? One thing that comes to mind is the ability to put functionality on a machine somewhere within your network that can then be called by multiple devices without having to go through all of the configuration and setup required to get an XML Web Service under IIS running. You can then update or fix the service code in one place and all devices using the function immediately get the benefit without having to redeploy or roll out the fix. You could also do computationally-heavy work on a desktop machine with more power, but still do the collection and display on a lightweight mobile device.

     

    At any rate, I hope that this white paper saves someone else a bit of the headache I went through in trying to get a WCF application running. Some additional links that I found at least somewhat useful in researching this are below. Enjoy.

     

    CF WCF Intro materials:

    http://blogs.msdn.com/markprenticems/archive/2007/03/27/introduction-to-windows-communication-foundation-for-the-net-compact-framework-messaging-stack.aspx

     

    The Lunch Launcher

    http://blogs.msdn.com/davidklinems/archive/2007/11/12/the-journey-of-the-lunch-launcher-part-8-what-did-i-learn.aspx

     

    Using NetCFSvcUtil.exe

    http://blogs.msdn.com/danhorbatt/archive/2007/09/12/using-compact-svcutil-to-interact-with-servicecontracts.aspx

     

    Calling WCF Service from CF 3.5

    http://blogs.msdn.com/andrewarnottms/archive/2007/09/13/calling-wcf-services-from-netcf-3-5-using-compact-wcf-and-netcfsvcutil-exe.aspx

     

    The CF 3.5 subset of WCF

    http://blogs.msdn.com/andrewarnottms/archive/2007/08/21/the-wcf-subset-supported-by-netcf.aspx

     

    Remote Logging WCF

    http://blogs.msdn.com/danhorbatt/archive/2007/11/01/remote-logging-wcf-on-net-compact-framework.aspx

     

     

  • Getting a Millisecond-Resolution DateTime under Windows CE

    November, 2007
    Chris Tacke
    OpenNETCF Consulting, LLC

     

    Download the Source Code
     Download the PDF

     
    Introduction

    One seemingly strange behavior that Windows CE devices (including Pocket PC, Smartphone and Windows Mobile devices) exhibit is that when you query the device time, you get back either a zero of some constant but invalid value for the milliseconds field. This is true whether you call GetLocalTime or GetSystemTime in native code or DateTime.Now in managed code. Why is it that devices running an OS that has real-time capabilities can’t provide us as developers with a clock that has simple millisecond resolution? The answer lies not in the OS – which is quite capable of giving that resolution – but in how the time function is implemented by the device OEM.

     

    Typically Windows CE devices contain a piece of hardware internally called a real time clock or RTC. The RTC is very accurate, but often has a resolution of only 1 second. When the OS was implemented, most OEMs simply return the value that the RTC holds and therefore when you query the time that’s all you get.

     

    So the question remains – how can we as developers get a millisecond-resolution time? If you’re simply looking to get the time short tasks take, then often calling GetTickCount or Environment.TickCount (which is the number of milliseconds since the OS started and tied to a separate oscillator in the processor) is sufficient, but what if we want a true time stamp with a millisecond field? The answer is that we simply have to calculate it. In this white paper we’ll look at code that does that calculation and to make our code reusable, we’ll create a simple class called DateTime2 that will expose a Now property that has millisecond resolution.

     

    Calculating the millisecond field

    Since we know that the Environment.TickCount returns the number of milliseconds since startup, we know that we have access to data that can become our millisecond field. By using modulo division on it we can get a “current millisecond” piece of data:

     

    int tick = Environment.TickCount % 1000;

     

    However the problem with this is that there’s only a 1 in 1000 chance that it will be synchronized with the system clock, so it’s more than likely that this millisecond tick will roll over to zero at a time when the system clock is not rolling over. All we have to do then is manually synchronize that rollover. To do that we simply need to find what the value of the above tick is when the clock rolls its second value over. So during initialization of our class, we simply poll the clock repeatedly and quickly until we see the second roll over, then we calculate the offset. Bear in mind that this initialization can take up to 1 second to complete.

     

    private static int m_offset = 0;

     

    static DateTime2()

    {

       int s = DateTime.Now.Second;

       while (true)

       {

         int s2 = DateTime.Now.Second;

     

         // wait for a rollover

         if (s != s2)

         {

           m_offset = Environment.TickCount % 1000;

           break;

         }

       }

    }

     

    So now that we have our offset, we simply use it to calculate our millisecond field every time the Now property is retrieved.

     

    public static DateTime Now

    {

       get

       {

         // find where we are based on the os tick

         int tick = Environment.TickCount % 1000;

     

         // calculate our ms shift from our base m_offset

         int ms = (tick >= m_offset) ? (tick - m_offset) : (1000 - (m_offset - tick));

     

         // build a new DateTime with our calculated ms

         // we use a new DateTime because some devices fill ms with a non-zero garbage value

         DateTime now = DateTime.Now;

         return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Month, now.Second, ms);

       }

    }

     

    Testing our code

    So now that we have a function that is logically sound, how do we check that it really works? First, toss out any idea that you might test or use this on the emulator. The emulator emulates hardware, including the RTC and the system tick and is therefore completely unreliable for any type of time testing (I actually tested in with this code and saw major creep of the millisecond field across the second rollover period).

     

    To test the function on actual hardware, we use logic that is pretty much the same as our offset initialization code. We simply want to watch the system clock roll over, and when it does, we want to get the millisecond field of our DateTime. It should be at or near zero, and more importantly it should be consistent. So let’s look at what a test function might look like.

     

    private void Test()

    {

       int seconds = 5;

       int s = DateTime2.Now.Second;

       while (seconds > 0)

       {

         DateTime dt = DateTime2.Now;

         int s2 = dt.Second;

         if (s != s2)

         {

           System.Diagnostics.Debug.WriteLine("Test ms=" + dt.Millisecond);

           s = dt.Second;

           seconds--;

         }

       }

    }

     

    When I ran this on a Dell Axim x51 I found that the tick repeatedly came out around 998 or so, meaning that my millisecond field would actually roll to zero a couple milliseconds before the actual second field. For many applications this might be fine, but of course we can do better, right? If we can test the inaccuracy, we can always account for it. So let’s add a Calibrate function to our DateTime2 class that will run our test code logic for a period of time, calculate the average offset from zero over that period and then adjust our global offset by that average.

     

    public static void Calibrate(int seconds)

    {

       int s = DateTime2.Now.Second;

       int sum = 0;

       int remaining = seconds;

       while (remaining > 0)

       {

         DateTime dt = DateTime2.Now;

         int s2 = dt.Second;

         if (s != s2)

         {

           System.Diagnostics.Debug.WriteLine("ms=" + dt.Millisecond);

           remaining--;

           // store the offset from zero

           sum += (dt.Millisecond > 500) ? (dt.Millisecond - 1000) : dt.Millisecond;

           s = dt.Second;

         }

       }

     

       // adjust the offset by the average deviation from zero (round to the integer farthest from zero)

       if (sum < 0)

       {

         m_offset += (int)Math.Floor(sum / (float)seconds);

       }

       else

       {

         m_offset += (int)Math.Ceiling(sum / (float)seconds);

       }

    }

     

    After I ran Calibrate for 5 seconds on the same device, I found that Test spit out an offset of zero very repeatedly, which is what we want.

     

    Conclusion

    So we now understand that most CE devices don’t fill the milliseconds field of their system time because it’s not readily available, and we’ve seen that with a little extra code we can derive a reasonably accurate replacement that does. Our new DateTime2 class provides a reasonable utility class that can be used in a lot of applications where you’d like to have a millisecond field.

     

    Keep in mind, however, that what we have here is still based on two separate oscillators on the device and those two will drift apart over time. Your offset might be zero today, but if you run the app for a week I would expect you’ll find it to have drifted a few ticks - how much depends on the hardware, its layout and the temperature the device is at. Also keep in mind that managed code is still nondeterministic. Just because you get a millisecond accurate time doesn’t mean that you can reliably do tasks with millisecond accuracy.

  • Using GDI+ on Windows Mobile

    Alex Feinman
    OpenNETCF Consulting
    October 2007

     

    Download the Source Code
     Download the PDF

    What is GDI+

    GDI+ is a library created to add rich drawing and imaging feature to the basic set of Windows APIs. It has been introduced in Windows 2000 timeframe and eventually became a basis of System.Drawing and System.Drawing.Imaging namespaces. GDI+ is a dual API. It has a set of rich objects, such as Graphics, Bitmap, Pen, Brush etc as well, as a flat API suitable for use in legacy C or for P/Invoking. Starting with Windows Mobile 5 (Pocket PC), GDI+ ships on Windows Mobile devices. Despite the fact that the desktop .NET drawing and imaging support is built on top of GDI+, the Compact Framework does not use it. This in part explains somewhat more Spartan selection of graphic features available in Compact framework. Of course one of the main reasons for this was the lack of universal availability of GDI+. E.g it is not present on 2003 and 2003 SE devices, any version of smartphone (including Windows Mobile  6 Standard) or basic Windows CE.

    It is also important to understand that Windows Mobile version of GDI+ lacks many features of its desktop counterpart. Although it is 100% API-compatible, a lot of calls is simply not implemented. All it does is returning the corresponding status (NotImplemented) to the caller.

    Why GDI+

    As I understand it, the need for GDI+ has been driven by inking support. Transcriber (an Input Method supporting cursive recognition) and in WinMo 6 WISPLite (inking and recognition support) both use GDI+ internally.

    GDI+ allows drawing with antialiasing, transparency and some limited 2D transformation support. Currently, there is no other way to draw an antialiased polyline, curve or other object in Windows CE, than by using GDI+.

    GDI+ supports Rectangles, Pies, Ellipses, Beziers as part of a Path object. It also supports various special types of pens and brushes including gradients.

    Things notably missing from Windows Mobile version of GDI+ are:

    • Text support
    • World Transform support. In general transforms are only supported on a Path object
    • Many image operations

    From the performance standpoint GDI+ is a slow-ish API. It needs to be used judiciously and sparingly. Caching complex objects as bitmaps also helps.

    How

    API access

    GDI+ API consists of 2 parts. One is GDIPLUS.DLL, which exports so-called flat API – a large number of C functions that comprise the entire functional set of the API. The other is a set of C++ objects, found in the include directory of Platform SDK, which encapsulates flat API functionality, provides memory management, cleanup and some useful shortcuts. When using GDI+ from Compact Framework, it is relatively easy to P/Invoke the flat API. The class-based API is a different thing. It is absolutely, 100% incompatible with Compact Framework. It can however be recreated in C# and given its usefulness, it should be.

    Caution needs to be exercised, when using GDI+. The code needs to make sure that the platform indeed has GDIPLUS.DLL and be prepared to deal with the lack of it, for example by falling back to less rich, traditional methods of drawing.

    Session initialization and termination

    Before GDI+ can be used, a call to “GdiplusStartup” needs to be made:

            GdiplusStartupInput input = new GdiplusStartupInput();

            GdiplusStartupOutput output;

            GpStatus stat = NativeMethods.GdiplusStartup(out token, input, out output);

     

    The above code initializes GDI+ subsystem. The parameters can be ignored – there is not much use to them unless you have a debug build of GDIPLUS.DLL, which is not likely. This is also a good place to add try/catch and watch for MissingMethodException. Getting one would typically indicate that your platform does not have GDIPLUS.DLL available.

    When the application is shutting down (or GDI+ is not needed anymore), the following call is required:

            NativeMethods.GdiplusShutdown(token);

     

    Following this call no other GDI+ calls are allowed.

    Managed wrapper

    To be able to use GDI+ in a Compact Framework application, we need to wrap most of the flat API and port the object model. This is done for you in OpenNETCF.GDIPlus.dll assembly. It has a class called NativeMethods, where all of the flat API is exposed. It also has objects matching GDI+ objects. Among those are:

    • GraphicsPlus
    • ImagePlus
    • BitmapPlus
    • GraphicsPath
    • BrushPlus and its derivative classes:
      • SolidBrushPlus
      • HatchBrush
      • LinearGradientBrush
      • PathGradientBrush
      • TextureBrushPlus
    • PenPlus
    • RegionPlus

    Drawing with GDI+

    All drawing in GDI+ is done using GraphicsPlus object, similar to how in .NET all drawing is done using Graphics object. In order to obtain GraphicsPlus object an appropriate constructor needs to be used. For example to draw inside OnPaint override (or wherever .NET Graphics object is available), one would use a constructor that takes HDC:

            protected override void OnPaint(PaintEventArgs e)

            {

                IntPtr hdc = e.Graphics.GetHdc();

                using(GraphicsPlus g = new GraphicsPlus(hdc))

                    PaintControl(g);

                e.Graphics.ReleaseHdc(hdc);

            }

     

    If the drawing target is an off-screen bitmap, then there is a choice of creating a .NET Graphics object on it followed by the above, or using Bitmap.GetHbitmap() and then constructing GraphicsPlus object on HBITMAP. There are some other choices, but mostly of limited use.

    While its desktop peer supports many settings, the Windows Mobile version of Graphics object supports only one – smoothing mode. It can be sent to None, Antialias, High Speed and High Quality. Naturally, antialised output takes the longest time to render.

    Brushes

    Brush objects in GDI+ can be used to draw directly on the screen by filling various shapes as well as to create pens that use brush textures to fill the line. Below are several brush examples:

    BrushDemo.png

    In order to create the above brushes the following code was used:

    1. PathGradient brush
                  // Create rectangular path
                  GraphicsPath path = new GraphicsPath(FillMode.FillModeAlternate);
                  path.AddRectangle(new GpRectF( 0, 0, ClientRectangle.Width, 
                      ClientRectangle.Height / 5));
                  
                  // Create rectangular gradient brush
                  // with red in center and black in the corners
                  brPathGrad = new PathGradientBrush(path);
                  brPathGrad.SetCenterColor(Color.Red);
                  int count = 2;
                  brPathGrad.SetSurroundColors(new Color[] { Color.Black, Color.Black }, 
                      ref count);
    2. Solid Brush
                  brSolid = new SolidBrushPlus(Color.CornflowerBlue);
    3.  Hatch Brush
                  brHatch = new HatchBrush(HatchStyle.HatchStyle25Percent, 
                      Color.Black, Color.White);
    4. Linear Gradient
                  brLinGrad = new LinearGradientBrush(new GpPointF(0, 0), 
                      new GpPointF(50, 50), Color.Black, Color.White);
    5. Texture brush
                  StreamOnFile sf = new StreamOnFile(bitmapPath);
                  ImagePlus img = new ImagePlus(sf, false);
                  brTexture = new TextureBrushPlus(img, WrapMode.WrapModeTile);

     

    Pens

    Pens in GDI+ support thickness, transparency, antialiasing, custom caps, and some other features. In addition a pen can be created solid, hatched and generally based on any brush one can create. Using pens it is possible to draw lines, rectangles, ellipses, beziers and otherwise random paths.

    Pens support start and end caps. Caps can be predefined or entirely custom, based on a path object.

    Below is a sample of the output from various pens used under various conditions:

    PenDemo.png

    Here is how each pen has been created and used:

    1.  Solid with caps. Standard caps are used – round and arrow
                  brSolid = new SolidBrushPlus(Color.CornflowerBlue);
                  penSolid = new PenPlus(Color.Red, 10);
                  penSolid.SetEndCap(LineCap.LineCapRound);
                  penSolid.SetStartCap(LineCap.LineCapArrowAnchor);
    2.  Solid with caps and antialiasing. This one is the same as before except it is drawn with antialiasing
                  g.SetSmoothingMode(SmoothingMode.SmoothingModeAntiAlias);
                  penSolid.SetColor(Color.Blue);
                  g.DrawLine(penSolid, 5, rcf.Top + 10, rc.Width - 10, rcf.Top + 10);
    3. Hatched (25%)
                  brHatch = new HatchBrush(HatchStyle.HatchStyle25Percent, 
                      Color.Black, Color.White);
                  penHatch = new PenPlus(brHatch, 10);
    4. Solid with transparency
                  penSolidTrans = new PenPlus(Color.FromArgb(-0x5f7f7f7f), 10);
    5. Custom cap. The custom cap has been created out of a path object consisting of a single ellipse
                  penSolidCustomCap = new PenPlus(Color.Black, 20);
                  GraphicsPath path = new GraphicsPath(FillMode.FillModeAlternate);
                  path.AddEllipse(-0.5f, -1.5f, 1, 3);
                  CustomLineCap cap = new CustomLineCap(null,path, LineCap.LineCapFlat, 0);
                  penSolidCustomCap.SetCustomEndCap(cap); 
    6. Dash
                  penDash = new PenPlus(Color.Black, 5);
                  penDash.SetDashStyle(DashStyle.DashStyleDot);
    7. Gradient brush-based

                brGrad = new LinearGradientBrush(

                    new GpPointF(0, 0), new GpPointF(100, 100),

                    Color.Black, Color.White);

                penGradient = new PenPlus(brGrad, 30);

     


     

    Demo application

    This whitepaper includes a demo application. The application demonstrates the above techniques as well as shows how to use GDI+ to capture ink with smoothing and antialiasing.

    Main.png

     

    Conclusion

    GDI+ is available on modern Windows Mobile platform that support stylus input. It allows creating graphic applications that are hard or impossible to implement otherwise. Using GDI+ in conjunction with Compact .NET Framework allows a developer to create a rich graphic application without expending substantial effort.

  • Sharing Windows Mobile Ink with the Desktop

    Mark Arteaga
    OpenNETCF Consulting

    September 2007

    Download the Source Code
     Download the PDF

    Contents

    Introducation
    Ink Serialized Format Data
    Sharing Ink With A Custom Application
    Sharing Ink with One Note 2007
    What’s Next
    Conclusion

     

    Introducation

    In the previous article we discussed technical information on the Mobile Ink Library and WISP Lite.  In this article we will discuss how the Ink Serialized Format (ISF) data can be shared on both a Windows Mobile 6 application and an application running on the desktop.

    Ink Serialized Format Data

    Ink Serialized Format (or ISF) is the binary format in which the ink data is persisted as.  This format directly compatible to the Tablet PC ink format and can be interchanged on both the Table PC and a Windows Mobile 6 device.  Using ISF data that was saved on a Tablet PC on a Windows Mobile device, Windows Mobile will ignore any properties in the ISF stream that are not supported but still preserve the properties when saved again. 

    As developer using the Mobile Ink Library you have the option to persist the ink data as either as:

    1. Ink Serialized Format - The ink data using ISF.  This is the most compact representation of ink data.
    2. Base64 Ink Serialized Format - The ink data using ISF and Base64 encoded.
    3. Gif - The ink data in Graphics Interchange Format (Gif)
    4. Base64 Gif - The ink data Graphics Interchange Format (Gif) and Base64 encoded

    In the next sections we’ll look at sharing ISF data in a custom desktop application and a Windows Mobile application and also importing ISF data created on a Windows Mobile device into One Note 2007.

    Sharing Ink With A Custom Application

    Since ISF data produced on the desktop is binary compatible on a Windows Mobile 6 Device and vice-versa I decided to take the common scenario of signature capture on a mobile device and display the data on a custom application written using the .NET Framework running on the desktop.

    The Mobile Ink Library includes a sample called InkSerializationCF which captures a users signature and embeds an ExtendedProperty within the ISF stream.  We will use this sample to show how the ISF data can be shared with the desktop.

    Note, if you want the try this sample on the desktop you will need to install a few SDKs.  Here is a chart summarizing what is need on different operating systems.

    Operating System

    SDK Requirements

    Windows Vista
    Windows XP Tablet PC 2005

    Windows Vista SDK

    Windows XP

    Windows XP Tablet PC SDK
    Windows Vista SDK

     

    Changes Since First Release

    Since the Mobile Ink Library was first released, I have gone ahead and made some changes to align the classes more with the Microsoft.Ink classes available. 

    The first change was to the OpenNETCF.WindowsMobile.Ink.InkOverlay class.  This class has been renamed to OpenNETCF.WindowsMobile.Ink.Ink class.  The InkOverlay class has been marked obsolete and is still available.

    In the above diagram you will notice in the OpenNETCF.WindowsMobile.Ink.Ink class we also added a new property called ExtendedProperties.  These are the custom properties you can add to an ISF stream.  In the case of the signature sample application we add the date the signature was saved, the first name and last name of the person signing.

    Next is the InkControlBase class.  We have added a new property called Ink which exposes the Ink object discussed above.  Since we are using the OpenNETCF.WindowsMobile.Ink.InkPicture control as a base class for the OpenNETCF.WindowsMobile.Ink.InkSignature control and since InkPicture inherits from InkControlBase, this will help us keep the same object model as the Microsoft.Ink.InkPicture control.  Here is a class diagram of the controls in the Mobile Ink Library:

    Having a similar object model allows us to use the same InkSignature control on both the Windows Mobile application and the Desktop application.

    Sharing the InkSignature Control

    Now that some appropriate changes have been made to the Mobile Ink Library classes to align with the Microsoft.Ink classes, we can now look at how we can share ISF data generated from a Windows Mobile 6 device within a desktop application.

    The InkSignature control is a custom control that will capture signature data.  It derives from InkPicture and since we change the classes to align more with the desktop we just have to change the internal implementation of InkSignature to be able to share the control on both the desktop and the device.  (For more information on sharing code between .NET Compact Framework and .NET Framework see this article in MSDN Magazine.)  By using conditional compile statements we are able to have code in the same source file for both the device and the desktop.

    To implement the code sharing we will be working with two separate solutions.  The first is InkSerializationCF which is a .NET Compact Framework project for Windows Mobile 6 device.  The second is InkSerializationFx which is the .NET Framework application that runs on the desktop.  The InkSerializationFx application will only be used to view any signatures saved via the InkSerializationCF application.

    The main change to accomplish the code sharing is to the InSignature.Open(string) method.  Here we have used conditional compiles to allow us to use on both the desktop and device.

    #if !Fx

    public override void Open(string file)

    #else

    public void Open(string file)

    #endif

    {

    #if !Fx

        base.Open(file);

    #else

        this.InkEnabled = false;

        StreamReader sr = new StreamReader(file);

        byte[] data = Encoding.ASCII.GetBytes(sr.ReadToEnd());

        sr.Close();

        this.Ink = new Microsoft.Ink.Ink();

        this.Ink.Load(data);

        this.Refresh();

    #endif

     

        //See if the ISF file opened is valid

        object ret = this.Ink.ExtendedProperties[new Guid(m_sigValidationGuid)].Data;

        if (ret == null)

        {

    #if !Fx

            this.Clear();

    #else

            this.InkEnabled = false;

            this.Ink = new Microsoft.Ink.Ink();

            this.InkEnabled = true;

    #endif

            this.m_firstname = "";

            this.m_lastname = "";

            this.m_dateSigned = DateTime.MinValue;

            throw new Exception("Invalid signature file!");

        }

     

        //Retreive the firstname extended properties

        ret = this.Ink.ExtendedProperties[new Guid(m_firstNameGuid)].Data;

     

        this.m_firstname = ret.ToString();

     

        //Retreive the lastName extended properties

        ret = this.Ink.ExtendedProperties[new Guid(m_lastNameGuid)].Data;

        this.m_lastname = ret.ToString();

     

        //Retreive the date the sign was signed

        ret = this.Ink.ExtendedProperties[new Guid(m_dateSignedGuid)].Data;

        this.m_dateSigned = DateTime.FromFileTime(long.Parse(ret.ToString()));

    }

    The Open() method will extract the signature information, and all the ExtendedProperties that contains the date signed, and first and last name.  From the above code you will notice not many changes are needed to be able to use the same source file on the desktop and on the device. 

    In the InkSerializationFx project, we need to add a reference to Microsoft.Ink (since OpenNETCF.WindowsMobile.Ink is not supported on the desktop).

    Then we also have to add a using statement with a conditional compile so InkSerializationCF does not use it:

    #if Fx

    using Microsoft.Ink;

    using System.IO;
    #endif

    A conditional compile statement was also added to the Save() method since the InkSerializationFx does not allow any modification of the signature data.

    #if !Fx

    public void Save(string filename, string firstName, string lastName, InkPersistenceFormat format)
    {

    ...

    }

    #endif

    The InkSerializationCF sample was also modified to save the ISF data in different formats.  One thing to note is when sharing the ISF data with the desktop, you can only use the ISFBase64 format as the regular ISF stream will not load.

    After all this talk about code let’s see the results.  Here are the results of a signature on a Windows Mobile 6 device.

    And the results on the desktop.

    For the desktop signature application, the signature is read-only as on the desktop you don’t want the signature to be modified.  You can add more ink to the ISF file and if you try to load it on the device, you will see those changes.  I will leave it up to the reader to explore that scenario.

    Now that you have knowledge on sharing ink data between a desktop application and a Windows Mobile device, next we will look at sharing the ISF data with One Note 2007.

    Sharing Ink with One Note 2007

    Since ISF data is compatible on the desktop and a Windows Mobile 6 device,  I decided to explore one of the comments in the ‘Readme.txt’ file in the Windows Mobile 6 SDK which said:

    “These ink notes can also be imported to OneNote on the desktop.”

    Well, not being a regular user of One Note but seeing the value this can possibly have in an application I decided to explore this comment a bit. 

    Integration with OneNote

    When InkNotesCF was first ported from the native sample it allowed the user to save the note in a certain Xml format as follows:

    <?xml version="1.0"?>

    <Import xmlns="http://schemas.microsoft.com/office/onenote/2004/import">

      <EnsurePage path="Pocket Notes\InkFiles.one" guid="3855d272-28de-4a3f-b157-4eca51c5d15a" title="\Storage Card\note2003.ink" />

      <PlaceObjects pagePath="Pocket Notes\InkFiles.one" pageGuid