Hello Pocket PC
A Pocket PC application is still a Windows application, so it has a message loop, a main window, and window procedures. However, some new requirements do change the design a bit. First, a Pocket PC application must make sure that only one copy of itself is running at any one time. The operating system doesn't ensure this — that is the application's job. Second, instead of using a command bar — as do other Windows CE applications — Pocket PC applications use the menu bar. In many ways, the menu bar acts like an updated command bar, but it does have some peculiarities. A Pocket PC application should not have a Close button, an Exit command, or a Close command in its menus. This is because PDA users don't use applications; they use their PDAs. (The user interface gurus that work on this stuff have decided that users would rather not know when a particular application is running or not.)
Let's move on to some code. Figure 17-2 shows two screen shots of a simple Pocket PC application called HelloPPC. The left image shows the window with the soft input panel, or SIP hidden; the image on the right shows HelloPPC with the SIP showing. Notice how the text centers itself in the visible portion of the workspace. The HelloPPC window has a red outline to highlight its size and position.
Figure 17-2
The HelloPPC application with the SIP both hidden and showing
Listing 17-1 shows the source code for HelloPPC. Fundamentally, what you'll notice about HelloPPC is that it is predominantly a standard Windows CE application. The differences between this code and that shown in Chapter 1 have to do with the difference between the Pocket PC and the Explorer shells. I'll talk about these differences in the sections following the code.
HelloPPC.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h" // Windows stuff
#include "commctrl.h" // Common ctl stuff
#include "aygshell.h" // Pocket PC stuff
#include "HelloPPC.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "HelloPPC.ico" // Program icon
//----------------------------------------------------------------------
// Accelerator keys
//
ID_ACCEL ACCELERATORS DISCARDABLE
BEGIN
"Q", IDM_EXIT, VIRTKEY, CONTROL, NOINVERT
END
HelloPPC.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//
//================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define ID_ACCEL 1 // Accelerator table ID
#define IDM_EXIT 100
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSettingChangeMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoActivateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoHibernateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
// WM_COMMAND message handlers
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
HelloPPC.c
//======================================================================
// HelloPPC - A simple application for the Pocket PC
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include <aygshell.h> // Pocket PC includes
#include "helloppc.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("HelloPPC");
HINSTANCE hInst; // Program instance handle
// Pocket PC globals
HWND hwndMenuBar = NULL; // Handle of menu bar control
BOOL fHibernated = FALSE; // Indicates hibernated state
SHACTIVATEINFO sai; // Used to adjust window for SIP
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_PAINT, DoPaintMain,
WM_COMMAND, DoCommandMain,
WM_SETTINGCHANGE, DoSettingChangeMain,
WM_ACTIVATE, DoActivateMain,
WM_HIBERNATE, DoHibernateMain,
WM_DESTROY, DoDestroyMain,
};
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDM_EXIT, DoMainCommandExit,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
HACCEL hAccel;
// Initialize this instance.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0) return 0x10;
hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE (ID_ACCEL));
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
// Translate accelerator keys.
if (!TranslateAccelerator(hwndMain, hAccel, &msg)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc;
HWND hWnd;
// Save program instance handle in global variable.
hInst = hInstance;
// Allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
// Register application main window class.
wc.style = CS_VREDRAW | CS_HREDRAW; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Create main window.
hWnd = CreateWindow (szAppName, // Window class
TEXT ("Hello"), // Window title
WS_VISIBLE, // Style flags
CW_USEDEFAULT, // x position
CW_USEDEFAULT, // y position
CW_USEDEFAULT, // Initial width
CW_USEDEFAULT, // Initial height
NULL, // Parent
NULL, // Menu, must be null
hInstance, // Application instance
NULL); // Pointer to create
// parameters
if (!IsWindow (hWnd)) return 0; // Fail if not created.
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for main window
//
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
SHMENUBARINFO mbi;
SIPINFO si;
int cx, cy;
// Initialize the shell to activate info structure.
memset (&sai, 0, sizeof (sai));
sai.cbSize = sizeof (sai);
// Create a menu bar.
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // Zero structure
mbi.cbSize = sizeof(SHMENUBARINFO); // Size field
mbi.hwndParent = hWnd; // Parent window
mbi.dwFlags = SHCMBF_EMPTYBAR; // Flags like hide SIP btn
mbi.nToolBarId = 0; // ID of toolbar resource
mbi.hInstRes = 0; // Inst handle of app
mbi.nBmpId = 0; // ID of bitmap resource
mbi.cBmpImages = 0; // Num of images in bitmap
mbi.hwndMB = 0; // Handle of bar returned
// Create menu bar and check for errors.
if (!SHCreateMenuBar(&mbi)) {
MessageBox (hWnd, TEXT ("Couldn\'t create menu bar"),
szAppName, MB_OK);
DestroyWindow (hWnd);
}
hwndMenuBar = mbi.hwndMB; // Save the menu bar handle.
// Query the sip state and size our window appropriately.
memset (&si, 0, sizeof (si));
si.cbSize = sizeof (si);
SHSipInfo(SPI_GETSIPINFO, 0, (PVOID)&si, FALSE);
cx = si.rcVisibleDesktop.right - si.rcVisibleDesktop.left;
cy = si.rcVisibleDesktop.bottom - si.rcVisibleDesktop.top;
// If the sip is not shown, or showing but not docked, the
// desktop rect doesn't include the height of the menu bar.
if (!(si.fdwFlags & SIPF_ON) ||
((si.fdwFlags & SIPF_ON) && !(si.fdwFlags & SIPF_DOCKED))) {
RECT rectMB;
GetWindowRect (hwndMenuBar, &rectMB);
cy -= (rectMB.bottom - rectMB.top);
}
SetWindowPos (hWnd, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER);
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
LRESULT DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code)
return (*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
}
return 0;
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
HPEN hPen, hOld;
RECT rect;
HDC hdc;
hdc = BeginPaint (hWnd, &ps);
GetClientRect (hWnd, &rect);
// Draw a red rectangle around the window.
hPen = CreatePen (PS_SOLID, 1, RGB (255, 0, 0));
hOld = (HPEN)SelectObject (hdc, hPen);
Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom);
SelectObject (hdc, hOld);
DeleteObject (hPen);
// Draw the standard hello text centered in the window.
DrawText (hdc, TEXT ("Hello Pocket PC! "), -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// DoSettingChangeMain - Process WM_SETTINGCHANGE message for window.
//
LRESULT DoSettingChangeMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
// Notify shell of our WM_SETTINGCHANGE message.
SHHandleWMSettingChange(hWnd, wParam, lParam, &sai);
return 0;
}
//----------------------------------------------------------------------
// DoActivateMain - Process WM_ACTIVATE message for window.
//
LRESULT DoActivateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
// If activating, restore any hibernated stuff.
if ((LOWORD (wParam) != WA_INACTIVE) && fHibernated) {
fHibernated = FALSE;
}
// Notify shell of our activate message.
SHHandleWMActivate(hWnd, wParam, lParam, &sai, 0);
return 0;
}
//----------------------------------------------------------------------
// DoHibernateMain - Process WM_HIBERNATE message for window.
//
LRESULT DoHibernateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
// If not the active window, reduce our memory footprint.
if (GetActiveWindow() != hWnd) {
fHibernated = TRUE;
}
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
SendMessage (hWnd, WM_CLOSE, 0, 0);
return 0;
}
Listing 17-1
The HelloPPC application
The HelloPPC application creates a main window and prints Hello Pocket PC in the center of the window. It also draws a red rectangle around the border of its window to clearly show the extent of the window. The program creates a menu bar without a menu but with a button to display the SIP. If you tap the SIP button, you will see the main window resize to avoid being covered by the SIP. If you attempt to start a second copy of HelloPPC, the system will instead switch to the copy currently running. Finally, if you open the SIP and tap Ctrl+Q, the application will quit. Each of these little features takes a little bit of code to conform to the standards of a Pocket PC application. Now let's examine these code fragments and learn how it's done.
This topic is from Programming Microsoft Windows CE, Third Edition, by Douglas Boling, published by Microsoft Press. © 2003 by Douglas McConnaughey Boling. Reprinted here by permission of the author.