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="476d389f-a48b-4891-b665-148a6ed7d9d1">

        <Object guid="">

          <Position x="0" y="0" />

          <Ink>

            <Data></Data>

          </Ink>

        </Object>

      </PlaceObjects>
    </
    Import>

    As you can see from the Xml namespace the version of OneNote that was used when the native sample was created was OneNote 2003.  This Xml format was also ported to the managed InkNoteCF sample. 

    With the release of OneNote 2007, the COM API has changed which also resulted in a new Xml schema to import ink data into OneNote.  We will not go through the changes but for more information on OneNote 2007 from a developers perspective see ‘What’s New for Developers in OneNote 2007 (Part 1)’ and ‘What’s New for Develoeprs in OneNote 2007 (Part 2)

    The new Xml structure for importing data into one is as follows:

    <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2007/onenote" ID="{0}">

      <one:Title>

        <one:OE author="Mark Arteaga" lastModifiedBy="Mark Arteaga">

          <one:T><![CDATA[\Storage Card\note2003.ink]]></one:T>

        </one:OE>

      </one:Title>

      <one:Outline>

        <one:OEChildren>

          <one:OE>

            <one:InkParagraph>

              <one:InkWord>

             <one:Data></one:Data>

              </one:InkWord>

            </one:InkParagraph>

          </one:OE>

        </one:OEChildren>

      </one:Outline>
    </
    one:Page>

     

    As you can see, the new Xml format (or schema) has changed from OneNote 2003.  If you would like to explore the schemas you can download the ‘2007 Office System: XML Schema Reference’.  I’ll leave it up to the reader to explore the schemas and other technical information.

    To import the ink data into OneNote you will need to use the OneNote COM API.   In the sample program that imports the data we use a C# application and it automatically generates a interop assembly called Microsoft.Office.Interop.OneNote.  (for more information on this see this section in the ‘What’s New for Developers in OneNote 2007 (Part 1) article)

    The basic flow to create a new note in OneNote is this:

    Within the import application, the ink data generated by InkNoteCF is saved to the ‘Unfiled Notes’ section of OneNote.  The actual code looks like this:

    Microsoft.Office.Interop.OneNote.ApplicationClass onApp = new ApplicationClass();

    try

    {

        //Get the path for the unfiled notes

        string unfiledNotesPath;

        onApp.GetSpecialLocation(SpecialLocation.slUnfiledNotesSection, out unfiledNotesPath);

       

        //Get the ID of the unfiled notes

        string UnfiledNotesID;

        onApp.OpenHierarchy(unfiledNotesPath, "", out UnfiledNotesID, CreateFileType.cftNone);

       

        //Create a new page

        string pageID;

        onApp.CreateNewPage(UnfiledNotesID, out pageID, Microsoft.Office.Interop.OneNote.NewPageStyle.npsBlankPageWithTitle);

     

        //Read the file data

        StreamReader sr = new StreamReader(txtInkNote.Text);

        string data = sr.ReadToEnd();

        data = string.Format(data, pageID);

     

        //Add the content to the one note page

        onApp.UpdatePageContent(data, DateTime.MinValue);

    }

    catch (Exception ex)

    {

        MessageBox.Show(ex.ToString());

    }

    As you can see from the code, it’s not very difficult to import data into OneNote 2007.

    Changes To InkNoteCF

    When the InkNoteCF sample was first released, it only supported saving the data in OneNote 2003 format.  Since I did not have OneNote 2003 and only had 2007, I refactored the code to allow the user to save in either the OneNote 2003 or OneNote 2007 format.  In this section we will go over some of the changes done. (Note, if you are interested in importing the ink to OneNote 2003 see this article on MSDN)

    The first thing that was modified was the Note class.  The original one only had support for OneNote 2003 so a new NoteBase class was created to accommodate the two different versions.  The new class hierarchy is show in the following diagram.

     

     

    In the main user interface, a new menu item called ‘Format’ was added to allow the user to save the ink note as either OneNote 2003 or OneNote 2007 format.

    I’ll leave it up to the user to delve into the code and to see how the saving code works.  In a nutshell, it just calls the XmlDocument.Save() method.

    Running the Code

    After all this background information you are probably thinking, what is the result?  Well, here are the screen shots of the application on the device and in OneNote 2007.

     

    What’s Next

    From here the Mobile Ink Library is in a good state to be used in managed applications.  We are hoping to improve it some more by wrapping some of the WISP Lite interfaces and align the object model more closely with the desktop.  At this point we have no plans to do that but invite the community to contribute to the project.  You can download the source code for Mobile Ink Library and the Samples at www.opennetcf.com/opensource/mobileink.ocf.

    Conclusion

    The new WISP Lite API available in Windows Mobile 6 gives the native developer the ability to ‘ink enable’ a Windows Mobile application.  Using the Mobile Ink Library from OpenNETCF, .NET Compact Framework developers can now also ‘ink enable’ their applications.  In this article we discussed how using the Mobile Ink Library, you can easily share ink data with a custom application or with OneNote 2007.  Have fun Inking!

  • Using the OpenNETCF Mobile Ink Library for Windows Mobile 6

    Mark Arteaga
    OpenNETCF Consulting
    September 2007

    Download the Source Code
    Download the PDF

    Contents

    Introduction
    Technical Overview
    Mobile Ink Library Custom Controls
    Samples Available with the Mobile Ink Library
    Conclusion

    Introduction

    With the recent release of the Mobile Ink Library for Windows Mobile 6, some people are probably thinking 'well what can I use that for'.  In this article we will be going over some technical information and some possible uses for inking on the Windows Mobile 6 platform.

    Technical Overview

    Windows Mobile 6 Inking

    In the latest release of Windows Mobile, there is a new API called Windows Mobile Ink.  What is Windows Mobile Ink?  Where does it come from? Why do you need it? In a nutshell, Windows Mobile Ink is a new Inking API only available for the Windows Mobile 6 platform called WISP Lite.  It is based on Windows Inking Services Platform (WISP) for the Tablet PC. 

    Desktop developers have managed classes available under the Microsoft.Ink namespace to allow them to develop managed applications to take advantage of 'inking' features available on Tablet PC.  This article does not deal with the desktop so I'll leave it up to the user to search the many articles available on inking on the Tablet PC.

    The Windows Mobile 6 SDK contains some examples of using WISP Lite with native code.  Unfortunately if you are a managed developer, there are no managed classes available for your .NET Compact Framework applications.  The Mobile Ink Library addresses this issue and provides a library of classes which wrap the COM interfaces and the controls that are available on the Windows Mobile 6 platform.

    Differences from the Desktop and Device

    The Mobile Ink Library and WISP Lite allows a developer to add inking functionality to their Windows Mobile application.  WISP Lite (as the name implies) is a 'lighter' version or a subset of WISP available on the desktop.  For the managed desktop developer, there is the Microsoft.Ink namespace which provides inking capabilities in your .NET Framework application.  For the native developer there is also a COM API available to add inking capabilities to a native application. 

    Currently for inking capabilities on the Windows Mobile 6 platform there are only COM interfaces and a 'windowed control' named InkCanvas available to native developers.  Managed developers using .NET Compact Framework don't have any managed classes available but can wrap the COM interfaces and the InkCanvas control using version 2 of .NET Compact Framework.  The Mobile Ink Library has already done this and is available under the OpenNETCF.WindowsMobile.Ink namespace.  This gives the managed developer the ability to take advantage of this new APIs available. 

    Both the desktop and the Windows Mobile versions of inking APIs allow the developer to save the ink in a format called Ink Serialized Format or ISF.  The ISF data output, which can be saved as Base64 or Binary, is compatible on both the desktop and mobile device.  If you try to open an ISF stream on mobile device that was saved on the desktop, the mobile device will ignore any properties in the stream that are not supported on Windows Mobile but still preserve the properties when saved again.  See this page for more details on the differences between the desktop and device.

    COM Interfaces

    There are various COM interfaces available for Windows Mobile Ink and for details on using it with native code see 'Using the Automation Library with Windows Mobile Ink'.  The Mobile Ink Library wraps the appropriate interfaces to allow a managed developer to ink enable a .NET Compact Framework application.  Two interfaces that are primarily used and will be covering next are IInkDisp and IInkOverlay.

    IInkOverlay

    An IInkOverlay object is used to attach to a window (or control) using the IInkOverlay.hWnd property.  When you 'attach' a window to an IInkOverlay object, the window becomes 'ink enabled' and allows the collection of ink data. 

    IInkOverlay also allows you to change the size, shape and color of the ink using the IInkOverlay.DefaultDrawingAttributes property.  The following example changes the size of the pen width using a TrackBar.Value property.

    IInkDrawingAttributes attrs = overlay.DefaultDrawingAttributes;
    attrs.Width = (float)trackBar1.Value;
    overlay.DefaultDrawingAttributes = attrs;

    An example use of IInkOverlay is attaching it to a PictureBox and adding ink notes to an image.  The following code sample uses the OpenNETCF.WindowsMobile.Ink.Ink class to achieve this:

    PictureBox pb = new PictureBox();
    //Create a new InkOverlay object
    OpenNETCF.WindowsMobile.Ink.Ink ink = new OpenNETCF.WindowsMobile.Ink.Ink();
    //We need to set the handle (or control) the inkoverlay object is bound to
    ink.IInkOverlay.hWnd = pb.Handle;
    ink.IInkOverlay.Enabled = true;

    To change the editing mode of the Ink object the EditingMode property can be set with one of the following values:

    public enum InkOverlayEditingMode
    {
        Ink = 0,
        Delete = 1,
        Select = 2
    }

    IInkOverlay exposes a property called Ink which gets or sets the IInkDisp object that is associated with the IInkOverlay object.

    IInkDisp

    IInkDisp is responsible for collecting the ink strokes from a user.  It also has the ability to Save() and Load() the ink strokes collected by an IInkDisp.  To create an instance of IInkDisp, use OpenNETCF.WindowsMobile.Ink.InkDisp class has one static method called CreateInstance().  

    Usually you will want to create a new IInkDisp when you want to clear existing ink in an IInkOverlay.Ink.  An example of this is:

    IInkDisp inkDisp = InkDisp.CreateInstance();
    overlay.Enabled = false;
    overlay.Ink = inkDisp;
    overlay.Enabled = true;

    Note, to set the IInkOverlay.Ink property, you must first set IInkOverlay.Enabled to false and then once you set the IInkOverlay.Ink property set the IInkOverlay.Enabled back to true. 

    When saving the ink strokes, the Save() method returns a byte[]  and there are four possible options defined in OpenNETCF.WindowsMobile.Ink.InkPersistenceFormat:

    ·       InkSerializedFormat - The ink data using ISF.  This is the most compact representation of ink data.

    ·       Base64InkSerializedFormat - The ink data using ISF and Base64 encoded.

    ·       Gif - The ink data in Graphics Interchange Format (Gif)

    ·       Base64Gif - The ink data Graphics Interchange Format (Gif) and Base64 encoded

    These options allow the developer to save in various formats depending on their application requirements.  For example, if you are emailing the collected ink, the receiver may not be able to render the ISF format so you will probably want to send it as a Gif image attached to the email.

    IInkDisp has a property call ExtendedProperties which allows the you to save custom data within the ink data.  For example, using ExtendedProperties you can 'embedded' some data to know when the ink data was last saved.  One caveat is ExtendedProperties are only persisted when saving as either InkSerializedFormat or Base64InkSerializedFormat so if you save as a Gif or Base64Gif, this data will be lost.

    That summarizes IInkOverlay and IInkDisp which are probably the two main classes needed for inking.  I'll leave it up to the reader to explore some of the other methods and properties available in these interfaces and some of the other interfaces available.

    InkCanvas Control

    The InkCanvas control is a windowed control and allows the developer to avoid using the COM Interfaces for inking in their application.  In addition to the COM Interfaces wrapped by Mobile Ink Library, the library also includes a managed wrapper appropriately called InkCanvas (found under OpenNETCF.WindowsMobile.Ink.InkCanvas). 

    InkCanvas simplifies capturing ink within your application.  It uses windows messages to request operations and notify the user of any changes like adding or deleting ink.  If using the IInkDisp COM object is desired, the OpenNETCF.WindowsMobile.Ink.InkCanvas.Ink property can be used.  InkCanvas also has the following properties which may be useful

    ·       Mode - Gets or sets the editing mode

    ·       Ink - Gets the IInkDisp object

    ·       BackColor - Gets or Sets the background color

    ·       ZoomLevel - Gets or sets the zoom level

    ·       CanvasSize - Gets or sets the canvas size

    ·       RecognizedText - Gets the recognized ink as text

    ·       PenStyle - Gets or sets the style of pen to use for example pen or highlighter

    There are also various methods available in the InkCanvas control which is pretty self explanatory so I'll leave it up to the reader to explore. 

    Ink Recognition

    One of the great benefits of the Windows Mobile Ink API is the ability to convert the ink data to a text string via ink recognition. 

    The following code sample will convert ink data to a text string using the COM objects:

    private Ink m_ink;

    public override string ToString()
    {
        //Get the appropriate COM objects required.
        IInkDisp disp = m_ink.InkDisp;
        IInkStrokes strokes = disp.Strokes;
        IInkRecognitionResult results = strokes.RecognitionResult;

        //Get the recognized string.  An alternate way of doing this is strokes.ToString()
        string ret = results == null ? "" : results.TopString;

        //Release the COM objects
        if (results != null)
            Marshal.ReleaseComObject(results);
        Marshal.ReleaseComObject(strokes);
        Marshal.ReleaseComObject(disp);

        //Return the recognized text
        return ret;
    }

    If you use the InkCanvas control the RecognizedText property can be used.  Internally the InkCanvas control does the following to recognize the ink data:

    public string RecognizedText
    {
        get
        {
            return Ink.Strokes.RecognitionResult.TopString;
        }
    }

    public Interfaces.IInkDisp Ink
    {
        get
        {
            if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                return null;
            IntPtr pInk;
            NativeMethods.SendMessage(childHandle, (int)ICM.GETINK, 0, out pInk);
            if (pInk == IntPtr.Zero)
                return null;
            return (Interfaces.IInkDisp)Marshal.GetObjectForIUnknown(pInk);
        }
    }

    Essentially both methods would produce the same results for the same ink data.

    Mobile Ink Library Custom Controls

    The Mobile Ink Library consists of four controls to make it easier for the developer to ink enable a .NET Compact Framework application.  Using the IInkOverlay is pretty straight forward, but to simplify the life of the developer various controls were created to ink enable .NET Compact Framework applications using Windows Mobile 6.0.

    InkControlBase

    InkControlBase is the abstract base class in which the InkRecognizer, InkPicture and InkSignature inherit from.  The following is the class diagram of the control:

    As you can see from the diagram InkControlBase inherits from System.Windows.Forms.Control.  'Inking' the control is enabled by the Ink property which wraps the IInkOverlay interface (for more information on IInkOverlay see the above).

    Since IInkOverlay requires a handle to the window (or control) to attach to, we override the OnHandleCreated() in the InkControlBase class to attach the control.  The following code is used to accomplish this:

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if (StaticMethods.IsRunTime)
        {
            if (m_inkOverlay == null)
            {
                //Create the IInkOverlay object
                m_inkOverlay = new Ink();

                //Listen for all stroke events  
                m_inkOverlay.IInkOverlay.SetEventInterest(InkCollectorEventInterest.Stroke, true);

                //We need to set the handle (or control) the inkoverlay object is bound to
                m_inkOverlay.IInkOverlay.hWnd = this.HandleInternal;
                m_inkOverlay.IInkOverlay.Enabled = true;
            }
        }
    }

    You will notice the following line:

    m_inkOverlay.IInkOverlay.hWnd = this.HandleInternal;

    HandleInternal is an internal virtual property and returns the Handle of the InkControlBase control used to attach to the IInkOverlay. 

    /// <summary>
    /// Gets the internal handle to associate the IInkOverlay object with
    /// </summary>
    internal virtual IntPtr HandleInternal
    {
        get
        {
            return this.Handle;
        }
    }

    Classes inheriting from InkControlBase have the option to override this property to return a different handle.  We will visit this again when we discuss the remaining controls.

    InkControlBase provides all the basic functionality such as Clear(), Open() and Save() methods.  These methods internally use the Ink property to make the required calls to IInkOverlay. 

    InkRecognizer

    InkRecognizer class was created to allow the developer to recognize ink inputted by a user into plain text and inherits from InkControlBase. 

    From the class diagram, you will see that InkRecognizer does not do much except override the ToString() method.  The ToString() method will return the text string that is recognized by the IInkOverlay object using the following code:

    public override string ToString()
    {
        if (StaticMethods.IsDesignTime || this.InkOverlay == null)
            return base.ToString();
        else
        {
            //Get the appropriate COM objects required.
            IInkDisp disp = Ink.InkDisp;
            IInkStrokes strokes = disp.Strokes;
            IInkRecognitionResult results = strokes.RecognitionResult;

            //Get the recognized string.  An alternate way of doing this is strokes.ToString()
            string ret = results == null ? "" : results.TopString;

            //Release the COM objects
            if (results != null)
                Marshal.ReleaseComObject(results);
            Marshal.ReleaseComObject(strokes);
            Marshal.ReleaseComObject(disp);

            //Return the recognized text
            return ret;
        }
    }

    From the code above you will notice that Ink property is used to access the IInkDisp, IInkStrokes and IInkRecognitionResults to return the recognized text.  (For more information on text recognition see the above).  The BasicRecognitionCF sample demonstrates the use of the InkRecognizer control.

    InkPicture

    InkPicture control allows the developer to add ink to a picture using the standard System.Windows.Forms.PictureBox control.

     

    The InkPicture control is not as straight forward as the InkRecognizer. 

    The first difference is InkPicture overrides HandleInternal.  If you recall, HandleInternal provides the handle to which the IInkOverlay object will be attached to.  InkPicture internally has an m_pictureBox field which is the standard PictureBox control used to display an image and we use the m_pictureBox.Handle to attach to the IInkOverlay object.

    The InkPicture control exposes the internal PictureBox control as a property to allow the developer to modify any properties for the picture box.  It also exposes an Image property to get or set the image that is displayed in the PictureBox.

    InkSignature

    Capturing signatures is a very common requirement for Windows Mobile line of business applications and using and using the Windows Mobile Ink API (or WISP Lite) is a perfect solution.  Since the InkPicture control has the main requirements for capturing signatures (which is really just capturing ink data) it can be used as the base class for InkSignature.

     

    Since this is a signature control, we may want to give some sort of indication to the user to sign.  To do this we can add an image to the control using the Image property.  Below is an example of what that could possibly look like:

    The InkSignature control also exposes a FirstName, LastName and DateSigned property.  These class properties are saved as ExtendedProperties within the Ink Serialized Format (ISF) file when the signature data is saved. Be aware that these properties are only saved when the ink data is saved using ISF and won't be saved if saved to a GIF file.  The InkSerializationCF Sample demonstrates the use of the InkSignature control.

    InkControlBase Workarounds 

    During the development of these controls, it was found that IInkOverlay behaved differently in managed code than it did in native code.  Whenever a user started inking on the signature control, the InkSignature control could never lose focus by tapping on any part of the screen (for example the Textbox).  Specifically, the Capture property of the control was set to True whenever a user started inking which caused other controls to not receive any Click events.  It was also noticed that the InkControlBase control would never receive focus when clicked on because of IInkOverlay.

    To work around this a workaround was implemented in the OnMouseDown and OnMouseUp of the InkControlBase. 

    Focus Issue

    First we'll look at OnMouseDown which resolves the 'focus' issue:

    protected override void OnMouseDown(MouseEventArgs e)
    {

       
    base.OnMouseDown(e);
        if (this.ClientRectangle.Contains(e.X, e.Y))
           
    OnMouseDownInternal(e);
    }

    From the code above you can see that on mouse down we check to see if we are in the bounds of the control, if we are we call OnMouseDownInternal().

    internal virtual void OnMouseDownInternal(MouseEventArgs e)
    {
        this.BoundingControl.Focus();
    }

    OnMouseDownInternal() will call the Foucs() method on the BoundingControl.  In the case of InkRecognizer it calls the InkControlBase implementation which returns this.

    internal virtual Control BoundingControl
    {
        get
        {
            return this;
        }
    }

    In the case of InkPicture and InkSignature, we override the BoundingControl property and return m_pictureBox.

    internal override Control BoundingControl
    {
        get
        {
            return this.m_pictureBox;
        }
    }

    This allows us to set focus to the underlying control and still have the control fire off events like GotFocus.

    Capture Issue

    The next issue faced was the Capture issue.  It seemed IInkOverlay always set Capture to true for the bounded control.  To work around this issue we had to override OnMouseUp as follows:

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (!this.ClientRectangle.Contains(e.X, e.Y))
            OnMouseUpInternal(e); 

        base.OnMouseDown(e);
    }

    Again from the above code you can see that we call OnMouseUpInternal() where we set the Capture property to false and then send a 'manual tap' so the user does not have to tap the screen twice.

    internal virtual void OnMouseUpInternal(MouseEventArgs e)
    {
        //HACK When using the IInkOverlay object in managed 
        //code seems that the control Capture property is set to true.  
        //When trying to click outside the bounds of the 
        //control (ie a button) the control would not 
        //receive the mouse click
        this.BoundingControl.Capture = false;
        SendTap(Control.MousePosition.X, Control.MousePosition.Y);
    }

    The SendTap() method makes a native call to mouse_event to manually send another tap to the screen since the first tap (or click) was consumed by the InkBaseControl.

    internal void SendTap(int x, int y)
    {
        NativeMethods.mouse_event(
            NativeMethods.MOUSEEVENTF.LEFTDOWN | NativeMethods.MOUSEEVENTF.ABSOLUTE, 
            (int)((65535 / Screen.PrimaryScreen.Bounds.Width) * x), 
            (int)((65535 / Screen.PrimaryScreen.Bounds.Height) * y),
            0,
            0);
        NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF.LEFTUP, 0, 0, 0, 0);
    }

    InkCanvas

    The InkCanvas control was discussed briefly in the technical overview but we will go through it again since it's a custom control part of the Mobile Ink Library.  The InkCanvas control is a managed wrapper to the native InkCanvas control available on Windows Mobile 6.  The InkCanvas control gives you the ability to add inking capabilities to your Compact Framework application on the Windows Mobile 6 platform without the need to use any of the COM interfaces. 

    Here is the class diagram of InkCanvas:

    From the above class diagram you can see there are three different modes that the InkCanvas can be set to. 

    ·         INK - Sets the InkCanvas to inking mode and allows the collection of ink data

    ·         SELECT - Sets the InkCanvas control select ink strokes collected

    ·         DELETE - Sets the InkCanvas to delete ink strokes as the user taps on the stroke

    There is also a PenStyle property which allows you to change the size, color, width and pen type of the stroke being collected.  If you need to interact with the IInkDisp interface, you can also use the Ink property and use the COM interfaces available.  The main method of communication with the native InkCanvas is via windows messages.  Since the InkCanvas control inherits from the OpenNETCF.Windows.Forms.Control2 class, Control2 helps simplify the creation of native control and capture any messages such as StrokeAdded. This allows us to raise relevant events such as StrokeAdded, StrokeDeleted and CharacterAdded.  The InkNotesCF Sample demonstrates the use of InkCanvas control.

    Samples Available with the Mobile Ink Library

    Also included with the installation are five sample applications demonstrating the use of the library.  The following is a brief description of the samples.

    InkControlDemo

    InkControlDemo demonstrates the various features of the InkCanvas control available in the Mobile Ink Library.  It demonstrates how to:

    ·         Add, select and delete ink data

    ·         Change the color of the ink canvas, back color and pen color

    ·         Change the width of the pen

    ·         Text Recognition

    ·         Extracting an image of the ISF ink data

     

     

    InkObjDemo

    InkObjDemo demonstrates the following:

    ·         How to use the IInkOverlay COM interface within a managed application

    ·         How to save the ink data as Ink Serialized Format (ISF) from the IInkOverlay COM Object

    ·         How to load and Ink Serialized Format file into an IInkOverlay object

    ·         Clearing the ink collected by IInkOverlay

    ·         Changing the width of the pen for the ink

     

    BasicRecognitionCF

    BasicRecognitionCF was ported from the BasicRecognition native sample available with the Windows Mobile 6 Professional SDK.  It allows a user to input text with the stylus, and recognize the text.  It demonstrates the following:

    ·         Using the InkRecognizer custom control

    ·         Saving and Loading ISF files

    ·         Ink recognition

    InkSerializationCF

    InkSerializationCF was ported from the InkSerialzation native sample available with the Windows Mobile 6 Professional SDK.   It allows a user to sign on a region and save the signature along with the persons name in the ISF file.  It demonstrates using the following:

    ·         Using the InkSignature custom control

    ·         Saving and loading a signature file (ISF file)

    ·         Embedding ExtendedProperties within an ISF binary stream

     

    InkNotesCF

    InkNotesCF was ported from the InkNotes native sample available with the Windows Mobile 6 Professional SDK .   It allows a user to view and edit ink notes. These ink notes can also be imported to OneNote 2007 or 2003 on the desktop.  It demonstrates using the following:

    ·         InkCanvas control

    ·         Changing the pen used for inking

    ·         Erasing Ink

    ·         Selecting Ink

    ·         Zooming in and out

    ·         Saving and loading ISF files

    ·         Emailing the note as a GIF attachment

     

     

    Conclusion

    In this article we took a look at the Windows Mobile Ink API for Windows Mobile 6 and the OpenNETCF Mobile Ink Library.  The Mobile Ink Library opens the doors to .NET Compact Framework developers to ink enable their Windows Mobile 6 applications.  In the next article we’ll look at using the same Ink Serialized Format (ISF) data on both  a Windows Mobile 6 application and a desktop application.

  • Improving Data Access Performance with Data Caching

    Chris Tacke
    OpenNETCF Consulting
    September 2007

    Download the Source Code
    Download the PDF

    Introduction

    Performance of  the data access layer of a mobile or embedded application is often viewed by developers in very broad strokes.  When we begin designing a solution we consider the performance versus benefits of using DataReaders,  DataAdapters, DataSets and the like.  Most often we just make an early determination based on what our expected use and the generally accepted "best practice" is and then move on without much further thought. 

    Traditionally if we only wanted to ever read the data in a non-scrollable cursor, we'd use a DataReader.  If we want to alter the data we'd use a DataSet.  Version 2.0 of the Compact Framework brought the wonderful "blended" use DataAdapter, and many developers migrated to it and considered the variable of data access performance to be largely out of their hands beyond that point.

    A recent plant floor automation system project that we worked on was a stark reminder that such assumptions are not true, and that performance considerations need to be looked at throughout the development of any solution.

    Data Access as a Bottleneck

    Like many projects, we didn't really start to discover show-stopping performance problems in our recent endeavor until we were getting close to delivery of a full working system.  Early tests during development usually miss the full impact of all of the interactions of a full running system, and it's really hard, if not impossible, to guess what the actual execution performance of any solution will be until all of the major pieces ar in place and cooperating.  Sure, you might get hints at problem areas early on, but those are usually gross, easy-to-find-and-remedy problems usually contained within a single susbsystem.  It's not until you have enough infrastructure in place for the system as a whole to start working as a unit that you start to see the larger-scale problems, and coincidentally it's usually about the same time that you start delivering drops to your customer.

    Our system collected event data from Programmable Logic Computers (PLCs) on the factory floor, aggregated that data into relational SQL CE tables, and then generated meaningful reports from that data through a light-weight ASP.NET Web Server running on Windows CE  that we built (I'll discuss more on that specific technology in a future article).  The basic unit of measure that we used to judge system performance was the number of messages coming from PLCs that would could handle while still serving up reports.

    The customer's initial  requirement included the ability to handle around 5 events per second, and early tests and proof-of-concept demonstrations had us running as many as 60 events per second so we were confident that we were not just going to meet their requirements, but dazzle them with an order-of-magnitude-better performance.  Then as we approached the time for final delivery of the system and we started tying all of the multitude of pieces together we suddenly found the system struggling to even handle 2 or 3 events per second, and new calculations from the customer we indicating a desire to handle not just 5 but 10 events per second.

    My initial instinct based on the application's behavior and experience was that Garbage Collection was the problem, but as I outlined in a previous article that turned out not to be the case.  After heavily instrumenting the code I determined that a large amount of our cycle execution time was being spent in data access (a cycle being the receipt of an event as an array of bytes, parsing that event  and then storing the results in a table) .

    Part of the parsing and storage involved looking up foreign key integers from lookup tables (the events come in with "denormalized" text data, so we have to look up the ID based on text) and after instrumenting the code to even finer granularity I found that even these simple reads were taking a lot longer than one would expect.

    Profiling Lookup Table Access Speed

    Since I knew that simple lookups were taking a long time (tens to hundreds of milliseconds) I knew immediately that we needed to try to decrease this, but how?  We needed the data from the lookup table to do subsequent inserts and/or updates.  The answer was to generate our own data caching mechanism at the table level.

    Most data entities in the solution have been abstracted to single class definitions and a search on the table was done through methods like GetCustomerByID or GetIDFromCustomerName.  We were doing a lot of the latter type - getting an ID based on other fields, but the caching technique we're going to cover in this article works well for any of them.

    It's a lot easier to talk about performance gains if we can quantify the performance before the "fix" and again after the fix, so before we move on to the solution, let's take a look at some post-mortem investigation data I gathered. 

    I created a simple application that creates a SQL CE database with a single table (our simulated lookup table) and then puts just 2 rows into the table.  With only 2 rows we remove the possible effects of any table indexing, since a lookup is going to require two fetches at most.

    I then created very basic code that would get a row from the table based on the row identifier (so SELECT * FROM Table WHERE RowID = N).  The architecture of our large solution was such that most data transactions created their own connection to the database, so I added code to my test application  that used the same behavior.  To smooth out the results, the application would create the connection, open it, run the SQL and then close the connection.   It did this 100 times and output the average time it took to perform the operation.  The results from running the test on three separate test devices are below (the actual production device being the first in the list):

    Device

    Processor

    Mean Execution (ms)*

    OLDI SAM-L8

    800MHz Geode x86

    7.5 to 9.5

    iCOP eBox 2300

    200MHz Vortex86

    47 to 52

    Dell Axim x51

    416MUz Intel PXA270

    38 to 62

    *Ranges are given because we ran several iterations of the 100-run tests

    Intuition and experience (and other developers) told me that the first step in improving these numbers would be to reuse the connection instead of creating and opening it every time.  So I modified the test application to create a global connection and use it without closing for all of the queries.  The results for those tests are below.  Note that these numbers exclude the first run which creates and opens the connection, so these numbers reflect purely the time to get one row of data from a table containing only two rows of data.

    Device

    Processor

    Mean Execution (ms)

    OLDI SAM-L8

    800MHz Geode x86

    7.1 to 14.8

    iCOP eBox 2300

    200MHz Vortex86

    51 to 52

    Dell Axim x51

    416MUz Intel PXA270

    43 to 61

    Now if you go back and look at the two result tables you'll probably be as surprised as I was (and yes, I double- and triple-checked these numbers). 

    Lesson 1: There's no performance benefit whatsoever to keeping the connection created and opened through the run of your application.

    Ok, so things aren't quite what we expected, but I created a meaningful test that provided baseline numbers that I needed to improve.  The next step was to implement caching and then quantify the results to see if the improvement in performance warrants the extra time it takes to implement the caching itself.

    Implementing a Data Cache

    So what exactly is a data cache and how do we go about creating one? The concept (and really the implementation) is quite simple.  You start by creating a data type (could be a struct or a class) that describes a row of data. That entity can be a subset of the columns in a table if your lookup query typically only looks at a couple fields.  You then create a searchable collection to hold a series of these entities.  In my test application I used a Hashtable because it makes lookups by a key value very easy.  This collection of entities is the cache.

    Using the cache is simple.  When a table lookup is needed, instead of going right to the database you first look in the cache to see if the row is there.  If it is, you return the data from the cache.  If it's not, you get it from the database, store it in the cache and then return the data.  In this way the cache grows as data is queried to spread out the load time over many operations.  Another mechanism would be to populate the cache with all of the lookup table rows on creation.   This method would mean that all lookups, including the first will be as fast as possible, but you have to pay up front to fill it.

    Figure 1

    I added a cache to the test application and re-ran the tests.  The results follow.  Again these numbers omit the first run which was skewed high because we had 1 table lookup and 99 cached instead of 100 cache reads.

    Device

    Processor

    Mean Execution (ms)

    OLDI SAM-L8

    800MHz Geode x86

    0.0193 to 0.0201

    iCOP eBox 2300

    200MHz Vortex86

    0.1014 to 0.1164

    Dell Axim x51

    416MUz Intel PXA270

    0.0975 to 0.1437

     

    Yes, again those numbers are correct - the decimal points are not in the wrong place.

    Lesson 2: Cache reads are two orders of magnitude faster than a database read.

    Of course I expected the cache reads to be faster, but I wasn't expecting this much of a gap.  In fact I have no explanation for why the difference is so large.  The database that the test application created was in RAM, not persistent storage, so both the database read and the cache read are coming from RAM.

    Conclusion

    So what did we learn from this test?  First, that the "general knowledge" that reusing a connection is faster appears to be completely false and second that caching data has some seriously large performance advantages.  Of course you still have to weigh those advantages against the advantages of getting the data from the database. 

    Your cached data is not going to get updated when a table is updated, so it probably not a good idea for tables that have data that changes a lot.  The cache maintenance code gets more complex if you have code that deletes or updates the lookup table.

    You can't join or query your cached data with SQL.  If the data is not a typical lookup table and is often used in joins, then it's probably not a good candidate for caching.

    The data cache takes up memory.  If you have a really large lookup table (say a lookup of every product a large retail store has on its shelves for example) then a cache might not be a good idea, though you could modify the cache maintenance code to hold maybe the last 100 or so rows read, so frequently queried rows would often be in the cache and fast to read.

    So is caching the answer to every database performance woe that might plague you in a project?  Obviously not, but it is an important tool that you can use to improve some areas of your application. It is simply another tool that you now have and it's going to be up to you to recognize if and when it's applicable to a problem you're working on, but at least now you know it exists. And, as I was told many times by a cartoon as a kid, knowing is half the battle.  Go Joe!

  • Developing Connected Smart Device Applications with SqlClient

    Prashant Dhingra
    Microsoft Corporation
    September 2007

    Download the Sample Code 
    Download the PDF

    Introduction

    The Microsoft .NET SqlClient class provides a consistent set of APIs for both .NET Framework and .NET Compact Framework applications. SqlClient is a data provider for Microsoft SQL Server. SqlServerCe is a data provider for accessing Microsoft SQL Server Compact Edition database.

    The SqlClient namespace exists both in the full .NET Framework and in .NET Compact Framework.  Generally speaking there are three high-level ways of providing data access using the Microsoft data providers:

    1. Using a .NET Framework-based SqlClient data provider to access data in a SQL Server on from a PC
    2. Using a .NET Compact Framework-based SqlClient data provider to access data in a SQL Server from a device.
    3. Using a .NET Compact Framework-based SqlServerCe provider to access data in a SQL Server Compact Edition file directly on a device.

    Accessing a SQL Server database from a device application is very similar to accessing a SQL Server database from a desktop application (methods 1 and 2 above). This exercise will look at number 2 by demonstrating how you can query and update a backend SQL Server Database using a Compact Framework-based Smart Device application.

    In this exercise you will develop a .NET Compact Framework application using Visual Studio 2005. The application will allow you to see real time data stored in SQL Server 2005. Since the application is directly accessing and updating SQL Server, it does not require the use of SQL Server Compact Edition nor do you need to configure replication. It does require that the device have reliable connectivity with the backend server while working on this application.

    From Device you can connect to SQL Server using TCP/IP connection. You need to configure SQL Server to accept remote connections over TCP/IP.

    Enable Remote Connections

    First you need to allow remote connections access to your SQL Server 2005 instance.  To allow remote connections, follow the steps below.

    1. Click on Start | All Programs | Microsoft SQL Server 2005 | Configuration Tools | SQL Server Surface Area configuration.


    2. On the surface area configuration dialog click on "Surface Area Configuration for Services and Connections" option.
    3. Click on Remote Connections in Database engine node. 
    4.  Enable Remote connections by clicking on Local and remote connections.

      Figure 2
    5. Select sub option "Using both TCP/IP and name pipes"

    Note: SQL Server 2005 Express Edition,  Evaluation Edition and Developer Edition SKUs allow local connection only. Enterprise, Standard and Workgroup Edition SKUs allow remote connections over TCP\IP.

    Configure Windows Firewall

    If your system has the Windows Firewall configured, it must be configured to allow the remote connection to communicate with the SQL Server executable. Follow the steps given below to ensure connectivity through the firewall.

    1. Start the Control Panel on the computer running SQL Server.
    2. Double Click on Windows Firewall.
    3. Click on the Exceptions tab.
    4. Click on the Add Program button.
    5. Add the path of sqlsrvr.exe. By default the sqlsrvr.exe is installed at <Drive>\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe

      Figure 3
    6. Click OK.

    Develop a SQL Server Management Studio application for Smart Devices

    Now that you have configured your SQL Server to allow remote connections we'll walk through creating a simple SQL Server Management Studio-like application that will run on a Smart Device.  This application will allow you to run SQL statements against the remote SQL Server directly from a connected device.

    This example uses the AdventureWorksDW database to allow users to run ad-hoc queries against the database without having to be at a desktop computer.  You can alter the connection string in the code to target any other database, or alternatively you might extend the code to allow the user to enter the database name through the user interface.

    Follow these steps to create the Smart Device application:

    1. Create a Smart Device project (Windows Mobile 5 or Windows Mobile 6) in Visual Studio. Start Visual Studio 2005. The example uses Windows Mobile 6.0 Professional Edition.
    2. Add a reference to System.Data.SqlClient namespace in the project. Right click on References and Select Add Reference option. In the Add Reference dialog box Select System.Data.SqlClient dll.
    3. Design the user interface for project as shown in figure.
    4. Use Toolbox to create a Tab control with 4 tabs - Objects, SQL, Result and Notes.
    5. Create a Tree node on Objects Tab to display SQL Server database objects.
    6. Create a Text box in SQL Tab to run queries.
    7. Create a DataGrid on Result tab to display result.
    8. Create a Text box on Notes Tab.
    9. Create a button to execute queries.
    10. Optionally create multiple buttons to store queries
    11. You can make a user interface similar to SQL Server Compact Edition Query Analyzer that connects to SQL Server instead of SQL Server Compact Edition database.

     

    The code below demonstrates how to access the remote SQL server using the SqlClient provider objects and display the results in a DataGrid.

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Text;

    using System.Windows.Forms;

    using System.Data.SqlClient;

     

    namespace SSMS_Device

    {

        public partial class SSMS : Form

        {

            SqlConnection myConn;

            String connStr;

     

            public SSMS()

            {

                InitializeComponent();

                connStr =

                    @"Server='PDHINGRA';Database=AdventureWorksDW; User Id=MyLogin; Password=MyPassword";

            }

     

            private void button10_Click(object sender, EventArgs e)

            {

                try

                {

                    myConn = new SqlConnection(connStr);

                    myConn.Open();

                    FillList();

                }

     

                catch (Exception ex)

                {

                    MessageBox.Show("Connection error: " + ex.ToString());

                }

     

                finally

                {

                    myConn.Close();

                }

            } // button click

     

            private void FillList()

            {

                //SqlDataReader myDataReader;

                SqlDataAdapter myAdapter = new SqlDataAdapter(txtSQL.Text, myConn);

                DataSet myDataSet = new DataSet();

                myAdapter.Fill(myDataSet, "Result");

                // dataGridView1.DataSource = myDataSet.DefaultViewManager;

                dataGrid1.DataSource = myDataSet.Tables["Result"].DefaultView;

     

            }

            private void FillObject()

            {

                try

                {

                    myConn = new SqlConnection(connStr);

                    myConn.Open();

                    SqlDataReader myDataReader;

                    SqlCommand myCmd = myConn.CreateCommand();

     

                    myCmd.CommandText = "SELECT TABLE_SCHEMA, TABLE_NAME"

                        + " FROM INFORMATION_SCHEMA.TABLES WHERE"

                        + " TABLE_TYPE = 'BASE TABLE'";

                    treeView1.BeginUpdate();

                    treeView1.Nodes.Clear();

                    treeView1.Nodes.Add(new TreeNode("Tables"));

                    TreeNode childNode = treeView1.Nodes[0];

     

                    int childCount = 0;

                    myDataReader = myCmd.ExecuteReader();

                    while (myDataReader.Read())

                    {

                        string TableName = (myDataReader[0].ToString())

                            + "." + (myDataReader[1].ToString());

                        childNode.Nodes.Insert(childCount, new TreeNode(TableName));

                        childCount += 1;

                    }   

                    myDataReader.Close();

                    treeView1.Nodes.Add(new TreeNode("Views"));

                   

                    SqlCommand myCmdView = myConn.CreateCommand();

                    myCmdView.CommandText = "SELECT TABLE_SCHEMA, "

                        + "TABLE_NAME FROM INFORMATION_SCHEMA.TABLES "

                        + "WHERE TABLE_TYPE = 'VIEW'";

                    TreeNode childNodeView = treeView1.Nodes[1];

     

                    int childCountView = 0;

     

                    myDataReader = myCmdView.ExecuteReader();

                    while (myDataReader.Read())

                    {

                        string ViewName = (myDataReader[0].ToString())

                            + "." + (myDataReader[1].ToString());

                        childNodeView.Nodes.Insert(childCount, new TreeNode(ViewName));

                        childCountView += 1;

                    }

     

                    myDataReader.Close();               

                    treeView1.ExpandAll();

                    treeView1.EndUpdate();

                    myConn.Close();

                } // try

                catch (SqlException myexception)

                {

                    foreach (SqlError err in myexception.Errors)

                    {

                        MessageBox.Show(err.Message);

                    }

                } // catch

            }

     

            private void SSMS_Load(object sender, EventArgs e)

            {

                FillObject();

            }

     

        }

    }

    1. Build and compile the application.
    2. Deploy the application to an emulator or a device.
    3. The application will start and create a connection to backend SQL Server.
    4. The application then fetches the table and view names from the INFORMATION_SCHEMA.TABLES view.
    5. The tables and views are then displayed in a TreeView as shown in the figure below.

      Figure 4
    6. Click on the SQL Tab and enter a SQL query such as that shown in the figure below. 
    7. Click on the Execute Button (">") to run your query.

      Figure 5
    8. When the Execute B is clicked, the application creates a connection to the backend AdventureWorks database and exeutes the query. The result are then populated in the Results Tab as shown in figure below.
    9. Click on the Results Tab to see the result.

      Figure 6

    The code in the download doesn't use the Notes tab, but you could add information to it such as the time required to execute the query, the number of rows affected, etc.

    Difference between SqlClient provider in .NET Framework and .in NET Compact Framework

    The System.Data.SqlClient provider is a collection of classes to access SQL Server 2005 databases. You can use the SqlClient provider in a Smart Device platform in the same way you use the SqlClient provider in a Desktop application, however there are few differences in the .NET Compact Framework version of the provider that are important to note.  For the .NET Compact  Framework

    • The SqlClientPermission and SqlClientPermissionAttribute classes are not supported.
    • Connection Pooling is not supported
    • Transactions spanning to multiple databases are not supported
    • Direct TCP/IP connections to a SQL Server instance are supported
    • Windows authentication is supported, however you must specify the user id and password in the connection string.
    • Encrypted connections to SQL Server instances are not supported. The connection will fail if computer running SQL Server has an SSL certificate installed

    In this exercise you built a tool similar to SQL Server Management Studio to run on Mobile devices. You can use the tool to execute SQL queries from a mobile device directly against a backend SQL Server. This exercise also demonstrated how to build connected enterprise applications for mobile devices.

    Building an Enterprise Solution - Connected vs Disconnected

    This exercise has shown that it's straightforward to create an enterprise application with a direct connection to a back-end SQL Server.  An alternative is to maintain a local SQL CE Compact Edition data store on the Smart Device itself and use the SqlServerCe provider for data access.

    The SQL Server Compact Edition book (ISBN : 0672329220) has detailed exmples for building enterprise applications using both the System.Data.SqlClient and System.Data.SqlServerCe namespaces.

  • Debugging Without ActiveSync

    Chris Tacke
    OpenNETCF Consulting
    March 2007 

    Download CEDebgSetup source and binaries
    Download the PDF

    Introduction

    If you've tried to debug a native or managed application with Microsoft Visual Studio 2005 on any device other than a Windows Mobile device, you're likely very aware of how painful it can be to just get connected.  Typically it's a process requiring the proper alignment of planets, holding your tongue just right and often a visit to Stonehenge.

    We hope that this short white paper  along with CeDbgSetup.exe will take a lot of the pain out of the process. 

    Requirements and Assumptions

    Before proceeding, make sure you have the following:

    • Visual Studio 2005, Standard Edition or better with Service Pack 1 (SP1) installed. If you don't have SP1, CeDbgSetup.exe will deploy the wrong CoreCon files for your environment and Studio will only laugh at you with a very unhelpful "Cannot Connect" message.
    • CeDbgSetup.exe. This is a free, shared source tool that we've written to do a lot of the grunt work on the device for you. As usual it's provided as-is and without warranty, but if you have a problem let us know.
      As of this writing CeDbgSetup.exe supports only ARMv4I and x86 devices. Sorry to you MIPS and SHx developers, but I don't have a device with one of those processors, so I can't build or test the tool for those architectures. If you have an SDK for one of them, let us know and we may be able to update the tool.
    • As of this writing we've only verified that this works with Studio running on Windows XP.
    • You probably should shut off or put an exception in firewalls and other security stuff that might interfere with the transport
    • We've tested with the PC and the device running 802.11 connections, so yes, this work wireless on either or both sides
    • You don't need ActiveSync. Need we say more?

    Configure the device

    1. First make sure the device has a valid IP address. Record the IP for future use.
      Using a fixed IP, especially on networks where the DHCP lease is short and addresses change frequently. If the device IP address changes, this entire process must be done again.
    2. Deploy and Run CeDbgSetup.exe on the device. This application is provided for free from OpenNETCF Consulting. If your device has a mechanism to persist the file and auto-launch it on boot, then we recommend that you do so to make things simple.
      When successfully run, shortcuts will be added to the device desktop. In normal operation you should not actually need to use these shortcuts.

    Configure the PC

    1. Verify you can ping the device the ensure that there is a solid communication channel.

    Configure Visual Studio

    1. Enable the Device toolbar by right-clicking on Studio's toolbar and checking ‘Device'

      Figure 1
    2. Select your target device. In this example we'll choose the iCOP eBox 2300, which is running CE 6.0. You'll see that I've got several other devices to choose from.

      Figure 2
    3. Open the Devices option window by either clicking the ‘Device Options' button on the Device toolbar or using the following menu path Tools->Options->Device Tools->Options.

      Figure 3
    4. Select your platform from the "Show devices for Platform" dropdown.
    5. In the "Devices" list, select your target device. At thins point it may be useful to rename the device. You can also "clone" any device by clicking "Save As" which can be helpful if you want to have different transports or IP addresses for the same type of target device.
    6. Select your device in the "Devices" list and click "Properties"

      Figure 4
    7. Click on the "Configure" button

      Figure 5
    8. Select the "Use specific IP address" radio button and enter device's IP address you recorded way back at the start of this process.
    9. Click the "OK" button on all 3 dialogs to bring you back to Studio.
    10. Click the "Connect to Device" button on the Device toolbar

      Figure 6

      You should see the following:

    Figure 7

     

    At this point Visual Studio can communicate with the device and you're ready to develop.

     

  • Image Manipulation in Windows Mobile 5.0

     

    Rob Miles
    Department of Computer Science, University of Hull
    August 2007

    Download Sample Image Capture
    Download Full Image Processing Game
    Download the PDF

    Introduction

    Windows Mobile 5.0 offers the prospect of games based on images which the player captures with the onboard camera. In this article you will discover how to capture such images and prepare them for use as sprites and backgrounds in a game.

    What You Will Need to Get Started

    You will need Visual Studio .NET 2005. You will also need to acquire the Windows Mobile 5.0 SDK for the device that you are targeting. This is a free download.

    You don't actually need a phone handset or mobile device because you will be using techniques that make it possible to do all the work on the emulators provided by Microsoft Visual Studio® .NET 2005. However, the emulators do not support the camera behavior, although you can use the image loading dialog.

    If you have a device, it will make things much more interesting. Only the most recent Windows Mobile 5 devices have Compact Framework Version 2.0 built in, but this will be installed automatically as required when the program is downloaded into the device.

    If you want to download the programs into a real device you will also need Active Sync version 4.1. The programs have been tested on an SPV C500 and an Imate Jasjar.

    The Camera in a Mobile Device

    Whilst Smartphones have always been supplied with cameras, and many Pocket PC devices now sport them, it is only with Windows Mobile 5.0 that programmers are able to use them easily. In previous devices the camera has been controlled by software which was specifically written by the hardware manufacturers. This software did not provide a way in which programmers writing in managed code could easily interact with the camera to initiate the taking of pictures. That has changed in the latest version of Windows Mobile, it is now possible for a program to initiate the taking of photographs by the device owner.

    However, it is important to understand just what level of use you can make of the camera. There is no direct camera control from a C# program, in that you cannot order the camera to take a picture. Instead your program displays a camera capture dialog form. When the user has taken a picture control is returned to your program.

    If you want direct control of the camera device itself this can be achieved by use of the DirectShow interface which is also supported by Windows Mobile 5.0. However, for the purpose of simple games, allowing the user to take a photograph is sufficient.

    Getting Started with Visual Studio 2005 and Mobile Devices

    The kind of project that you need is a Visual Studio 2005 .NET Compact Framework Version 2.0 forms application.

    Figure 1

    Figure 1. Creating the new project

    Note that you only get the Windows Mobile 5.0 projects once you have installed the appropriate software development kits (SDK). The examples that are going to be created will target the Windows Mobile 5.0 Smartphone with a QVGA display. All the code can be used on other platforms with only minimal changes.

    Once you have created a new project, the next thing to consider is the form factor of the target device. At present Smartphone devices are available with two screen resolutions, the 176x220 of the original and 320x240 of more recent devices. The higher resolution is often referred to as QVGA because the screen is quarter the size of a 640x40 VGA screen. You can select the screen resolution by using the FormFactor property of the form:

    Figure 2

    Figure 2. Selecting the FormFactor

    Note that as a general rule you should make sure that your program will work with either size screen. However this feature is useful for allowing you to match your program with a particular sized device. I am going to be using the camera on device with QVGA resolution, so I have selected this option. You should select the option which matches your target device.

    The Microsoft.WindowsMobile.Forms Resource

    The CameraCaptureDialog is actually supplied in the Microsoft.WindowsMobile.Forms library. This is part of the Windows Mobile 5.0 SDK, but is not automatically added as a reference when a project is built. To add the library, right click on the References item in the Solution Explorer, select Add Reference from the menu which appears and then add the library:

    Figure 3

    Figure 3. The WindowsMobile Forms library

    Now our program can create and use all the items in this library, including the CameraCaptureDialog.

    Using the CameraCaptureDialog Form

    A camera capture dialog is created like any other dialog box, and also shown in the same way. The code to take a picture is as follows:

    CameraCaptureDialog cameraDialog = new CameraCaptureDialog();

    if ( cameraDialog.ShowDialog()==DialogResult.OK )
    {
        // if we get here the picture has been taken correctly
        // the FileName property gives the name of the image file
    }

    Note that the dialog does not return an image; instead it functions like a file dialog, in that it returns the name of the file where the image has been stored. If you want to use the image in your program you must then load the image from the file. The camera capture dialog has a cancel option, so your program must always allow for the action being cancelled.

    As with other windows forms, it is important to restore the original form once the picture has been taken. The method below will use the CameraCaptureDialog to take a picture and return the name of the picture file if this succeeds. It is called from within an existing form, and will return the display to that form when it finishes.

    private string takePicture()
    {
        string result = null;

        CameraCaptureDialog cameraDialog = new CameraCaptureDialog();
        cameraDialog.Mode = CameraCaptureMode.Still;
        cameraDialog.StillQuality = CameraCaptureStillQuality.High;

        cameraDialog.Resolution = new Size(640, 480);

        if ( cameraDialog.ShowDialog()==DialogResult.OK )
        {
            result = cameraDialog.FileName;
        }

        this.Show();
        cameraDialog.Dispose();

        return result;
    }

    Note that it the code also sets properties of the dialog to select a high quality still image at a resolution of 640x480 (although the user can override these when the picture is taken). When the method runs the camera capture dialog on the Smartphone is displayed:

    Figure 4

    Figure 4. A Camera Capture Dialog

    Note that the precise appearance of this dialog, and the options which are available for the camera, will vary from one Windows Mobile device to another.

    Using the SelectPictureDialog Form

    Sometimes the user may wish to select an existing picture from a file instead. Windows Mobile 5 provides an additional dialog which can be used to select a picture. It is used in exactly the same way as the camera dialog.

    SelectPictureDialog selectDialogue = new SelectPictureDialog();

    if (selectDialogue.ShowDialog() == DialogResult.OK)
    {
        // if we get here the picture file has been selected
        // the FileName property gives the name of the image file
    }

    Figure 5

    Figure 5. A Select Picture Dialog

    The SelectPictureDialog also contains the option of taking a picture with the camera as well. If the user does this, the name of the file containing the picture that was taken is returned.

    Sample Image Application

    The sample application allows you to load an image or take a picture using the camera. The image which is loaded is displayed in a picture box.

    Image Processing to create Sprites

    Capturing full sized pictures is a start, but if they are to be used in games they need to be converted into images which can be used as the basis of game sprites. This means that they will probably need to be cropped down, so that only part of the picture is to be used. As a plain edged sprite will not be very interesting, it would also be useful to be able to create sprites with a softer edge, and with more interesting shapes.

    This can be achieved by the use of a mask image which is combined with part of the captured image to produce shapes.

    Figure 6

    Figure 6. A Round Mask

    The figure shows a mask which can be used to create a round sprite. The purple background color will be set as the transparent color when the sprite is drawn. The other colors on the mask will be added to the existing pixels so that the sprite appears to get lighter towards the edge. The center of the circle is black, so that part will have no effect on the image. Our clipping code must resize the image and add the mask to get the desired effect.

    Figure 7

    Figure 7. Applying the Mask

    Figure 7 shows the mask after it has been applied to part of a picture. The sprite that is produced has been resized to an appropriate resolution and then drawn on a black background.

    Working with Bitmaps

    It is comparatively easy to get hold of the color of a particular pixel in an image. The Bitmap class provides a method called GetPixel which returns the color at a particular pixel position in the bitmap:

    Color pixelCol = myBitmap.GetPixel(0,0);

    This would obtain the color of the pixel in the top left hand corner of the bitmap and set the variable pixelCol to that value. There is a corresponding SetPixel method which can be used to set the color of a pixel:

    myBitmap.SetPixel(0,0,Color.Red);

    This would set the color of the pixel in the top left hand corner of the bitmap to red.

    The sprite generation program must combine pixels in the mask with those in the source image. Pixels in the source image which are in the same position as transparent ones in the mask must be set to the transparent color, otherwise the mask pixel color values are added to the source ones:

    Color maskColor = mask.GetPixel((int)maskX, (int)maskY);

    if (maskColor.Equals(transparentColor))
    {
        source.SetPixel(sourceX, sourceY, transparentColor);
    }
    else
    {
        if (!maskColor.Equals(Color.Black) )
       
    {
            Color sourceColor = source.GetPixel(sourceX, sourceY);
            int red = masterColor.R + maskColor.R;
            if (red > 255) red = 255;
            int green = masterColor.G + maskColor.G;
            if (green > 255) green = 255;
            int blue = masterColor.B + maskColor.B;
            if (blue > 255) blue = 255;
            source.SetPixel(sourceX, sourceY, 
                Color.FromArgb( red, green, blue));
        }
    }

    This code fetches a pixel from the mask at location maskX, maskY. If the mask pixel is the same color as the transparent color the source image pixel at sourceX,sourceY is set to transparent. If the pixel is not the transparent color the program adds the color of the pixel in the mask is added to the color of the source.

    Note that the color value is limited to the range 0-255, to stop strange effects if the value wraps around. If the above statements are performed on each pixel on the image, and the X and Y values are updated appropriately, this will perform the image processing that is required. Unfortunately it takes a long time to do this. On a standard Smartphone it can take a few minutes to process even a small image.

    Speeding up Bitmap Access

    The problem is that the GetPixel and SetPixel methods are very slow. The managed C# is trying to access the low level bitmap information and this takes time. To improve the speed the program must access this low level information directly. It turns out that this is quite easy. The Bitmap class provides a method called LockBits which will provide a pointer to the bitmap image data in memory. It also locks this data in position, so that it will not be moved around by the system.

    BitmapData bitmapBase = myBitmap.LockBits(
        bounds,                         // bounding rectangle on the bitmap
        ImageLockMode.ReadWrite,        // mode of the access
        PixelFormat.Format24bppRgb) ;   // required format - 8 bits per color

    The actual image data is presented as a sequence of pixel values for each row of the image in turn. In other words an image which is 60 pixels wide and 40 pixels high would have 180 bytes of data for the top row, followed by 180 bytes for the next, and so on.

    The BitmapData provides us with the base of the screen, as a pointer to bytes:

    private Byte* bitmapBaseByte;
    bitmapBaseByte = (Byte*) bitmapBase.Scan0.ToPointer();

    The pointer variable bitmapBaseByte now points at the beginning of the raw data for our image. In other words, since first byte of a pixel is the blue component for that pixel, the code:

    *bitmapBaseByte = 0xFF;

    - would se the blue component of the top left hand pixel on the screen to maximum.

    The actual pixel data is placed in memory as three consecutive 8 bit values for blue, green and red respectively. The best way to manipulate this in C# is to use a structure:

    public struct PixelData
    {
        public byte blue;
        public byte green;
        public byte red;
    }

    By careful use of casting the program can pull out the color components in each pixel:

    PixelData * currentPixel = *((PixelData*)( bitmapBaseByte));
    currentPixel->green = 0;

    This would set the green component of the top left hand pixel to minimum.

    The program can move down the image to the next pixel by incrementing the pointer. To move to the next row of the image the pointer must be increased by the size of the row. There is a slight complication in that the number of bytes in each row is must always be a multiple of four when the bitmap data is mapped into memory. If this is not the case then the end of the row is padded out with "blank" bytes up to the nearest four byte boundary.

    In the case of a row which contains 50 pixels the actual number of bytes in each row would be 152, i.e. 50 pixels multiplied by 3 bytes per pixel gives 150. This is then lifted up to 152, that being the nearest value to 150 which is divisible by 4. This makes the calculation of the width of each row slightly more complicated:

    byteWidth = pixelWidth * 3;

    if (byteWidth % 4 != 0)
    {
        byteWidth += (4 - (byteWidth % 4));
    }

    The pixelWidth value is multiplied by three (blue, green and red bytes) to get the byteWidth value. If this width is not divisible by four it is adjusted upwards by the appropriate amount.

    Once the raw data manipulation is complete the bitmap must be unlocked so that it can be returned to normal use:

    myBitmap.UnlockBits(bitmapBase);

    Unsafe Code

    Note that this means that our C# code is no longer safe, in that we are following pointers directly into memory, which bypasses all the usual safety checks performed in managed code. This means that the class which contains methods which do this must be declared as unsafe:

    public unsafe class ProcessBitmaps {

    For this to build correctly, the project must be modified to allow unsafe code to be compiled. This setting can be found on the Build tab in the Project Properties:

    Figure 8

    Figure 8. Allowing unsafe code in the project

    Figure 8 shows how this option is selected. If the program goes wrong, for example it tries to access a byte outside the range of the bitmap, it will not actually crash the host device, but it will fail without any kind of error report or exception being thrown.

    Moving through the Bitmaps

    When the sprite is being built the program must move through the source bitmap and combine the pixels in it with pixels in the mask bitmap. However, the bitmaps will not necessarily be the same shape and size. This means that the program must map the pixels from one bitmap into the matching ones in the other. This can be achieved by making the step through the mask bitmap the same relative size as the step through the image bitmap:

    float maskXStep =
        (float)maskBitmap.Width / (float) sourceRectangle.Width;

    float maskYStep =
        (float)maskBitmap.Height / (float) sourceRectangle.Height;

    float maskX = 0;
    float maskY = 0;

    The maskBitmap is the Bitmap containing the mask image. The variable sourceRectangle is a rectangle which describes the region of the source rectangle which is to be masked. The user selects this during the edit process. These two floating point steps are then used to update the position in the mask bitmap as each pixel in the source is processed. The step value corresponds to the movement of a single pixel in the given direction.

    for (int y = sourceRectangle.Top; y < sourceRectangle.Bottom; y++)
    {
        pPixel = (PixelData*)(sourcepBase + y * sourceWidth + 
            sourceRectangle.Left * sizeof(PixelData));

        mPixel = (PixelData*)(maskpBase + (int)maskY * maskWidth);

        for (int x = sourceRectangle.Left; x < sourceRectangle.Right; x++)
        {
            if (mPixel->red == transparentPixel.red &&
                mPixel->green == transparentPixel.green &&
                mPixel->blue == transparentPixel.blue)
            {
                // if the mask pixel color is the trasparent mask,
                // overwrite the image pixel with our transparent colour
                pPixel->red = transparentPixel.red;
                pPixel->blue = transparentPixel.blue;
                pPixel->green = transparentPixel.green;
            }
            else
            {
                // add the mask colour onto the image colour
                value = mPixel->red + pPixel->red;
                if (value > 255) value = 255;
                pPixel->red = (byte)value;
                value = mPixel->green + pPixel->green;
                if (value > 255) value = 255;
                pPixel->green = (byte)value;
                value = mPixel->blue + pPixel->blue;
                if (value > 255) value = 255;
                pPixel->blue = (byte)value;
            }

            int oldMaskX = (int)maskX;
            maskX += maskXStep;
            int dist = ((int)maskX) - oldMaskX;
            mPixel += dist;
            pPixel++;
        }

        maskX = 0;
        maskY += maskYStep;
    }

    The code above performs the masking process. The pixels are taken from a rectangle in the source image and combined with the appropriate ones in the mask image. If the source pixel corresponds with one which is set to the transparent color in the mask, the source pixel is set to transparent as well.

    Controlling the Edit Process

    The idea behind the program is that the user will take a picture with the camera and then select it for use as a sprite in the game. To do this they will have to move a cursor around the screen and also change the size of the cursor to fit the required region.

    Figure 9

    Figure 9. Selecting the part of the image to clip

    The cursor can be made to operate in two modes, either moving over the screen or changing in size. The two modes are toggled by pressing in on the joystick. When the required part of the image has been selected the use presses the Done menu key to complete the image processing and create the sprite itself. The menu is used to select the cursor mode and also to allow the user to choose between a range of differently shaped masks for the mask cursor.

    Managing the Cursor Bitmap

    The first version of the program was written so that the actual image processing was performed during the image selection, in other words the mask was applied in real time so that the user was able to see the exact result of the crop action. Unfortunately, the processor in the Smartphone is not sufficiently powerful to do this and provide an acceptable user interface, so instead the mask was converted into a cursor image which is overlaid on the background.

    This image is obtained by converting all the black portion of the mask image into a transparent color, and then drawing the result on top of the background. A method, MakeBlackTransparent, was created which performs this simple image processing task.

    When the user selects a new mask another image is created which is used in this way.

    Saving Bitmap data into image files

    Windows Mobile 5 provides a very simple mechanism for saving bitmaps into files in a variety of image formats. The Image class provides a Save method which is supplied with the path to the file to be created and the image format:

    myBitmap.Save(@"\My Documents\My Pictures\Bitmap.bmp", ImageFormat.Bmp);

    This would save the image in myBitmap into a file Bitmap.bmp with in the windows bitmap format. The ImageFormat enumerated type also lets you specify GIF, JPEG and PNG formats for the save operation. The path above causes the picture to be saved in the My Pictures subdirectory of the My Documents folder.

    Creating the Background

    The program can now produce interestingly shaped sprites which can then be used in a game. However, we also need a background for the sprites to move over. This can also be based on an image which was taken with a built in camera, but the image processing will be quite different.

    Rather than crop portions out of an image we will instead be performing image processing on the picture to make it more appropriate as a background. To do this the user may want to make the image brighter or darker, apply a color tint or add noise or other effects. The fundamental behavior of the code will be very similar, in that it will involve manipulating the colors in image data, but the action will be quite different:

    Figure 10

    Figure 10. An Image Processed Keyboard

    The above image shows a fairly boring picture of a keyboard which has been given the "wild" color process, had some noise added and then been increased in brightness to make an interesting background.

    Color Manipulation

    When performing image processing only the source image is required. There is no masking involved. The color manipulation can be performed by simply adding an adjustment value to each of the red, green and blue components of a pixel. The best way to do this is to make a single method which accepts the change values for each of the components. This single method can then be used tint an image or change the brightness simply by passing different data values in. The actual processing is quite simple, but the program must make sure that the color values do not go beyond the allowed 8 bit values:

    PixelData* pPixel;

    for (int y = 0; y < source.Height; y++)
    {
        pPixel = (PixelData*)(sourcepBase + y * sourceWidth);

        for (int x = 0; x < source.Width; x++)
        {
            int redVal = pPixel->red + redChange;
            if ( redVal <0 ) redVal = 0;
            if ( redVal > 255) redVal = 255;
            pPixel->red = (byte)redVal;
            int greenVal = pPixel->green + greenChange;
            if ( greenVal <0 ) greenVal = 0;
            if ( greenVal > 255) greenVal = 255;
            pPixel->green = (byte)greenVal;
            int blueVal = pPixel->blue + blueChange;
            if (blueVal < 0) blueVal = 0;
            if (blueVal > 255) blueVal = 255;
            pPixel->blue = (byte)blueVal;
            pPixel++;
        }
    }

    This code applies each of the change values, which can be either positive or negative, to perform the tinting action.

    Image Processing

    Most image processing programs provide a range of effects which can be used to make a picture more interesting. These include actions such as adding drawing effects and adding noise or remapping the color palette. We are going to use a couple of simple filters which will provide the user with some scope to make the background more artistic. These filters will operate only on single pixels, for effects such as blurring or lens distortion the code gets more complex as values must be transferred from one pixel to another.

    The Wild Color Filter

    One way to get a "wild" color effect is to add a random offset to the components in the color of a pixel. This will shift the color balance in potentially interesting ways. In this case the program does not limit the maximum values of the components, but instead allows them to wrap around the limits to get more interesting effects:

    int redVal = (pPixel->red + redChange) % 256;
    pPixel->red = (byte)redVal;
    int greenVal = (pPixel->green + greenChange) % 256;
    pPixel->green = (byte)greenVal;
    int blueVal = (pPixel->blue + blueChange) % 256;
    pPixel->blue = (byte)blueVal;

    In the code above the values of redChange, greenChange and blueChange are picked at random for the entire pass through the image. This very simple technique seems to result in genuinely interesting and often very artistic looking changes to the image. By using a fixed seed for the random number generator it is possible to make a given set of changes reproducible each time the program is used.

    The Noise Filter

    The noise filter randomly replaces pixels in the image with ones which have colors which have been randomly chosen. The amount of noise which is added can be controlled by the range of random numbers which trigger the swap action. A figure of 5% noise seems to give appropriate levels of noise:

    for (int y = 0; y < source.Height; y++)
    {
        pPixel = (PixelData*)(sourcepBase + y * sourceWidth);

        for (int x = 0; x < source.Width; x++)
        {
            if (rnd.Next(100) < 5)
            {
                pPixel->red = (byte)rnd.Next(255);
                pPixel->green = (byte)rnd.Next(255);
                pPixel->blue = (byte)rnd.Next(255);
            }

            pPixel++;
        }
    }

    Selecting Image Processing Actions

    Each of the image effects is selected from a given menu option. However, if the user wants to repeat a given action a number of times this would become tedious. The program has therefore been constructed so that it tracks the most recently used image processing option and then repeats this when the joystick is pushed in. This memory is managed in terms of a delegate instance which is set to point to the most recently used image processing method.

    delegate void imageProcessItem () ;

    The delegate imageProcessItem can be used to create delegate instances which refer to methods which are void and accept no parameters. The class contains a delegate which keeps track of the most recently used image processing method:

    private imageProcessItem doAddNoiseProcessItem = null;

    This is initially set to null, but when a method is called the delegate is made to refer to that method:

    private imageProcessItem doBlueTintItem = null;

    private void doBlueTint()
    {
        Cursor.Current = Cursors.WaitCursor;
        ProcessBitmaps.ProcessColor(previewImage, 0, 0, 10);

        if (doBlueTintItem == null)
        {
            doBlueTintItem = new imageProcessItem(doBlueTint);
        }

        currentItem = doBlueTintItem;
        Invalidate();

        Cursor.Current = Cursors.Default;
    }

    Rather than create a new delegate instance each time a method is called, the program instead creates a delegate for each method which is then assigned as appropriate. The code above is called when the user selects the "Blue Tint" option. First it sets the cursor to wait. Next it calls the ProcessColor method - passing it the image and the red, green and blue change value. Then it creates the delegate if required and sets the current item. Finally it puts the cursor back.

    Sample Project

    The sample project contains all the above behaviors. A single sprite can be cropped created and will then be moved over a background image, which can be selected and processed as required. The main class, GameForm, implements the movement of the sprites over the background. The MakeSpriteForm class loads in images and provides the user interface for the sprite mask selection and cropping process. The MakeBackgroundForm class loads in images and provides the user interface for the image processing actions. All the actual image processing is provided by the ProcessBitmaps class, which provides a number of static methods that perform the appropriate image updates.

    Future Work

    The program at the moment does the image capture and processing, but it does not include any of the game behavior. It is also not possible to perform any image processing on the sprites themselves. Adding these features, and making a game out of the supplied sprites would not be difficult, and is left as an exercise for the reader.

  • Don’t Fear the Garbage Collector

     

    Chris Tacke
    OpenNETCF Consulting
    August, 2007

    Download the PDF 

    Introduction

    We here at OpenNETCF Consulting just delivered version 1.0 of a plant floor monitoring system running on an x86 device with CE 5.0.  Since the application was designed for data collection and reporting, we naturally leveraged the .NET Compact Framework version 2.0 and our own Smart Device Framework to meet their functional requirements within a very aggressive schedule.

    As we got to the point where we had most of the functionality nailed down, both the customer as well as us started to get a bit concerned about the overall performance of the application.  As we added features the application's throughput dropped, and it was getting to the point of unacceptable.

    Of course the application is very complex, with multiple processes using lots of threads.  Managed and native threads routing data to and from a database through all sorts of communications layers (we'll look at the app in more detail in another article).  So what is a developer to do?  Where do you start to look for areas that you rework or refactor to improve overall solution performance?

    One behavior that we found peculiar was a fairly consistent periodicity to the application's throughput.  We have statistical counters that track message throughput of the application and we'd watch it oscillate, running up to 10 or 12 messages a seconds for a few seconds, dropping down to zero, then ramping back up over and over.

    My gut feeling was that we were seeing an artifact of Garbage Collection (GC), but before jumping into the code and blindly assuming that I was correct, we needed some data to prove the theory one way or another.

    Garbage Collection

    Before we dive into how we went about testing this theory, it's important to understand how we came up with the theory in the first place.  What about the general behavior of the application led me to infer that this might be a root cause of the problem?  To answer that question we need to take a short side trip and look at how the GC works at least at a high level.  For more in-depth coverage of how the GC does what it does, take a look at my MSDN WebCast[1] as well as the blog entries from Scott Holden[2] and Steven Pratschner[3] of the .NET Compact Framework team.

    When an application creates an instance of a reference-type object, the allocation must be backed by physical memory.  When the object is no longer in use, that memory can be released back to the system for our application or any other to use. As managed code developers, we're protected from the ugly sausage-making task of allocating and freeing the physical memory, instead we rely on the Compact Framework to cleanly handle all of this for us.

    The GC's algorithms for how it does all of this work can be a bit complex, but at a high level what it does is allocate large blocks of physical memory which it then divides into smaller "chunks" that it uses to hold our object instances.  As we destroy objects, it logically frees those chunks but doesn't necessarily free the physical memory backing it.  The GC's collection of these blocks is called the GC Heap.

    As our application plods along creating and destroying objects, "holes" are created in the GC Heap where destroyed objects once resided.  The GC keeps track of these holes and when certain thresholds or triggers are passed, it goes in and cleans them up by running finalizers, possibly shifting around the in-use blocks to collect the free space together (think of a disk defrag) and sometimes even freeing some of the allocated physical memory.  This process is known as Garbage Collection or just Collection.

    In order to do perform Collection, the GC has to make sure everything in the application is in a known safe and stable state.  It does this by suspending all threads in the application (think of the problems you'd have if a Thread was accessing some property of a class instance while at the same time the GC was moving it in memory).  It is this action - the suspension of all threads in the application - that is what many developers fear, and is what I suspected was causing the periodic performance fluctuations we were seeing.  If the GC was collecting with a similar frequency as our lows in throughput performance, it would be strong evidence that it was the culprit.

    Remote Performance Monitor

    The first step in checking the theory that the Garbage Collector was our performance-thief was to collect solid data.  Fortunately Microsoft has provided managed developers a tool called Remote Performance Monitor (RPM).  RPM has been around since version 1.0 of the Compact Framework, but unfortunately it doesn't seem to get much press short of a couple blog entries[4] and presentations for those fortunate enough to attend shows like MEDC.

    RPM is a cool tool, even up through version 2.0 (though the 3.5 version has some features that will blow you away[5]), provided you know what to make of all of the data it spews out.  Essentially what it provides is a live view of a litany of counters from the CLR.  It provides the ability to save of views of the data, and even the ability to "publish" the data to PerfMon on the desktop so you can get nice trend graphs of variables over time.

    To test our theory, however, we didn't need much data.  We simply wanted a general sense of how much memory we were allocating in our application and how often the Garbage Collector was performing Collections and compactions.

    So I fired up RPM, launched our primary data collection application through the RPM interface, and had it collect data for about a half hour while I went and had a cup of coffee.  Never trust the results of short term test runs, and never analyze data without a good dose of caffeine.  Here are some of the statistics from that first half-hour run.

      Category Counter Value
    1 Loader Total Program Run Time (ms) 1,695,518
    2 Loader Methods Loaded 5,190
    3 Loader Classes Loaded 1,623
    4 Loader Assemblies Loaded 23
    5 GC Total Bytes In Use After G 3,310,272
    6 GC Pinned Objects 489,219
    7 GC Peak Bytes Allocated (native + managed) 4,006,896
    8 GC Objects Not Moved by Compactor 2,010,250
    9 GC Objects Moved by Compactor 1,306,894
    10 GC Objects Finalized 280,433
    11 GC Managed String Objects Allocated 2,189,191
    12 GC Managed Objects Allocated 7,138,911
    13 GC Managed Bytes In Use After GC 548,092
    14 GC Managed Bytes Allocated 440,733,748
    15 GC GC Latency Time (ms) 14,702
    16 GC GC Compactions 409
    17 GC Collections (GC) 412
    18 GC Code Pitchings 2
    19 GC Calls to GC.Collect 0
    20 GC Bytes of String Objects Allocated 184,459,896
    21 GC Bytes Collected By GC 440,063,976
    22 GC Boxed Value Types 531,631

     

    I've numbered each line so we can run through them and discuss what each number means.

    You can see from line 1 that we had a run time of 28 minutes 15.518 seconds.  Lines 2-4 say that the loader loaded 5190 methods and 1623 classes out of 23 separate assemblies during this run period.

    Line 5: At the time this view was captured, there was about 3.3MB in use by the application immediately following the most recent Collection.

    Line 6: The GC has encountered nearly half a million pinned objects during its Collections (see line 17).

    Line 7: The maximum amount of allocated memory in use at any given time through this run was just over 4MB.

    Line 8: Just over 2 million objects were not moveable during compaction.  This indicates a high number of pinned objects when coupled with Lines 9 and 16.

    Line 9: A cumulative total of 1.3 million objects were moved during Compactions (line 16).

    Line 10: 280 thousand items had to be Finalized.  Any item with a finalizer survives two GC Collections before its GC Heap allocation is completely freed [5].  This seems like a lot, but it's not evident immediately whether these are from our application or from assemblies were using (e.g. SQL CE or the CF itself).  If this were causing a problem, I'd expect the GC heap to be having size problems, which I don't think is the case, so it doesn't cause me any undue concern.

    Line 11: We've allocated over 2.1 million strings in 28 minutes.  While not overly alarming, it is the most suspect thing so far. That's a rate of about 1290 string allocations per second. This is a potential source of garbage and looking at the code to try to reduce this rate might be a good idea.

    Line 12: We've allocated over 7.1 million managed object in 28 minutes.  Again this isn't overly alarming, but it does mean that we're allocating objects at a rate of over 4200 per seconds.  We might want to investigate places in the code where objects are created instead of reused.  It's also interesting that roughly 30% of our total allocations have been strings (see line 11).  It doesn't mean a lot by itself, but it's a data point to remember as I profile other applications.

    Line 13:  We had 548kB of managed objects still live after the last GC.  When we couple this statistic with line 5 (bytes in use after GC) we see that a large amount of our allocations appear to be unmanaged.  Due to the architecture of this application, that doesn't surprise me, so it's not a cause for concern.

    Line 14:  In 28 minutes we've allocated over 440MB of data.  Wow.  That's a lot of allocation.

    Line 15: We've had a GC latency of almost 15 seconds.  More on this one later.

    Lines 16-17: We've had 412 Collections and 409 Compactions.  That means we're collecting on average once every 4.1 seconds, and nearly every Collection also requires a compaction.  This means we're generating a lot of garbage, and the GC Heap is quite fragmented.

    Line 18: We've only had to pitch code twice.  This tells me we're not having any low-memory conditions.

    Line 19: Our code never calls GC.Collect.  Nothing more to be said.

    Line 20: Coupled with line 11 (bytes of string objects) this tells me that our string objects average about 26 bytes each.  Considering that we're Unicode and that managed strings also carry a 4-byte length with them, each string is, on average, less than 10 characters.  That's a lot of small strings.  Again, seeing if we can reuse string might be worthwhile.

    Line 21: This number is really close to Line 14 (managed bytes allocated).  This tells me that we're probably not leaking and memory, we're just churning a whole lot of it.

    Line 22: This says we have a boxing rate of over 300 boxing operations a second.  While not distressing, we might want to at least look around and see if there's some obvious place that we're getting implicit boxing that we don't need.

    So what do all these statistics means when looked at in aggregate?  I see a whole lot of allocation and collection.  This system is designed to run 24/7, so if we extrapolate out these numbers to a day of running we would then estimate that we'd churn (allocate and release) nearly 23 Gigabytes of data and Collect over 21,000 times.  Sounds a bit frightening in those terms doesn't it?

    The real gem of information here, though, is line 15 - GC Latency.  This is the total amount of time that was spent by the system doing those 412 Collections and it's only 14.7 seconds.  That means each collection is really taking only about 35 milliseconds.  To bring that even further into perspective, we spent 14.7 seconds out of over 28 minutes collecting, so or about 0.87% of our execution time. 

    So while the raw numbers look big, we're actually spending less than 1% of our total execution time doing collections.  My first reaction to this fact was to re-run all of my calculations.  My second reaction was one of amazement and reverence.  The Compact Framework team has obviously done a hell of a good job refining the GC's algorithms and they deserve recognition for that.  The Collection statistics for this application could have been much, much worse if they hadn't done such a good job.

    Does this mean I can rest easy?  Not at all. We're still churning a lot of data and creating a whole lot of objects.  It's still a worthwhile exercise to go in and spend a couple hours trying to reduce the both the Collection and churn rate, but it also means that we're going to quickly have diminishing returns.  Spending a days or weeks trying to eke out every last performance gain would be foolish.

    In fact I did revisit the code and spent about two hours reworking some of the code to reduce allocations.  It turned out that we were generating a lot of debugging information that we used for logging even when the logging was turned off (the objects were created, just never used).  After cleaning this, and a few other areas up I got the GC rate down to about once every 6 seconds, or about 0.6% of run time which is nearly a 50% improvement, but the reality was that it was still a very miniscule part of the overall run and it lead to no noticeable improvement in throughput.

    Summary

    So what did we learn from this exercise, other than we needed to look elsewhere for the performance bottle necks (they turned out to be in our usage of the SQL CE database, and I'll be going into depth on what we learned there in a later article)?

    Don't fear GC.  The GC has an extremely important job.  It is the magic box that allows us to worry about our architecture and not about memory leaks, buffer overruns and access violations.  The Compact Framework team has made the GC work well and as transparent as we could hope (though I definitely don't want them to stop trying to improve it or allowing us to have more control over it).  The GC allows us to develop far more stable code in a much shorter period of time, and we must accept that it's not something to be feared or avoided.

    We also got a little practice in using the tools Microsoft provides to help us analyze our systems.  Without tools like this we'd be left guessing about what to work on and I'd probably still be trying to reduce the garbage the application is creating.

    We still need to be cognizant of the garbage we create, but maybe in the future the first thing that comes to mind when we have a performance problem shouldn't be the Garbage Collector or the runtime.  Instead maybe we should instead ask ourselves what mistake we made in our own architecture or implementation.  After all even the best system can't compensate for poor code, and none of us are immune to making mistakes no matter how much experience we have.

    References

    [1] http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032318790&Culture=en-US
    [2] http://blogs.msdn.com/scottholden/archive/2004/12/28/339733.aspx
    [3] http://blogs.msdn.com/stevenpr/archive/tags/GC/default.aspx
    [4] http://blogs.msdn.com/stevenpr/archive/2006/04/17/577636.aspx
    [5] Microsoft hasn't yet officially published any RPM 3.5 stuff, but when they do we'll update this reference
    [6] <Insert link to MEDC decks on Community>
        Also See Reference 1