Archive for June, 2008

.NET Micro Framework presentation links

Thursday, June 26th, 2008

A big thank you for those who attended my presentation at the Christchurch Embedded .NET User Group last night. Although it was a small turn out it seemed there was a lot of interest in this platform.

As promised here are the slides and some links to various resources I referenced during my presentation:

Sample Applications

Device Solutions (Embedded Fusion) Hardware

Original origins of the .NET Micro Framework

Tools

Some of my blog posts

Additional Resources

What’s wrong with my product name? Why special characters need not apply.

Monday, June 23rd, 2008

Recently while porting another subset of Chris Craft’s 30 Days of .NET applications to C++ I came across another example where the Visual Studio Smart Device CAB deployment functionality has a small trap that could trip up developers new to the tool, and it all depends upon the name you choose for your product…

Day 10 introduced an application named “What Is My IP?“. While creating the CAB file to deploy this application I followed Chris’s lead and set the Product Name to “What Is My IP?”, as shown in the screenshot below.

Screenshot showing Product Name property for CAB file

Everything compiled without error but when I went to install the CAB file I was greeted with the following error message:

Screenshot showing Windows Mobile device failing to install CAB file

Alternatively in some circumstances I could install the application, but uninstallation would fail without removing a single file or shortcut!

The Problem

In a previous blog post I discussed the internal structure of Windows Mobile CAB files. It turns out the problem is to do with the question mark at the end of the product name.

The Visual Studio Smart Device CAB Deployment project also makes use of the Product Name as the default installation directory. Within the *.INF file produced behind the scenes you will find a section similar to the following.

[CEStrings]
AppName="What Is My IP?"
InstallDir=%CE1%\%AppName%

When this file is processed to produce a CAB file, the _setup.xml file within the CAB will contain the following XML snippet:

<parm name="InstallDir"
      value="%CE1%\What Is My IP "
      translation="install" />

Since a question mark is not a valid character for a directory name it has been replaced with a trailing space. It seems this additional space is enough to trip up something within wceload.exe, the executable responsible for installing CAB files on Windows CE based devices.

The Solution

The only solution I’ve been able to come up with is to ensure that your product name does not end in a character that is not valid for a directory name. In this example I renamed the Product Name to “What Is My IP”.

Another alternative is to manually create your CAB file by writing your own *.INF file. By doing this you will be able to default the installation to a directory which isn’t mapped 1 to 1 to your Product Name.

30 Days of Windows Mobile – Day 04: Mileage Tracker

Sunday, June 15th, 2008

Screenshot of Mileage Tracker applicationWith petrol prices reaching record highs across the globe people are starting to pay more attention to fuel efficiency.

Day 4 of Chris Craft’s 30 Days of .NET introduces a Mileage Tracker application to help you check the efficiency of your vehicle and perhaps driving habits!

This blog post covers a number of aspects of porting the original C# source code into C++.

Transparent Labels

Windows CE does not support windows with true transparency. Instead we have to fake it. A common technique is to revert to manually drawing the elements of the user interface which require transparency over top of the background of their parent control.

// Draw the string "Hello World" in the rectangle
// (x1=10, y1=20) - (x2=60, y2=40) using a transparent
// background.
RECT rcBounds = {10, 20, 60, 40};
WCHAR szBuffer[] = L"Hello World";
 
SetBkMode(hDC, TRANSPARENT);
DrawText(hDC, szBuffer, wcslen(szBuffer), &rcCtrl, DT_LEFT);

Getting a little bit more advanced (with better design time support) the .NET Compact Framework solution suggested by Alex Yakhnin is equally implementable in native code and is the technique used in the source code available for download.

Gradient Background

The original C# application used a bitmap for a background. For this conversion I decided to demonstrate an additional API by implementing the background programatically. The solid colour part at the top is easily implemented by calling FillRect within the WM_PAINT message handler. The shaded gradient underneath can be implemented with a call to GradientFill.

Filtering edit controls

For this application it is desirable to restrict input in the edit controls to only decimal numbers (a distance of “abc” miles does not make much sense). Although the edit control has an ES_NUMBER window style which restricts input to numeric digits we can not utilise this as we also want to accept a decimal point.

Another approach is to subclass the edit control. Subclassing a window allows us to override or alter the existing behaviour of the control. The article “Safe Subclassing in Win32” provides a great introduction to this technique.

