Archive for the ‘Tutorials’ Category

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.