Sunday, February 19, 2006

SDF 2.0 continues to have functionality for working with the time and time zones for devices.  The differences from SDF 1.x are a namespace change, a classname change (don't believe the beta docs or the beta, these have changed since that drop), and under the hood a lot more internal checks are going on to head off errors and to provide correct device support.  Basically this one has been pretty heavily tested across a broad range of devices.

Some quick highlights:

Getting and Displaying a List of Time Zones

using OpenNETCF.WindowsCE;
...
// get and display all available zones
TimeZoneCollection tzc = new TimeZoneCollection();
tzc.Initialize();

foreach (TimeZoneInformation tzi in tzc)
{
    lstZones.Items.Add(tzi);
}

Displaying the Currently Set Time Zone

using OpenNETCF.WindowsCE;
...
// get and display the currrent zone

TimeZoneInformation currentTz = new TimeZoneInformation();
DateTimeHelper.GetTimeZoneInformation(ref currentTz);
lblCurrentZone.Text = currentTz.StandardName;

Setting the Current Time Zone (from the Listing above)

using OpenNETCF.WindowsCE;
...
if
(lstZones.SelectedItem != null)
{
    TimeZoneInformation tzi = (TimeZoneInformation)lstZones.SelectedItem;
    DateTimeHelper.SetTimeZoneInformation(tzi);

    // this verifies that the time zone did indeed get changed
    TimeZoneInformation tz = new TimeZoneInformation(
        (byte[])Registry.LocalMachine.OpenSubKey("Time").GetValue(
            "TimeZoneInformation"));
    MessageBox.Show("Current Timezone in Registry is:\r\n" 
        + tz.StandardName, "Verified");
}

Displaying the Current Time (without DateTime.Now)

using OpenNETCF.WindowsCE;
...
DateTime dt = DateTimeHelper.SystemTime;

txtHour.Text = dt.Hour.ToString();
txtMinute.Text = string.Format("{0:00}", dt.Minute);
txtSecond.Text = string.Format("{0:00}", dt.Second);

Setting The Current System Time (Local Time is Analogous)

using OpenNETCF.WindowsCE;
...
// get the current time so we can copy the date part

DateTime dt = DateTimeHelper.SystemTime;

DateTimeHelper.SystemTime = new DateTime(dt.Year, dt.Month, dt.Day,
    int.Parse(txtHour.Text), int.Parse(txtMinute.Text), int.Parse(txtSecond.Text));

 

2/19/2006 6:17:03 PM (Eastern Standard Time, UTC-05:00)  #    Comments [4]  | 
 Saturday, February 18, 2006

I'm trying to debug an error in some managed code related to timezones when a P/Invoke is throwing a native access violation exception.  To make sure it's not something with the platform itself I decided to do the same in native code first to ensure that it's not some platform limitation or bug.  Surprisingly I didn't readily find any sample code for doing it, so here's my contribution to the community at large.  It uses dynamic loading (and it doesn't make sure that the function loads succeed, so you might want to add error checking if you actually intend to use this):

#include <windows.h>

typedef void    (*INITCITYDB)(void);
typedef void    (*UNINITCITYDB)(void);
typedef void    (*LOADTZDATA)(void);
typedef void    (*FREETZDATA)(void);
typedef int        (*GETNUMZONES)(void);
typedef void *    (*GETTZDATABYOFFSET)(int, int*);
typedef void *    (*GETTZDATA)(int);

struct TZData
{
    TCHAR *Name;
    TCHAR *ShortName;
    TCHAR *DSTName;
    int GMTOffset;
    int DSTOffset;
};