Similar to a dialog procedure, each window (control) has an associated window procedure. The first step in subclassing is to replace the window procedure with our own custom one.

// Replace the window procedure associated with the
// 'hWndCtrl' window with one called 'MaskedEditProc'
SetWindowLong(hWndCtrl, GWL_WNDPROC, (LONG)MaskedEditProc);

The new window procedure has the opportunity to process or filter window messages before they are passed along to the original window procedure. For example by filtering the WM_CHAR window messages seen by the original window procedure we can make certain key presses disappear.

static LRESULT CALLBACK MaskedEditProc(HWND hWnd,
         UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  WNDPROC pfnOldWndProc =
      (WNDPROC)GetWindowLong(hWnd, GWL_USERDATA);
 
  // Process messages of interest
  switch (uMsg)
  {
    case WM_CHAR:
      if (iswdigit(wParam)	// digits
        || wParam == '-'	// negativeSign
        || wParam == '.'	// decimalSeparator
        || wParam == '\b')	// backspace
        break; // This character is allowed
      else
        return 0; // This character isn't allowed
  }
 
  // Allow the original window procedure to process
  // the message.
  return CallWindowProc(pfnOldWndProc, hWnd, uMsg,
                                 wParam, lParam);
}

A more advanced (and reusable) implementation of this technique is described in James Brown’s Masked Edit Control Input tutorial.

Sample Application

[Download mileagetracker.zip - 61KB]

Within the sample application available for download there are a number of possible tasks left as exercises for the reader.

  1. Modify the MaskedEditProc window procedure to make it reject input such as “1.23.4″. I.e. accept a maximum of one decimal point.
  2. Modify the decimal number parsing to be locale aware. Some locales for instance use a semi colon (;) as a decimal separator.
  3. Handle screen orientation and resolution changes.

EDNUG Meeting 3: Introduction to .NET Micro Framework

Sunday, June 15th, 2008

The third meeting of the Christchurch Embedded .NET User Group is coming up on Wednesday the 25th of June. More details can be found on the EDNUG website.

I will be providing a presentation that covers the basics of .NET Micro Framework based development. This will use an example of a small digital timer with a few unique features.

Andrew Leckie will then provide a “where next for version 3.0″ overview of Microsoft’s plans for the next version of the .NET Micro Framework due to release later this year.

Time permitting there will also be a 3rd topic yet to be finalised.

30 Days of Windows Mobile – Day 03: GPS Compass

Monday, June 9th, 2008

Screenshot of GPS Compass applicationThe third application in Chris Craft’s 30 Days of .NET is a GPS based Compass.

Interacting with GPS Devices

Windows Mobile 5.0 and above provides a unified API called the GPS Intermediate Driver that enables multiple applications to concurrently share a single GPS device. This API is highlevel and abstracts away the need to manually parse NEMA sentences etc.

To create a connection to the GPS Device we can include gpsapi.h and make use of the GPSOpenDevice API.

// Open a connection to the GPS Intermediate Driver
HANDLE hGPS = GPSOpenDevice(NULL, NULL, NULL, 0);

This API is reference counted. Each call to GPSOpenDevice must eventually be matched with a call to GPSCloseDevice. The GPS Hardware is only powered down when the last client closes it’s handle.

// Close our connection to the GPS Intermdiate Driver
GPSCloseHandle(hGPS);

Once a connection has been established we can call GPSGetPosition or GPSGetDeviceState to retrieve location and GPS device status information respectively. For example we can query the current location using a code sample such as the following:

GPS_POSITION pos;
 
// Setup the data structure
memset(&pos, 0, sizeof(pos));
pos.dwVersion = GPS_VERSION_CURRENT;
pos.dwSize = sizeof(pos);
 
// Ask the GPS intermediate driver to
// fill out the structure.
GPSGetPosition(hGPS, &pos, 500000, 0);

One thing to note is that the GPS_POSITION data structure contains a field named dwValidFlags. This field is a bitmask that tells you which fields contain valid data. For example if the dwValidFlags field does not contain the GPS_VALID_LATITUDE flag it means you can not rely on the dblLatitude field (perhaps a location fix has not been made yet).

The GPS Compass application makes use of an optional feature. By passing in two event handles to GPSOpenDevice we do not need to periodically call GPSGetPosition to gather new position updates. Instead we can wait until the GPS Intermediate Driver signals our events and only then call GPSGetPosition, safe in the knowledge that it will definatly return different results than the last call. This helps us be slightly more efficient with respect to battery lifetime due to less CPU load.

