Mobile Development - 'Support Side Story'

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

April, 2008

  • Mobile Development - 'Support Side Story'

    Weird issue with NETCF v2 SP2? Test 0ut v3.5 then!

    • 3 Comments

    I think this comes with experience... when you're facing a very odd issue, chances are that you're watching at a BUG in action. For example, last time it was a WebService call over GPRS which seemed to be processed successfully on the server, but returned an InvalidOperationException on the device. The problem was not with the GPRS Operator, it was not specific to any OEM-customization and it wasn't specific to Windows Mobile's version. So it must probably have been something with the NETCF itself...

    Before starting deeply troubleshooting in whatever direction (in this case it might have been network traces to look at SOAP messages, instrument the code, try to reproduce with Cellular Emulator...), you may verify if the issue is reproduced when the application runs on top of the NETCF v3.5. Note that a re-compilation of the code through VS2008 is NOT needed – you can use a .CONFIG file to instruct the CLR to load v3.5 assemblies even for an application developed against v2. See here for a post by David Kline about this. The configuration file must be named “yourApplication.EXE.CONFIG” (note the “.EXE”) and must be placed in the same folder of the “yourApplication.EXE” (in a NETCF Loader Log this is what's called "Policy file"), and it must contain:

    <configuration>
      <startup>
        <supportedRuntime version="v3.5.7283"/>
      </startup>
    </configuration>

    To make sure the application is running on top of the v3.5, you should check if the mscoree3_5.dll is a loaded module. If you can't attach Visual Studio's debugger and can't even connect the device to a PC with Remote Tools installed (Remote Process Viewer), then you can for example use JShell directly on the device, which is a sort of command-line tool available through the "Windows Mobile Developer Power Toys". Remember to completely exit the application before running the test or even better to soft-reset the device.

    Obviously you need to install the NETCF v3.5 on the device before doing so: the redistributable is available here

     

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    Programming the Connection Manager: how do I debug my code?

    • 1 Comments

    This post is somehow related to a previous one (Establishing GPRS Connection on Windows CE and Windows Mobile: Sample Codes), at least for the Windows Mobile-part. On Windows Mobile, Connection Manager is smart enough to the use the less expensive network available at the moment: hence, it'll open a GPRS connection only if the device is not connected through ActiveSync\WMDC's Desktop Passthrough and WiFi is not available. This depends on how you've provisioned the CM_Planner Configuration Service Provider, however that's the usual configuration.

    However, how can I debug my code when it invokes ConnMgr APIs? It's simple (when you know it...): just don't use ActiveSync\WMDC. I'm talking about:

    1. Disable USB under ActiveSync\WMDC's Connection Settings (i.e. uncheck "Allow USB connections")
    2. Follow suggestions provided by the VSD Team for Windows CE 5.0 here: they apply to Windows Mobile devices as well - basically you need to manually do what Visual Studio automatically does through ActiveSync\WMDC
    3. Note that you'll need to know the IP address of the device: to do so you can use the Windows Mobile Network Analyzer PowerToy, but if you only need to know the current IP Address, then a very quick solution is to display it within the Home Screen through for example the Today IP 2005 Home plugin (it works even on WM6).

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    GPS Programming Tips for Windows Mobile - Part 3

    • 1 Comments

    - GPS Signal Quality

    - Error Reduction

    - Avoid problems with localizations

    - Data Layer: do NOT use XML files...

    - Hints about showing itinerary with GPS Data independently on current device resolution\orientation

    (- links to this project on Codeplex and to webcast Creating Location-Aware Applications for Windows Mobile Devices (Level 300))

    [Part 1 and 2 are available here and here.]

    You may start from the NMEA basic parser sample provided by GeoFrameworks through their Mastering GPS Programming. When GPS signal quality is low, the GPS sends some data anyway [100Bytes - 300Bytes], so checking if you're reading 0 Bytes is not a good mean for signal quality. The values you can monitor within the NMEA sentences are:

    • HDOP = Words[8] of the GPGGA
    • QOS = Words[5] of the GPGGA
    • Satellites in view = Words[6] of the GPGGA

    Then, there may be various ways you can reduce the error, which is there even if the signal quality is good. A quite straightforward one is to calculate the average of X positions received in a timeframe (imagine a buffer filled with NMEA sentences retrieved from the antenna). However this is dependent on the average speed and sincerely for the position I would not stress on finding an average. This is not true for example for speed values coming from the antenna, because their range may vary a bit even if you're running at the same speed (and I remembered I've read somewhere in the SportsDo website that their program uses a NASA's algorythm for speed values! - this was out of scope in my case... Hot [I found it (see "Data Correction") - it says that the algorithm is for altitude, but I'd assume it's the same for speed as well])

    On top of this, consider extracting the GPS location from GPRMC, GPGGA and GPGLL at the same time. Note that not all the GPS Antenna rely on the same NMEA sentences, and not all have the same "sentence-pattern" - for example Sirfstar doesn't have GPGLL, and its pattern is:

    5x 
       $GPRMC - Recommended Minimum specific GPS/TRANSIT data
       $GPVTG - Track Made Good and Ground Speed
       $GPGGA - Global Positioning System fix data
       $GPGSA - GPS DOP and Satellites Active
    3x
       $GPGSV - Satellites in View

    In my case I was simply interested on the following and it sufficed:

    • GPRMC for Lat\Long and Speed
    • GPGGA for Lat\Long and Altitude

    So, on top of the NMEA Parser, I considered an "intermediate" layer responsible for notifying upper UI with location changes. Regarding this, remember that if you have to parse Double values, then you must take in account the CurrentCulture. The NMEA Interpreter sample provided by GeoFrameworks simply set the current culture to en-US, and I basically did the same for all the UIs and intermediate layers: this avoids problems with different cultures and also provides a simple way to store data in the database consistently. Also, it allows you to be able to move the database from a device to another, or even to a PC if you want (SportsDo, for example, allows you to upload the data of the activities to their server).

    Regarding the database: for a very initial draft of the application I had used the old way, i.e. store the data into a XML file. I chose so also in order not to ask my peers to install additional software's CABs (SQL Compact 3.5 or at least 3.1, which consist of 3 CABs) on low-resources old devices. The code was working fine, but after some "activities" we realized that the file was growing very fast, and writing was slower after each saved activity... we've even got OutOfMemoryExceptions sometimes! (see my previous post about NETCF Memory Leaks here) So, lesson learned: use SQL Compact, even if it'll require additional CABs when installing the application. Nerd

    So, to recap, imagine:

    • a "physical" layer that retrieves data from a Bluetooth antenna (and an intermediate layer that handles BT signal loss)
    • a NMEA Interpreter and its intermediate layer that handles error reduction, informs upper layers
    • a data Intermediate layer listening on notifications from the NMEA one and storing data

    So far we're running (or going bycicle, or rafting, whatsoever)... at the end of the activity we want to see data and show to friends to let them see how cool our Windows Mobile device is... Tongue out Well, playing with Graphics can be straightforward or painful depending on your goals. At the end of the day we're talking about a matrix of points representing the pairs latitude\longitude, speed\time, distance\time, altitude\time, etc... that you want to display, hopefully independently on device resolution and orientation. Adapting the application to the actual device capabilities can be done in many ways (and many others before me posted about that), see for example:

    Remember that Graphic objects internally held a reference to NATIVE resources, therefore you must explicitly call .Dispose or use the "using" directive, which automatically call .Dispose when finished with the object for you. A possible draft-code is the following: even if it does what it's meant to do, I'm sure there are better ways... (for example, it may show info about speed during the activity - as SportsDo does, and technically-speaking it doesn't use double-buffering, nor even SuspendLayout\ResumeLayout, etc., etc., etc...) I'm pasting here as it might give some ideas anyway:

    /// <summary>
    /// Show data details: takes a datatable (it could have been a hashtable or a dictionary), and depending on category, calculates ranges for X and Y
    /// positions and creates a Points[] structure. Then use this.CreateGraphics to draw lines and polygons
    /// </summary>
    /// <param name="category">data category to show</param>
    /// <param name="table">table containing data</param>
    internal void Show(string category, DataTable table)
    {
        Cursor.Current = Cursors.WaitCursor;
    
        //this is done at the beginning so that this.CreateGraphics can work
        this.Show();
        //let's make sure the Message Pump is not blocked
        Application.DoEvents();
    
        //table has 2 columns
        //when showing speed, distance, altitude, etc over time
        //Column1 is where relevant data is saved
        //Column2 is where time data is saved
        //when showing path, Column1 is the latitude and Column2 the longitude
        int Column1 = 0; 
        int Column2 = 0; 
    
        //when showing speed, distance, altitude a string will be drawn with max value
        string unit = string.Empty;
        
        //boolean values set depending on data to be shown
        //bool bSpeed = false;
        //bool bDistance = false;
        //bool bAltitude = false;
        bool bPosition = false;
    
        //depending on the category to "show", previous variables may assume different values
        #region switch (category)
        switch (category)
        {
            case "speed":
                Column1 = 1;
                Column2 = 0;
                unit = "kmph";
                //bSpeed = true;
                //bDistance = false;
                //bAltitude = false;
                bPosition = false;
                break;
            case "distance":
                Column1 = 1;
                Column2 = 0;
                unit = "km";
                //bSpeed = false;
                //bDistance = true;
                //bAltitude = false;
                bPosition = false;
                break;
            case "altitude":
                Column1 = 1;
                Column2 = 0;
                unit = "m";
                //bSpeed = false;
                //bDistance = false;
                //bAltitude = true;
                bPosition = false;
                break;
            case "position":
                Column1 = 0;
                Column2 = 1;
                //bSpeed = false;
                //bDistance = false;
                //bAltitude = false;
                bPosition = true;
                break;
            //default:
            //    //...
            //    break;
        }
        #endregion
    
        #region check if data was saved
        if (table.Rows.Count == 0) //no data has been saved
        {
            using (Graphics g = this.CreateGraphics())
            {
                Font f = new Font(FontFamily.GenericSansSerif, 16.0f, FontStyle.Bold);
                SolidBrush b = new SolidBrush(Color.Black);
                g.DrawString("NO DATA", f, b, 0.0f, 0.0f);
                b.Dispose();
                f.Dispose();
            }
            return;
        }
        #endregion
    
        //to maintain the code independent on the orientation of the device
        int ShortestSideOfTheDevice = Math.Min(this.ClientSize.Width, this.ClientSize.Height);
    
        //array to store points to be drawn
        Point[] points;
    
        //min,max, range values for latitude and longitude
        double minX, maxX, rangeX, minY, maxY, rangeY;
    
        #region fill points[] and calculate minX, maxX, rangeX, minY, maxY, rangeY when bPosition = false
        if (!bPosition)
        {
            //assume last row contains the total elapsed seconds
            maxX = double.Parse(table.Rows[count - 1][Column2].ToString(), frmMain.Instance.DataCultureInfo); 
            minX = 0;
            rangeX = maxX - minX;
    
            maxY = FindMax(table, Column1);
            minY = FindMin(table, Column1);
            rangeY = maxY - minY;
    
            //initialize Point array
            points = new Point[table.Rows.Count + 2];
            //adding 2 points [0, Height] and [Width, Height] because we want a polygon
            //first point
            points[0].X = 0;
            points[0].Y = this.ClientSize.Height;
            //last point
            points[table.Rows.Count + 1].X = this.ClientSize.Width;
            points[table.Rows.Count + 1].Y = this.ClientSize.Height;
    
            //fill Point array
            int i = 1;
            double percentX, percentY;
            foreach (DataRow row in table.Rows)
            {
                //handle range = 0, otherwise possible DivideByZeroException
                if (rangeX == 0.0f)
                    percentX = 100.0f;
                else
                    percentX = (double.Parse(row[Column2].ToString(), frmMain.Instance.DataCultureInfo) - minX) / rangeX;
    
                //handle range = 0, otherwise possible DivideByZeroException
                if (rangeY == 0.0f)
                    percentY = 100.0f;
                else
                    percentY = (double.Parse(row[Column1].ToString(), frmMain.Instance.DataCultureInfo) - minY) / rangeY;
    
                //discard the point if NaN
                if (!(double.IsNaN(percentX) || double.IsNaN(percentY)))
                {
                    points[i].X = Convert.ToInt32(this.ClientSize.Width * percentX);
                    points[i].Y = this.ClientSize.Height - Convert.ToInt32(this.ClientSize.Height * percentY);
                    ++i;
                }
            }
        }
        #endregion
    
        #region fill points[] and calculate minX, maxX, rangeX, minY, maxY, rangeY when bPosition = true
        else //bPosition = true
        {
            //find Min\Max for latitude\longitude so that we can scale the path
            //note that coordinates may be negative, when represented by doubles
            //this applies to minX, minY, maxX, maxY as well
            maxX = FindMax(table, Column2);
            minX = FindMin(table, Column2);
            rangeX = maxX - minX;
    
            maxY = FindMax(table, Column1);
            minY = FindMin(table, Column1);
            rangeY = maxY - minY;
    
            //initialize Point array
            points = new Point[table.Rows.Count];
    
            //in order to put the "image" at the center:
            //1. calculate current center in terms of latitude\longitude
            double centerX = minX + rangeX / 2;
            double centerY = minY + rangeY / 2;
    
            //2. calculate position in pixel of the current center
            double percentCenterX = (centerX - minX) / Math.Max(rangeX, rangeY);
            double percentCenterY = (centerY - minY) / Math.Max(rangeX, rangeY);
            double PixelCenterX = Convert.ToInt32(ShortestSideOfTheDevice * percentCenterX);
            double PixelCenterY = ShortestSideOfTheDevice - Convert.ToInt32(ShortestSideOfTheDevice * percentCenterY);
    
            //3. shift to center screen
            double PixelToShiftX = (ShortestSideOfTheDevice / 2) - PixelCenterX;
            double PixelToShiftY = (ShortestSideOfTheDevice / 2) - PixelCenterY;
    
            //now for each point:
            //    points[j].X += Convert.ToInt32(PixelToShiftX);
            //    points[j].Y -= Convert.ToInt32(PixelToShiftY);        
            
            //calculate percentage of the positions respect to the range
            //note that max(rangeX, rangeY) will be "mapped" to the min(this.ClientSize.Width, this.ClientSize.Height)
            //so that we can handle different orientations 
            int i = 0;
            double percentX, percentY;
    
            //to make the calculations hemisphere-independent, calculate percentages with absolute values
            foreach (DataRow row in table.Rows)
            {
                percentX = Math.Abs(
                    (Math.Abs(double.Parse(row[Column2].ToString(), frmMain.Instance.DataCultureInfo)) - Math.Abs(minX)) / 
                        Math.Max(rangeX, rangeY)
                    );
                percentY = Math.Abs(
                    (Math.Abs(double.Parse(row[Column1].ToString(), frmMain.Instance.DataCultureInfo)) - Math.Abs(minY)) / 
                        Math.Max(rangeX, rangeY)
                    );
    
                //discard the point if NaN
                if (!(double.IsNaN(percentX) || double.IsNaN(percentY)))
                {
                    //now fill the Point array: percentX and percentY are non-negative values
                    //PixelToShiftX and PixelToShiftY allow to center the image
                    points[i].X = Convert.ToInt32(ShortestSideOfTheDevice * percentX) + Convert.ToInt32(PixelToShiftX);
                    points[i].Y = ShortestSideOfTheDevice - Convert.ToInt32(ShortestSideOfTheDevice * percentY) - Convert.ToInt32(PixelToShiftY);
                    ++i;
                }
            }
        }
        #endregion
    
        #region ACTUAL DRAWING
        using (Graphics g = this.CreateGraphics())
        {
            using (SolidBrush b = new SolidBrush(Color.Indigo))
            {
                g.Clear(Color.White);
           
                //if showing speed, distance, altitude: draw a polygon
                if (!bPosition)
                {
                    g.FillPolygon(b, points);
                    using (Font f = new Font(FontFamily.GenericSansSerif, 10.0f, FontStyle.Bold))
                    {
                        b.Color = Color.Black;
                        g.DrawString(string.Format("Max: {0} {1}", maxY.ToString("F3", frmMain.Instance.DataCultureInfo), unit), f, b, 0.0f, 0.0f);
                    }
                }
                else //bPosition == true
                {
                    //draw path area - this is a square independently on the device
                    b.Color = Color.Honeydew;
                    g.FillRectangle(b, 0, 0, ShortestSideOfTheDevice, ShortestSideOfTheDevice);
                    
                    //draw path grid, one line for each km (horizontal and vertical)
                    double KmInRange = GpsLocation.CalculateLinearDistance(
                            new GpsLocation(0.0f, 0.0f, 0.0f, string.Empty, string.Empty),
                            new GpsLocation(0.0f, Math.Max(rangeX, rangeY), 0.0f, string.Empty, string.Empty));
            
                    using (Pen p = new Pen(Color.PaleTurquoise, 0.2f))
                    {
                        for (int i = 0; i < Math.Ceiling(KmInRange); i++)
                        {
                            g.DrawLine(p, 20 + i * Convert.ToInt32(ShortestSideOfTheDevice / KmInRange), 0, 20 + i * Convert.ToInt32(ShortestSideOfTheDevice / KmInRange), ShortestSideOfTheDevice);
                            g.DrawLine(p, 0, 20 + i * Convert.ToInt32(ShortestSideOfTheDevice / KmInRange), ShortestSideOfTheDevice, 20 + i * Convert.ToInt32(ShortestSideOfTheDevice / KmInRange));
                        }
                    }
                    
                    //draw path
                    using (Pen p = new Pen(Color.Blue, 1.0f))
                    {
                        g.DrawLines(p, points);
                    }
    
                    //Start point
                    b.Color = Color.Green;
                    g.FillEllipse(b, points[0].X - 3, points[0].Y - 3, 6, 6); 
                    
                    //Stop point
                    b.Color = Color.Red;
                    g.FillEllipse(b, points[points.GetLength(0) - 1].X - 2, points[points.GetLength(0) - 1].Y - 2, 4, 4); 
                }
            }
        }
        #endregion
    
        Cursor.Current = Cursors.Default;
    }

    The results of that on different platforms are, for path and speed:

    • Smartphone, 320x240:

    320x240_WM6Standard_path 320x240_WM6Standard_speed

    • Pocket PC, 240x240

    240x240_WM6Pro_path  240x240_WM6Pro_speed

    • Pocket PC, 640x480 landscape

    480x640_WM6Pro_path  480x640_WM6Pro_speed

    NEXT STEP: now that I have played with representation of GPS data, I want to see how difficult it can be to show ON THE DEVICE a map by using Virtual Earth (through GPX file format)...

    After that, now that I can really appreciate what the GPS Intermediate Driver can do for me (no need of Bluetooth and NMEA Intermediate layers!), I'll write a new version of the application: this time I'll also use SSCE 3.5 and probably the OrientationAware Application Block of the Mobile Client Software Factory... Smile ehy! I won't re-invent the wheel: check out this project on Codeplex!!

    Cheers,

    ~raffaele

    P.S. Check out what the MVP Maarten Struys has to say in this recent Level 300-Webcast!

    Creating Location-Aware Applications for Windows Mobile Devices (Level 300)

  • Mobile Development - 'Support Side Story'

    Make sure you realize (or realise...) Your Potential with Microsoft Technical Support!!

    • 1 Comments

    Ehy, check this out!! I felt really honored when my friend Chris proposed to interview me: he gave me the wonderful opportunity not only to present myself but above all to talk about the opportunities developers have with Microsoft Technical Support: make sure you get what you've paid for! Open-mouthed This is one of the interviews Chris is conducting with some players in the Windows Mobile Development arena: really a good idea mate!!

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    GPS Programming Tips for Windows Mobile - Part 2

    • 1 Comments

     

    - Useful samples on the web

    - Bluetooth Shared Source possible enhancements

    As I wrote in a previous post, in my case I wanted to play (once in my life!) with NMEA sentences and therefore didn't want to use GPS Intermediate Driver, at least for the first "release" of the (for-my-own-fun-) application: but now that I've done it (and the application is working indeed!), I'm sure that the next version will be based on it. Only when you don't have something you can really appreciate it when finally getting it... Smile

    Now, let's talk about useful samples on the web, not related to the GPS Intermediate Driver. A very good place to look for info for GPS-programming is GeoFrameworks. Their Mastering GPS Programming is a very good guide if you want to start programming location-aware applications. They even sell a set of controls, but I can't express on them since I didn't test them. Jon Person also made it available here.

    Also, as usual when looking for NETCF sample code, OpenNETCF is the first place. Microsoft MVPs have done a really extraordinary job on providing NETCF developers with what was and is missing respect to the Desktop Framework through their Smart Device Framework, and some other components as well. For GPS they created the Shared Source Serial and GPS Library

    In any case, if you don't want to use GPSID, then you must provide a sort of "physical" layer responsible for retrieving data from the GPS antenna. In this case Microsoft comes to help, through the Windows Embedded Source Tools for Bluetooth Technology (my GPS Antenna was a paired Bluetooth one). I slightly modified it in order to better accomplish my goals, and probably it's worth mentioning here.

    For example, in my Configuration Form I wanted to have a ComboBox showing the currently paired devices. In order to easily use the .DataSource property of the combo (or any other ListControl-based control) you may add a read-only "PairedDevicesArrayList" property:

    /// <summary> 
    /// ArrayList needed to easily fill a ListControl-based control (such as a Combo) through its .DataSource property 
    /// </summary> 
    public ArrayList PairedDevicesArrayList 
    { 
        get 
        { 
            ArrayList temp = new ArrayList(); 
            foreach (BluetoothDevice dev in btRadio.PairedDevices) 
            { 
                temp.Add(new BluetoothDevice(dev.Name, dev.Address)); 
            } 
            return temp; 
        } 
    }

    so that the UI could use something like:

    cmbPairedDevices.DataSource = BtLayer.PairedDevicesArrayList; 
    cmbPairedDevices.DisplayMember = "Name"; 
    if (cmbPairedDevices.Items.Count != 0) 
        cmbPairedDevices.SelectedIndex = 0; 

    The previous property is based on BluetoothDeviceCollection.PairedDevices, and I noticed that under the registry key containing paired devices info, there were some so-called "devices" I wasn't interested about, such as "activesync", "headset", "modem", etc. So I slightly modified the sample code here as well:

    /// <summary> 
    /// A collection representing Bluetooth devices which have been previously paired with this device. 
    /// </summary> 
    public BluetoothDeviceCollection PairedDevices 
    { 
        get 
        { 
            BluetoothDeviceCollection pairedDevices = new BluetoothDeviceCollection(); 
            const string BT_DEVICE_KEY_NAME = "Software\\Microsoft\\Bluetooth\\Device"; 
            IntPtr btDeviceKey = Registry.OpenKey(Registry.GetRootKey(Registry.HKey.LOCAL_MACHINE), BT_DEVICE_KEY_NAME); 
            ArrayList subKeyNames = Registry.GetSubKeyNames(btDeviceKey); 
            foreach (string deviceAddr in subKeyNames) 
            { 
                string deviceName = ""; 
                byte[] deviceAddress = new byte[8]; 
                IntPtr currentDeviceKey = Registry.OpenKey(btDeviceKey, deviceAddr); 
                deviceName = (string)Registry.GetValue(currentDeviceKey, "name"); 
                //RAFFAEL 
                //2 possible checks: 
                //1. the key name has some numbers 
                //2. the key has a subkey "Services" <-- choose this, good enough for now (but this can be done better) 
                ArrayList subKeyCheckList = Registry.GetSubKeyNames(currentDeviceKey); 
                if (subKeyCheckList.Count != 0) //at least one subkey 
                { 
                    foreach(object subkeyName in subKeyCheckList) 
                    { 
                        if ((string)subkeyName != "Services") //Services is not there 
                            break; //exit foreach 
                        else //Service is there 
                        { 
                            long longDeviceAddress = long.Parse(deviceAddr, System.Globalization.NumberStyles.HexNumber); 
                            BitConverter.GetBytes(longDeviceAddress).CopyTo(deviceAddress, 0); 
                            BluetoothDevice currentDevice = new BluetoothDevice(deviceName, deviceAddress); 
                            pairedDevices.Add(currentDevice); 
                        } 
                    } 
                } 
                Registry.CloseKey(currentDeviceKey); 
                //END RAFFAEL 
            } 
            Registry.CloseKey(btDeviceKey); 
            return pairedDevices; 
        } 
    }

    Then, it's a matter of connecting to the selected paired device, through BluetoothDevice.Connect() method:

    /// <summary> 
    /// Connect to the Serial Port service of the paired device whose index within the Paired Devices collection is 'selectedIndex' 
    /// </summary> 
    /// <param name="selectedIndex">index of the paired devices collection</param> 
    /// <returns></returns> 
    public bool ConnectToSerialPortOfSelectedPairedDevice(int selectedIndex) 
    { 
        bool res = true; 
    
        //check if paired device is correctly opened 
        if (null == (gpsDevice = (BluetoothDevice)BtRadio.PairedDevices[selectedIndex])) 
        { 
            //MessageBox.Show("Can't find any paired device."); 
            //or throw an exception that is caught by upper layers
            return false; 
        } 
    
        //try to connect to the Serial Port service for MAX_RETRIES times 
        int count = 0; 
        while ((null == (gpsStream = gpsDevice.Connect(StandardServices.SerialPortServiceGuid))) && (count < MAX_RETRIES)) 
        { 
            ++count; 
            if (count >= MAX_RETRIES) 
            { 
                res = false; 
                break; 
            } 
        } 
        return res; 
    }

    And finally you can use the data to fill a buffer, for example, through the NetworkStream.Read() method. Note that if you then pass the buffer to a NMEA Interpreter, you must remember to "clean" its contents in case your algorithm doesn't guarantee that the buffer is always filled the same way. And remember that NMEA sentences don't have a fixed length, also because this depends on how antennas-manufacturers chose the "output pattern" and at which point in time the buffer was started being filled. If you forget to "reset" the buffer (as I was doing...) you may see that the application will start thinking that you're going back and forward from a location you were in a previous point in time... Confused

    Here you should also implement an algorithm to handle the loss of Bluetooth signal (which is something different from the "quality of the GPS signal").

    //gpsStream is a NetworkStream object, representing the stream which reading data from. 
    //BluetoothRadio and BluetoothDevice use WINSOCK to connect to the antenna 
    ...
    bCanRead = this.gpsStream.CanRead; 
    if (bCanRead) 
    { 
        //clean the buffer
        for (int i = 0; i<buffer.Length; ++i) 
            buffer[i] = 0; 
    
        //now read
        try 
        { 
            bRead0Bytes = (0 == this.gpsStream.Read(buffer, 0, BUFFERSIZE - 1)); 
        } 
        catch (System.IO.IOException ioex) 
        { 
            //If you receive an IOException, check the InnerException property to determine if it was caused by a SocketException. 
            //If so, use the ErrorCode property to obtain the specific error code, and refer to the Windows Sockets version 2 API 
            //error code documentation in MSDN for a detailed description of the error. 
            if (ioex.InnerException.GetType() == typeof(SocketException)) 
            { 
                int socketerr = ((SocketException)(ioex.InnerException)).ErrorCode; 
                throw new Exception(string.Format("SocketException - error code: {0}", socketerr)); 
            } 
            throw new Exception("IOException while trying to read from GPS"); 
        } 
        catch (Exception ex)
        {
            //throw exception to upper layers
        }
    }

    In the next post I'll talk a bit about the intrinsic limitations of the GPS Signal Quality and possible way to reduce the errors. Among other concepts I'll show how to possibly exhibit location data through Drawing objects, in order to receive graphs like for example the following ones:

    320x240_WM6Standard_path 320x240_WM6Standard_speed

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    Encrypting\Decrypting Data through PFX certificate on Windows Mobile

    • 1 Comments

    Some time ago I worked with a colleague of mine, a proud member of the CSS for Developers (Alejandro Campos Magencio) on a case where the developer wanted to encrypt and decrypt data on Windows Mobile 5.0 devices by using PFX certificates enrolled on the device. Crypto APIs are quite limited on this platform, however by using some basic (i.e. supported) APIs we were able to reach a satisfactory solution.

    In very few words, PFX certificates contain a pair of public and private key: through the public you can encrypt, through the private you can decrypt. But... first of all, you need to register the certificate on the device's ROOT or Personal store. On Windows Mobile 6, an enroller was added to the platform therefore you can register PFX certificate by simply tapping on it, as it was already with .CER files on previous versions of the OS; but on Windows Mobile 5.0, you can't add a PFX certificate by doing so, nor you can use the documented way to add certificates by using the CertificateStore Configuration Service Provider, because contrarily to CER certificates, you can't export a .PFX to a Base-64 encoded X.509 stream (see Creating a Provisioning XML Document For The Root Certificate).

    So, how to add a PFX certificate to the ROOT store of a Windows Mobile 5.0-based device? The answer is: develop your own enroller. Or, find out if anyone else already did it... and a sample 3rd party enroller for .PFX files is the one described by Personal Certificate Import Utility for Pocket PC 2003 and Windows Mobile, which seems to be still valid for 5.0 as well. You can also use directly that 3rd party open-source product if you want, but in case you’ll have problems we couldn’t be able to support as this is not a Microsoft product.

    In any case, remember that "[…] Whether a root certificate can be installed on the device depends on how the device was configured by the original equipment manufacturer (OEM) or by the Mobile Operator", in terms of Security Configuration (this was an excerpt from the KB Article How to install root certificates on a Windows Mobile-based device), so if you can't even install the SDK Certificates, for example, then you have a locked device and therefore you should ask you OEM or Mobile Operator how they require you to install new certificates, if they even provide this opportunity.

    Recommended readings:

    Finally, here it is some pseudo-code (provided 'AS IS', for didactic purposes, no error-checking and no example data included - just look at which Crypto APIs are needed):

    - for encrypting:

    // Variables
    HCERTSTORE hStoreHandle = NULL;
    PCCERT_CONTEXT pSubjectCert = NULL;
    HCRYPTKEY hPubKey = NULL;
    wchar_t * SubjectName;
    DWORD dwEncryptedLen = 0;
    BYTE* pbData = NULL;
    
    // Open the certificate store
    hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, NULL);
    
    // Get a certificate that matches the search criteria
    pSubjectCert = CertFindCertificateInStore(hStoreHandle, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, SubjectName, pSubjectCert);
    
    // Get the CSP
    bResult = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
    
    // Get the public key from the certificate
    bResult = CryptImportPublicKeyInfoEx(hCryptProv, X509_ASN_ENCODING, &pSubjectCert->pCertInfo->SubjectPublicKeyInfo, CALG_RSA_KEYX, 0, NULL, &hPubKey);
    
    // Get lenght for encrypted data
    bResult = CryptEncrypt(hPubKey, NULL, TRUE, 0, NULL, &dwEncryptedLen, 0);
    
    // Create a buffer for encrypted data
    pbData = (BYTE *)malloc(pbData, dwEncryptedLen);
        
    // Encrypt data
    bResult = CryptEncrypt(hPubKey, NULL, TRUE, 0, pbData, &dwEncryptedLen, dwEncryptedLen);
    
    //CertFreeCertificateContext
    //CertCloseStore

     

    - for decrypting:

    // Variables
    HCERTSTORE hStoreHandle = NULL;
    PCCERT_CONTEXT pSubjectCert = NULL;
    wchar_t * SubjectName;
    HCRYPTPROV* phCryptProv = NULL;
    DWORD* pdwKeySpec = NULL;
    BOOL* pfCallerFreeProv = NULL;
    HCRYPTKEY* phPrivateKey;
    DWORD dwEncryptedLen = 0;
    BYTE* pbData = NULL;
    
    // Open the certificate store
    hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, NULL);
    
    // Get a certificate that matches the search criteria
    pSubjectCert = CertFindCertificateInStore(hStoreHandle, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, SubjectName, pSubjectCert);
    
    // Acquire the Private Key
    bResult = CryptAcquireCertificatePrivateKey(pSubjectCert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &phCryptProv, &pdwKeySpec, &pfCallerFreeProv);
    
    // Get Private Key
    bResult = CryptGetUserKey(phCryptProv, AT_KEYEXCHANGE, &phPrivateKey);
     
    // Decrypt data
    bResult = CryptDecrypt(phPrivateKey, 0, TRUE, 0, &pbData, &dwEncryptedLen);
    
    
    //CertFreeCertificateContext
    //CertCloseStore

     

    Other sample code is available from MSDN, not specific to PFX Certificates:

     

    Cheers,

    ~raffaele

Page 1 of 1 (6 items)