int _tmain(int argc, _TCHAR* argv[])
{
    TZData *pTZ = NULL;
    int index;

    // load the library
    HINSTANCE hLib = LoadLibrary(_T("CityDB.dll"));

    // load the CityDB functions
    INITCITYDB InitCityDB = (INITCITYDB)GetProcAddress(
            hLib, _T("InitCityDb"));
    UNINITCITYDB UninitCityDB = (UNINITCITYDB)GetProcAddress(
            hLib, _T("UninitCityDb"));
    LOADTZDATA ClockLoadAllTimeZoneData = (LOADTZDATA)GetProcAddress(
            hLib, _T("ClockLoadAllTimeZoneData"));
    FREETZDATA ClockFreeAllTimeZoneData = (FREETZDATA)GetProcAddress(
            hLib, _T("ClockFreeAllTimeZoneData"));
    GETNUMZONES ClockGetNumTimezones = (GETNUMZONES)GetProcAddress(
            hLib, _T("ClockGetNumTimezones"));
    GETTZDATABYOFFSET ClockGetTimeZoneDataByOffset = 
            
(GETTZDATABYOFFSET)GetProcAddress(hLib, _T("ClockGetTimeZoneDataByOffset"));
    GETTZDATA ClockGetTimeZoneData = (GETTZDATA)GetProcAddress(
            hLib, _T("ClockGetTimeZoneData"));

    // Init the library
    InitCityDB();

    // load the TZ data
    ClockLoadAllTimeZoneData();

    // find out how many zones are defined
    int zoneCount = ClockGetNumTimezones();

    // interate through them all
    for(int zone = 0 ; zone < zoneCount ; zone++)
    {
        // these are pointers to a timezone data struct
        pTZ = (TZData*)ClockGetTimeZoneDataByOffset(zone, &index);
    }

    // unload the TZ data
    ClockFreeAllTimeZoneData();

    // uninit the library
    UninitCityDB();

    return 0;
}

2/18/2006 1:45:07 PM (Eastern Standard Time, UTC-05:00)  #    Comments [3]  | 
 Sunday, February 12, 2006

CE supports a Multimedia timer, though it's not in CF 1.0 or 2.0.  We've rectified that in SDF 2.0 (though your platform must support the Multimedia timer to use it - meaning WM and PPC are out).

So what do high-performance timers buy you?  Well if you look at how a regular timer works, they run at a really low priority and are horrible if you want anything that resembles deterministic behavior.  If you set the interval to say 1000ms, it's guaranteed to not fire in less than 1000ms, but there's actually no upper bound at all.  Jitter of 50ms (5%) would not be exceptional and in fact I've seen substantially worse on systems with a high load.

Our Timer2 class (name is still not finalized, so don't finalize on it) is based on the desktop's Timer class and provides things like a one-shot capability (timer fires once and never again without you having to disable it) and if you derive from it you can have it run a callback instead of raising an event.

Here's a quick example of usage:

void StartMyOneshotTimer
{
  // create a timer
  Timer2 oneShot = new Timer2();

  // make it a one-shot timer
  oneShot.AutoReset = false;

  // fire 3 seconds from enabling
  oneShot.Interval = 3000;

  // allow 10ms latitude for when it fires
  // so it will fire between now + 2995 and now + 3005
  oneShot.Resolution = 10;

  oneShot.Elapsed += new ElapsedEventHandler(oneShot_Elapsed);

  oneShot.Start();
}

void oneShot_Elapsed(object sender, ElapsedEventArgs e)
{
  // do something here
}

2/12/2006 2:39:02 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Saturday, February 04, 2006

You might have noticed that SDF 2.0 no longer has serial or GPS classes.  This is intentional.  CF 2.0 now has serial classes and the Windows Mobile AKU includes GPS support.  We realize that there are some of you out there that are using GPSes on non-WM 5.0 devices and that you'd like to be able to use our stuff under Studio 2005.  For those people, we've spun the Serial and GPS classes into a stand-alone assembly: OpenNETCF.IO.Serial.  There are no plans for an installer for this assembly, and unless we get strong feedback it probably won't make it into the SDF either.

2/4/2006 2:41:38 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, February 02, 2006

If you're a Windows CE device developer and you've got a device with a persistent registry, you're probably already aware that the CF Registry class doesn't help much for saving the registry, restoring branches from a file or creating volatile keys.  Once again the SDF is here to help with CreateVolatileSubkey, RestoreHiveBasedKey, RestoreRamBasedRegistry, SaveHiveBasedKey and SaveRamBasedRegistry.

One note - the doc says they're in the Registry2 class - that's already been changed to RegistryHelper.

 

2/2/2006 11:31:28 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  |