String Concatenation

By default we do not have access to a nice string class such as System.String. A string within a C or C++ application is essentially a fixed size character array that uses a NULL character as a terminator.

To copy a string from one string buffer into another we can make use of a function called wcscpy.

// Allocate a string buffer capable of storing
// 31 characters (the 32nd element is used to store a NULL
// terminator) and then copy the string "Hello World" into
// the buffer
WCHAR szMyBuffer[32];
wcscpy(szMyBuffer, L"Hello World");

Likewise to append a string onto the end of another we can make use of the wcscat function as follows:

// Add the string "Goodbye World" onto the end
// of the string already in 'szMyBuffer'
wcscat(szMyBuffer, L" Goodbye World");

While using these functions memory management is much more explicit than it is in C#. For example when you allocate a string buffer you must specify its maximum length and there is no bounds checking to ensure you don’t attempt to store 200 characters in a 100 character buffer. This leads to so called buffer overrun errors if you are not careful.

You may be more familiar with string functions called strcpy (string copy) and strcat (string concatenate). wcscpy and wcscat are essentially identical except that they work on “wide characters” (UTF16 encoded data) instead of ANSI.

Moving a Window

To move or resize a control (or any window for that matter) you can make use of the MoveWindow function.

// Move and resize the window 'hWnd'
MoveWindow(hWnd, // the window to move
  10,  // new x location
  20,  // new y location
  30,  // new width
  40); // new height

One problem with using the MoveWindow function is that you always need to specify both the window’s new location and size. Sometimes it can be useful to use a different function called SetWindowPos. This function accepts a few additional parameters, the most important one being a flags argument that enables you to specify which parameters should be taken notice of. For example to move a window without resizing it you may use a code snippet like the following:

SetWindowPos(hWnd,
  NULL, // hWndInsertAfter
  10,   // new x
  20,   // new y
  0,    // new width
  0,    // new height
  SWP_NOSIZE); // flags

The SWP_NOSIZE flag tells the SetWindowPos function that it should ignore the width and height parameters and leave the window at its current size. If you wanted to resize a window yet keep it’s current location you could use a similar SWP_NOMOVE flag.

Creating a Menu

This is the first sample application that has required the use of a menu. A menu is designed in the resource editor and loaded by the SHCreateMenuBar API. The call to SHCreateMenuBar is typically placed in the handler for the WM_INITDIALOG window message. This is convenient since this window message is received just before the dialog is made visible.

case WM_INITDIALOG:
  // Configure the menu
  SHMENUBARINFO mbi;
  memset(&mbi, 0, sizeof(mbi));
  mbi.cbSize = sizeof(mbi);
  mbi.hWndParent = hWnd; // the dialog's handle
  mbi.nToolBarId = IDR_MENU; // the menu resource id
  mbi.hInstRes = GetModuleHandle(NULL);
  mbi.dwFlags = SHCMBF_HMENU;
 
  // Create the menu
  SHCreateMenuBar(&mbi);
  break;

Once a menu is visible there is a range of menu APIs that enable you to interact with the menu. For example to enable or disable a particular menu item you can make use of the EnableMenuItem API as shown below:

// Disable a menu item with id 'IDC_SOME_ITEM'
EnableMenuItem(hMenu, IDC_SOME_ITEM,
  MF_BYCOMMAND | MF_GRAYED);
 
// Enable a menu item with id 'IDC_SOME_ITEM'
EnableMenuItem(hMenu, IDC_SOME_ITEM,
  MF_BYCOMMAND | MF_ENABLED);

Notice that the EnableMenuItem function is used to both enable and disable menu items, The MF_GRAYED or MF_ENABLED flags passed as part of the last argument determines which action you want to perform.

A handy place to put code that configures the state of menu items is the message handler for the WM_INITMENUPOPUP window message. This message is sent to the menu’s owner just before a menu becomes visible.

Menu items behave very similiar to buttons and send a WM_COMMAND window message when they are selected by the user. This fact can be used to your advantage. If you want a button and menu item to both perform the same task you can assign them the same command ID.

Sample Application

[Download gpscompass.zip - 96KB]

30 Days of Windows Mobile – Day 02: Bluetooth Manager

Sunday, June 8th, 2008

Screenshot of Bluetooth Manager applicationThe second sample application produced by Chris Craft is a small utility to toggle the power of a Bluetooth radio. Leaving the power on to the bluetooth radio for extended periods of time can be a drain on the battery life of your device.

Accessing Bluetooth

This application only works on devices that utilise the Microsoft Bluetooth stack. Unlike other aspects of the operating system there is no standardised API for bluetooth development, leading OEMs to be free to pick the bluetooth stack they think best serves the requirements of their device.

The Microsoft Bluetooth APIs are designed for use with C and C++. To use them you need to include the header file “bthutil.h” and link to a library called “bthutil.lib”. You can determine these requirements by looking at the bottom of the MSDN documentation page for functions such as BthSetMode.

To change the state of the Bluetooth radio we can use the BthSetMode function as shown below:

// Turn the bluetooth radio off
BthSetMode(BTH_POWER_OFF);

BthSetMode accepts a single parameter which is the desired radio state. It can be one of the following three values:

Value Description
BTH_POWER_OFF The bluetooth radio is off
BTH_CONNECTABLE The bluetooth radio is on and other devices can connect to it
BTH_DISCOVERABLE The bluetooth radio is on and other devices can discover (find) it as well as connect to it

We can also query the current state of the bluetooth radio by calling a function called BthGetMode.

DWORD dwMode = BTH_POWER_OFF;
BthGetMode(&dwMode);
 
if (dwMode == BTH_CONNECTABLE)
{
  // Do something here if the radio
  // is currently connectable
}

Using the State and Notification Broker

Rather than periodically polling the BthGetMode function to determine when the radio changes state we can ask the operating system to proactively tell us when the bluetooth mode changes.

The State and Notification Broker (SNAPI) is built on top of an infrastructure that allows you to monitor the value of any registry key and obtain notifications whenever it changes. There are two header files involved.

  • regext.h – Provides functions that allow us to watch for registry value changes
  • snapi.h – Provides definitions of various registry values that store system state information

Our sample application makes use of a function defined in regext.h called RegistryNotifyWindow. This function monitors a registry value and posts a window message to a specified window whenever the value changes. We set this up using a code snippet that looks similar to the following:

HREGNOTIFY hregNotify;
 
// We are interested in any change to the registry value.
NOTIFICATIONCONDITION condition = {
  REG_CT_ANYCHANGE,
  SN_BLUETOOTHSTATEPOWERON_BITMASK, 0
};
 
// We want to listen to the BLUETOOTHSTATEPOWERON
// registry value and have a WM_BLUETOOTH_STATE_CHANGE
// window message posted to 'hdlg' whenever it changes.
RegistryNotifyWindow(SN_BLUETOOTHSTATEPOWERON_ROOT,
  SN_BLUETOOTHSTATEPOWERON_PATH,
  SN_BLUETOOTHSTATEPOWERON_VALUE,
  hDlg,
  WM_BLUETOOTH_STATE_CHANGED,
  0,
  &condition,
  &hregNotify);

By modifying the contents of the NOTIFICATIONCONDITION structure we can also produce more complex scenarios such as only being notified when the specified SNAPI property increases above a certain value.

Using a button control

This application uses two buttons. When the user presses a button the dialog is sent a WM_COMMAND window message to allow it to respond to the event. One of the parameters of the WM_COMMAND window message allows us to determine which button has been pressed. For example:

case WM_COMMAND:
   // Determine which button was pressed
   switch (LOWORD(wParam))
   {
     case IDC_BUTTON_POWERON:
       // turn the bluetooth radio on
       break;
 
     case IDC_BUTTON_POWEROFF:
       // turn the bluetooth radio off
       break;
   }
   break;

Using an edit control

To replace the entire contents of an edit (textbox) control you can use the SetWindowText function just like you would for a static control.

HWND hWndCtrl = GetDlgItem(hDlg, IDC_EDIT_LOG);
SetWindowText(hWndCtrl, L"This is the new content");

To append text to the end of the existing contents of an edit control is a two part process. First we can move the selection (cursor) to the end of the textbox via the EM_SETSEL window message and then we can replace that selection with the text we desire via the EM_REPLACESEL window message, as shown below:

// Move the cursor to the end of the edit control
SendMessage(hWndCtrl, EM_SETSEL, -1, -1);
 
// Replace that selection with the text we want
// to append to the end of the edit control
SendMessage(hWndCtrl, EM_REPLACESEL, FALSE,
  (LPARAM)L"Text to add");

Displaying an image

The static control is commonly used like a Label control would be within a .NET Compact Framework application however it can also be used like a Picturebox control.

To change the image displayed by the static control we can use the STM_SETIMAGE window message as demonstrated by the following code sample.

// Get the status bitmap control
HWND hWndCtrl = GetDlgItem(hDlg, IDC_STATUS_BITMAP);
 
// Load the desired image from a bitmap resource
HBITMAP hBitmap = LoadBitmap(GetModuleHandle(NULL),
  MAKEINTRESOURCE(IDB_POWER_OFF));
 
// Then send the STM_SETIMAGE window message
// to the static control
SendMessage(hWndCtrl, STM_SETIMAGE, IMAGE_BITMAP,
  (LPARAM)hBitmap);

The other thing we need to do for this code sample is to make the image click-able. By default a static control does not respond to stylus taps. Within the dialog resource editor you can set the Notify property for a static control to True. When this is done a WM_COMMAND message will be generated whenever the static control is clicked, just like a button would.

Sample Application

[Download bluetoothmanager.zip - 99KB]

The Bluetooth State and Notification broker properties are new to Windows Mobile 6.0 so ideally we should set the minimum OS version property in the CAB file to disallow installation on previous versions of the operating system. This is left as a learning experience for the reader (one hint is that the OS version you want to specify isn’t 6.0…).

Another reader exercise may be to download the Broadcom Bluetooth SDK and modify the sample application to work on devices that use this alternative Bluetooth stack.

30 Days of Windows Mobile – Day 01: Minutes to Midnight Countdown

Sunday, June 8th, 2008

Screenshot of Minutes to Midnight applicationA great resource is starting to develop on Chris Craft’s blog. Chris has started a series of blog posts designed to produce 30 .NET applications for beginner Windows Mobile developers to explore and learn from. Chris has written his applications in C# targeting the .NET Compact Framework and Lou Vega has offered to produce VB.NET conversions.

Neil Roodyn recently commented on the general lack of native C and COM based knowledge shown by new developers in the .NET community. Personally I would find it difficult to understand things like the need for the Control.Invoke method without having an understanding of the APIs and mechanisms the System.Windows.Forms framework is attempting to abstract.

For these reasons I have decided it would be neat to convert Chris’s examples into native C++ as a way to compare the advantages and disadvantages of native code. It should also help increase the number of Windows Mobile code samples available in C or C++, something João Paulo Figueira has also noted as becoming increasingly difficult to find.

Chris’s first application is a countdown timer that counts down the amount of time left before midnight. This blog post covers some details of how I re-implemented it in C++.

Designing the dialog

The easiest way to create a small native application is to create a dialog based GUI. This allows us to graphically layout the dialog and write a little less code.

Like System.Windows.Forms based applications there is a clear distinction between form layout and code. The layout of one or more dialogs lives within a Resource File (*.rc) while the code is placed within *.cpp files. Each dialog resource is assigned an ID such as IDD_MYDIALOG that allows the two parts to be matched up.

To display a dialog we can make use of the DialogBox API

DialogBox(hInstance, (LPCSTR)IDD_MYDIALOG, NULL, MyDialogProc);

This displays the dialog stored within the IDD_MYDIALOG resource and waits until it is closed. The last parameter is the name of a dialog procedure. This function written by you will be called to process window messages sent to the dialog. These window messages are similar in nature to the various events and virtual methods available on classes such as System.Windows.Forms.Control.

The basic structure of a Dialog Procedure would look something like the following:

INT_PTR CALLBACK MyDialogProc(HWND hDlg,
  UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_INITDIALOG:
      // this is similar to the Load event so we
      // can perform dialog initialisation code here
      break;
 
    case WM_CLOSE:
      // this is similar to the Close event so we
      // can perform dialog shutdown logic here
      break;
 
    ...
  }
}

Using labels

The equivalent of a System.Windows.Forms.Label control for native code is the static control.

In order to interact with a control placed on a dialog we must obtain a window handle for it. Within a Win32 (User32) GUI application controls are in fact a special type of window. What makes them a control is the fact that they are a child of another window.

To get the window handle of a control we can use the GetDlgItem function, passing in the window handle for the dialog and the ID we assigned the control in the dialog resource editor.

// Get the window handle for the control
// with an ID of IDC_MESSAGE
HWND hwndCtrl = GetDlgItem(hWnd, IDC_MESSAGE);

Having obtained a window handle for the control we can now send it window messages. This serves a similar purpose to the various properties and methods seen on classes such as System.Windows.Forms.Label. Sometimes there are even helper functions available to make this process slightly easier. For example we can change the text displayed on a static control by calling the SetWindowText function

// Change the label to display "Hello World"
SetwindowText(hwndCtrl, L"Hello World");

Using progress bars

Progress bars are interacted with in a similar way to static controls except the set of window messages they understand is different.

For example to set the minimum and maximum values of a progress bar we can send it a PBM_SETRANGE or PBM_SETRANGE32 message as demonstrated below.

// Set the progress bar referenced by ‘hWndCtrl’
// to have the range 25 to 75
SendMessage(hWndCtrl, PBM_SETRANGE, 0, MAKELPARAM(25, 75));

Looking at the documentation for PBM_SETRANGE and PBM_SETRANGE32 can you determine why there are two messages that can set the progress bar’s range? This is an example of an implementation detail that the .NET Compact Framework abstracts away for you.

To set the current value of a progress bar we can send it a PBM_SETPOS window message.

// Set the progress bar referenced by ‘hWndCtrl’
// to the value 45
SendMessage(hwndCtrl, PBM_SETPOS, 45, 0);

Using a timer

Instead of dragging and dropping a timer component onto a form a C++ developer must create the timer via code.

To start a timer we can use the SetTimer function:

// Create a timer with ID 1234 and
// an interval of 1000 milliseconds (1 second)
SetTimer(hWnd, 1234, 1000, NULL);

When we no longer need the timer we can call the matching KillTimer function to stop it.

// Stop the timer with ID 1234
KillTimer(hWnd, 1234);

You will notice that timers are associated with a window (the first parameter is a window handle). Every time the timer interval occurs the associated window will receive a WM_TIMER message which is similar in nature to the Timer.Tick event.

case WM_TIMER:
  if (wParam == 1234)
  {
      // timer 1234's interval has occurred so
      // we can do something here...
  }
  break;

The purpose of giving a timer an ID is to allow a single window to have multiple timers associated with it. The WM_TIMER window message provides the ID of the timer which caused the message to be sent and hence allows us to respond differently to different timers.

Colouring the background

To seperate the different sections of the user interface Chris used a shaded background. The easiest way to do this is to handle the WM_PAINT window message. This is sent to a window when it is time to paint its contents.

Once a little house keeping is taken care of we can colour the three regions by painting three rectangles. To paint a rectangle we can make use of the FillRect function as shown below.

// Define a rectangle at x=40, y=10 with size 40x10
RECT rcBounds;
rcBounds.top = 10;
rcBounds.bottom = 20;
rcBounds.left = 40;
rcBounds.right = 80;
 
// Create a red brush and fill the
// area of the rectangle
HBRUSH hbrRed = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &rcBounds, hbrRed);
DeleteObject(hbrRed);

Collectively the operating system drawing APIs are called the Graphics Device Interface (GDI). You will notice that GDI functions such as CreateSolidBrush have a strong resemblance to elements within the System.Drawing namespace such as the System.Drawing.SolidBrush class.

Time calculations

There is no equivalent to the System.DateTime structure, so time calculations have to be performed via a more manual process.

To get the current date and time we can call a function called GetLocalTime which returns a SYSTEMTIME data structure. This structure does not lend itself to calculations so we can then use the SystemTimeToFileTime function to convert it into a FILETIME structure which is essentially a 64bit integer representing time as the number of 100-nanosecond intervals since January 1, 1601.

This allows us to use division and other simple math operators to perform time-span calculations. For example given the current time we can perform the following calculations to determine how far we are away from midnight:

__int64 amount_of_today_past = current_time % ONE_DAY;
__int64 amount_of_today_left = ONE_DAY - amount_of_today_past;

Given this value we can then easily convert it into other units such as hours or minutes as desired. For example the following expression will return the same value as the TimeSpan.Minutes property.

__int64 minutes = (amount_of_today_left / ONE_MINUTE) % 60

Sample Application

[Download minutes2midnight.zip – 29 KB]

The C++ source code and a CAB file for this sample application can be downloaded. If you have any questions about the source code or would like to discuss native Windows Mobile development further please leave a comment on this blog entry.