|
|
 |

 |
|
Programming Microsoft® Windows® CE, Second Edition
|
|
 |
Author |
 |
Douglas Boling
|
 |
|
Pages |
1040
|
|
Disk |
2 Companion CD(s)
|
|
Level |
All Levels
|
|
Published |
06/13/2001
|
|
ISBN |
9780735614437
|
|
ISBN-10 |
0-7356-1443-1
|
|
Price(USD) |
$59.99
To see this book's discounted price, select a reseller below.
|
|
|
|
|
 |
|
|
Chapter 15: Extending the Pocket PC continued
The NumPanel Example Input Method (continued)
Figure 15-6 The NumPanel source code (continued)
//---------------------------------------------------------------------- // LockServer - Called to tell the DLL not to unload even if use count is 0 STDMETHODIMP MyClassFactory::LockServer (BOOL fLock) { if (fLock) InterlockedIncrement (&g_DllCnt); else InterlockedDecrement (&g_DllCnt); return NOERROR; } //---------------------------------------------------------------------- // CreateInstance - Called to have class factory object create other // objects STDMETHODIMP MyClassFactory::CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppv) { MyIInputMethod *pMyIM; HRESULT hr; if (pUnkOuter) return (CLASS_E_NOAGGREGATION); if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IInputMethod2)) { // Create Input method object. pMyIM = new MyIInputMethod(); if (!pMyIM) return E_OUTOFMEMORY; // See if object exports the proper interface. hr = pMyIM->QueryInterface (riid, ppv); // This will cause an obj delete unless interface found. pMyIM->Release (); return hr; } return E_NOINTERFACE; } //********************************************************************** // MyIInputMethod Object implementation //---------------------------------------------------------------------- // Object constructor MyIInputMethod::MyIInputMethod () { m_lRef = 1; // Set ref count to 1 on create. g_DllCnt++; return; }
//---------------------------------------------------------------------- // Object destructor MyIInputMethod::~MyIInputMethod () { g_DllCnt--; return; } //---------------------------------------------------------------------- // QueryInterface - Called to see what interfaces this object supports STDMETHODIMP MyIInputMethod::QueryInterface (THIS_ REFIID riid, LPVOID *ppv) { // If caller wants our IUnknown or IID_IInputMethod2 object, // return a pointer to the object. if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IInputMethod) || IsEqualIID (riid, IID_IInputMethod2)){ // Return ptr to object. *ppv = (IInputMethod *)this; AddRef(); // Increment ref to prevent delete on return. return NOERROR; } *ppv = NULL; return (E_NOINTERFACE); } //---------------------------------------------------------------------- // AddRef - Increment object ref count. STDMETHODIMP_(ULONG) MyIInputMethod::AddRef (THIS) { ULONG cnt; cnt = (ULONG)InterlockedIncrement (&m_lRef); return cnt; } //---------------------------------------------------------------------- // Release - Decrement object ref count. STDMETHODIMP_(ULONG) MyIInputMethod::Release (THIS) { ULONG cnt; cnt = (ULONG)InterlockedDecrement (&m_lRef); if (cnt == 0) { delete this; return 0; } return cnt; }
//---------------------------------------------------------------------- // Select - The IM has just been loaded into memory. // HRESULT STDMETHODCALLTYPE MyIInputMethod::Select (HWND hwndSip) { HBITMAP hBmp, hbmpMask; m_hwndParent = hwndSip; // Create image list for narrow (16x16) image. m_himlNarrow = ImageList_Create (16, 16, ILC_COLOR | ILC_MASK, 1, 0); hBmp = LoadBitmap (hInst, TEXT ("NarrowBmp")); hbmpMask = LoadBitmap (hInst, TEXT ("NarrowMask")); ImageList_Add (m_himlNarrow, hBmp, hbmpMask); DeleteObject (hBmp); DeleteObject (hbmpMask); // Create image list for wide (32x16) image. m_himlWide = ImageList_Create (32, 16, ILC_COLOR | ILC_MASK, 1, 0); hBmp = LoadBitmap (hInst, TEXT ("WideBmp")); hbmpMask = LoadBitmap (hInst, TEXT ("WideMask")); ImageList_Add (m_himlWide, hBmp, hbmpMask); DeleteObject (hBmp); DeleteObject (hbmpMask); // Create SIP window. m_hwndMyWnd = CreateIMWindow (hwndSip); if (!IsWindow (m_hwndMyWnd)) return E_FAIL; return S_OK; } //---------------------------------------------------------------------- // Deselect - The IM is about to be unloaded. // HRESULT STDMETHODCALLTYPE MyIInputMethod::Deselect (void) { DestroyIMWindow (m_hwndMyWnd); ImageList_Destroy (m_himlNarrow); ImageList_Destroy (m_himlWide); return S_OK; } //---------------------------------------------------------------------- // Showing - The IM is about to be made visible. // HRESULT STDMETHODCALLTYPE MyIInputMethod::Showing (void) { return S_OK; }
//---------------------------------------------------------------------- // Hiding - The IM is about to be hidden. // HRESULT STDMETHODCALLTYPE MyIInputMethod::Hiding (void) { return S_OK; } //---------------------------------------------------------------------- // GetInfo - The SIP wants info from the IM. // HRESULT STDMETHODCALLTYPE MyIInputMethod::GetInfo ( IMINFO __RPC_FAR *pimi) { pimi->cbSize = sizeof (IMINFO); pimi->hImageNarrow = m_himlNarrow; pimi->hImageWide = m_himlWide; pimi->iNarrow = 0; pimi->iWide = 0; pimi->fdwFlags = SIPF_DOCKED; pimi->rcSipRect.left = 0; pimi->rcSipRect.top = 0; pimi->rcSipRect.right = FLOATWIDTH; pimi->rcSipRect.bottom = FLOATHEIGHT; SendMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_GETINFO, (LPARAM) pimi); return S_OK; } //---------------------------------------------------------------------- // ReceiveSipInfo - The SIP is passing info to the IM. // HRESULT STDMETHODCALLTYPE MyIInputMethod::ReceiveSipInfo ( SIPINFO __RPC_FAR *psi) { // Pass the sip info data to the window. SendMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_SETINFO, (LPARAM) psi); return S_OK; } //---------------------------------------------------------------------- // RegisterCallback - The SIP is providing the IM with the pointer to // the IIMCallback interface. // HRESULT STDMETHODCALLTYPE MyIInputMethod::RegisterCallback ( IIMCallback __RPC_FAR *lpIMCallback) { m_pIMCallback = lpIMCallback; PostMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_REGCALLBACK, (LPARAM)m_pIMCallback); return S_OK; }
//---------------------------------------------------------------------- // GetImData - An application is passing IM-specific data to the IM. // HRESULT STDMETHODCALLTYPE MyIInputMethod::GetImData (DWORD dwSize, LPVOID pvImData) { return E_FAIL; } //---------------------------------------------------------------------- // SetImData - An application is querying IM-specific data from the IM. // HRESULT STDMETHODCALLTYPE MyIInputMethod::SetImData (DWORD dwSize, LPVOID pvImData) { return S_OK; } //---------------------------------------------------------------------- // UserOptionsDlg - The SIP Control Panel applet is asking for a // configuration dialog box to be displayed. // HRESULT STDMETHODCALLTYPE MyIInputMethod::UserOptionsDlg ( HWND hwndParent) { MessageBox (hwndParent, TEXT ("UserOptionsDlg called."), TEXT ("NumPanel"), MB_OK); return S_OK; } //---------------------------------------------------------------------- // SetIMMActiveContext - Provides information about the IME // HRESULT STDMETHODCALLTYPE MyIInputMethod::SetIMMActiveContext(HWND hwnd, BOOL bOpen, DWORD dwConversion, DWORD dwSentence, DWORD hkl) { return S_OK; } //---------------------------------------------------------------------- // RegisterCallback2 - The SIP is providing the IM with the pointer to // the IIMCallback interface. // HRESULT STDMETHODCALLTYPE MyIInputMethod::RegisterCallback2 ( IIMCallback2 __RPC_FAR *lpIMCallback) { m_pIMCallback = lpIMCallback; PostMessage (m_hwndMyWnd, MYMSG_METHCALL, MSGCODE_REGCALLBACK2, (LPARAM)m_pIMCallback); return S_OK;
}
NumPanel.c
//====================================================================== // NumPanel - An IM window // // Written for the book Programming Windows CE // Copyright (C) 2001 Douglas Boling //====================================================================== #include <windows.h> // For all that Windows stuff #define COBJMACROS #include <aygshell.h> // Pocket PC shell includes #include <sip.h> // SIP includes #include <keybd.h> // Keyboard flag includes #include "NumPanel.h" // Includes for this window extern HINSTANCE hInst; // // Local variables for number panel // TCHAR g_tcBtnChar[] = { TEXT(‘1’), TEXT(‘2’), TEXT(‘3’), TEXT(‘-’), TEXT(‘*’), TEXT(‘4’), TEXT(‘5’), TEXT(‘6’), TEXT(‘+’), TEXT(‘/’), TEXT(‘7’), TEXT(‘8’), TEXT(‘9’), TEXT(‘0’), TEXT(‘=‘), }; UINT g_BtnVChars[] = { ‘1’, ‘2’, ‘3’, VK_HYPHEN, VK_MULTIPLY, ‘4’, ‘5’, ‘6’, VK_ADD, VK_SLASH, ‘7’, ‘8’, ‘9’, ‘0’, VK_EQUAL, }; // Message dispatch table for SipWindowProc const struct decodeUINT SipMessages[] = { WM_CREATE, DoCreateSip, WM_PAINT, DoPaintSip, MYMSG_METHCALL, DoSetSipInfo, WM_LBUTTONDOWN, DoMouseSip, WM_MOUSEMOVE, DoMouseSip, WM_LBUTTONUP, DoMouseSip, WM_LBUTTONDBLCLK, DoMouseSip, WM_DESTROY, DoDestroySip, }; //---------------------------------------------------------------------- // CreateIMWindow - Create the input method window. // HWND CreateIMWindow (HWND hwndParent) { WNDCLASS wc; RECT rect; HWND hwnd;
// Register sip window class. memset (&wc, 0, sizeof (wc)); wc.style = CS_DBLCLKS; wc.lpfnWndProc = NPWndProc; // Callback function wc.hInstance = hInst; // Owner handle wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wc.lpszClassName = MYSIPCLS; // Window class name if (RegisterClass (&wc) == 0) return 0; // Create SIP window. GetClientRect (hwndParent, &rect); hwnd = CreateWindowEx (0, MYSIPCLS, TEXT(""), WS_VISIBLE | WS_CHILD | WS_BORDER, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hwndParent, (HMENU)100, hInst, 0); if (!IsWindow (hwnd)) return 0; return hwnd; } //---------------------------------------------------------------------- // DestroyIMWindow - Destroy the input method window. // int DestroyIMWindow (HWND hwnd) { // Clean up since we’re about to be unloaded. DestroyWindow (hwnd); UnregisterClass (MYSIPCLS, hInst); return 0; } //====================================================================== // NPWndProc - Window procedure for SIP // LRESULT CALLBACK NPWndProc (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { INT i; // Call routine to handle control message. for (i = 0; i < dim(SipMessages); i++) { if (wMsg == SipMessages[i].Code) return (*SipMessages[i].Fxn)(hWnd, wMsg, wParam, lParam); } return DefWindowProc (hWnd, wMsg, wParam, lParam); } //---------------------------------------------------------------------- // DoCreateSip - Process WM_CREATE message for window. // LRESULT CALLBACK DoCreateSip (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { LPSIPWNDSTRUCT pWndData;
// Allocate a data structure for the sip keyboard window. pWndData = LocalAlloc (LPTR, sizeof (SIPWNDSTRUCT)); if (!pWndData) { DestroyWindow (hWnd); return 0; } memset (pWndData, 0, sizeof (SIPWNDSTRUCT)); GetWindowRect (GetParent (hWnd), &pWndData->rectDocked); pWndData->rectLast.left = -1; SetWindowLong (hWnd, GWL_USERDATA, (INT)pWndData); return 0; } //---------------------------------------------------------------------- // DoSetSipInfo - Process set information user message for window. // LRESULT CALLBACK DoSetSipInfo (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { LPSIPWNDSTRUCT pWndData; RECT rect; pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA); switch (wParam) { // Called when RegisterCallback method called case MSGCODE_REGCALLBACK: pWndData->pIMCallback = (IIMCallback *)lParam; break; // Called when GetInfo method called case MSGCODE_GETINFO: pWndData->imi = *(IMINFO *)lParam; break; // Called when ReceiveSipInfo method called case MSGCODE_SETINFO: GetClientRect (GetParent(hWnd), &rect); MoveWindow (hWnd, 0, 0, rect.right - rect.left, rect.bottom - rect.top, TRUE); break; // Called when RegisterCallback2 method called case MSGCODE_REGCALLBACK2: pWndData->pIMCallback2 = (IIMCallback2 *)lParam; break; } return 0; } //---------------------------------------------------------------------- // DoPaintSip - Process WM_PAINT message for window. //
LRESULT CALLBACK DoPaintSip (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; HBRUSH hOld; PAINTSTRUCT ps; RECT rect, rectBtn; INT i, j, k, x, y, cx, cy, cxBtn, cyBtn; LPSIPWNDSTRUCT pWndData; pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA); hdc = BeginPaint (hWnd, &ps); GetClientRect (hWnd, &rect); cx = (rect.right - rect.left - 3 - GRIPWIDTH) / CXBTNS; cy = (rect.bottom - rect.top - 3) / CYBTNS; cxBtn = cx - 3; cyBtn = cy - 3; // Select a brush for the gripper. hOld = SelectObject (hdc, GetStockObject (GRAY_BRUSH)); Rectangle (hdc, rect.left, rect.top, rect.left + GRIPWIDTH, rect.bottom); SelectObject (hdc, hOld); k = 0; y = 3; for (i = 0; i < CYBTNS; i++) { x = 3 + GRIPWIDTH; for (j = 0; j < CXBTNS; j++) { SetRect (&rectBtn, x, y, x + cxBtn, y + cyBtn); DrawButton (hdc, &rectBtn, &g_tcBtnChar[k++], pWndData->dwBtnDnFlags & (1 << k)); x += cx; } y += cy; } EndPaint (hWnd, &ps); return 0; } //---------------------------------------------------------------------- // ComputeFloatRect - Compute the location and size of the drag rect. // int ComputeFloatRect (HWND hwnd, LPSIPWNDSTRUCT pWndData, POINT pt, RECT *prectOut) { pt.x -= pWndData->ptMovBasis.x; pt.y -= pWndData->ptMovBasis.y;
prectOut->right = FLOATWIDTH; prectOut->bottom = FLOATHEIGHT; prectOut->left = pt.x; prectOut->top = pt.y; prectOut->right += pt.x; prectOut->bottom += pt.y; return 0; } //---------------------------------------------------------------------- // DrawFloatRect - Draw a drag rectangle by XORing the desktop. // int DrawFloatRect (HWND hWnd, RECT rect) { HDC hdc; HBRUSH hbrOld; HPEN hpenOld; int nOldMode; // Get the DC. Set ROP, brush, and pen. hdc = GetDC (NULL); nOldMode = SetROP2 (hdc, R2_NOT); hbrOld = SelectObject (hdc, GetStockObject (NULL_BRUSH)); hpenOld = SelectObject (hdc, GetStockObject (BLACK_PEN)); Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom); SelectObject (hdc, hbrOld); SelectObject (hdc, hpenOld); SetROP2 (hdc, nOldMode); ReleaseDC (NULL, hdc); return 0; } //---------------------------------------------------------------------- // HandleGripper - Handles mouse messages over gripper bar // LRESULT HandleGripper (HWND hWnd, LPSIPWNDSTRUCT pWndData, UINT wMsg, LPARAM lParam) { POINT pt; RECT rectFloat; pt.x = (short)LOWORD(lParam); pt.y = (short)HIWORD(lParam); switch (wMsg) { case WM_LBUTTONDOWN: if (pt.x > GRIPWIDTH+3) return 0; SetCapture (hWnd); pWndData->fMoving = TRUE;
pWndData->ptMovBasis = pt; ClientToScreen (hWnd, &pt); pWndData->ptMovStart = pt; ShowWindow (GetParent(hWnd), SW_HIDE); break; case WM_MOUSEMOVE: if (!pWndData->fMoving) return 0; ClientToScreen (hWnd, &pt); ComputeFloatRect (hWnd, pWndData, pt, &rectFloat); // Erase old drag rectangle. if (pWndData->rectLast.left != -1) DrawFloatRect (hWnd, pWndData->rectLast); // Draw new drag rectangle. DrawFloatRect (hWnd, rectFloat); pWndData->rectLast = rectFloat; break; case WM_LBUTTONUP: if (!pWndData->fMoving) return 0; // Free up dragging stuff. ReleaseCapture(); pWndData->fMoving = FALSE; ClientToScreen (hWnd, &pt); // Erase last drag rectangle. ComputeFloatRect (hWnd, pWndData, pt, &rectFloat); if (pWndData->rectLast.left != -1) DrawFloatRect (hWnd, pWndData->rectLast); pWndData->rectLast.left = -1; ShowWindow (GetParent(hWnd), SW_SHOW); // Don’t move SIP if really small move. if ((abs (pWndData->ptMovStart.x - pt.x) < 3) && (abs (pWndData->ptMovStart.y - pt.y) < 3)) break; // Tell the Input Manager about the move. pWndData->imi.rcSipRect = rectFloat; pWndData->imi.fdwFlags &= ~SIPF_DOCKED; pWndData->imi.fdwFlags |= SIPF_ON; IIMCallback_SetImInfo(pWndData->pIMCallback, &pWndData->imi); break; case WM_LBUTTONDBLCLK: if (pt.x > GRIPWIDTH+3) return 0; ReleaseCapture(); pWndData->fMoving = FALSE;
pWndData->imi.fdwFlags |= (SIPF_DOCKED | SIPF_ON); pWndData->imi.rcSipRect = pWndData->rectDocked; IIMCallback_SetImInfo(pWndData->pIMCallback, &pWndData->imi); break; } pWndData->dwBtnDnFlags = 0; // If we moved, no buttons down. return 1; } //---------------------------------------------------------------------- // DoMouseSip - Process mouse button messages for window. WM_LBUTTONDOWN // LRESULT CALLBACK DoMouseSip (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { RECT rect; INT i, x, y, cx, cy, nChar; DWORD BtnDnFlags, dwShiftFlags = 0; LPSIPWNDSTRUCT pWndData; pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA); // See if moving gripper or gripper tap. if (HandleGripper (hWnd, pWndData, wMsg, lParam)) return 0; // Compute the button grid. GetClientRect (hWnd, &rect); cx = (rect.right - rect.left - 3 - GRIPWIDTH) / CXBTNS; cy = (rect.bottom - rect.top - 3) / CYBTNS; x = ((LOWORD (lParam)-3-GRIPWIDTH) / cx); y = ((HIWORD (lParam)-3) / cy); i = (y * CXBTNS) + x; // i now contains btn index. // Do small amount of message-specific processing. switch (wMsg) { case WM_LBUTTONDOWN: SetCapture (hWnd); // Fall through to WM_MOUSEMOVE case. case WM_MOUSEMOVE: BtnDnFlags = 1 << i; break; case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: if (pWndData->dwBtnDnFlags) ReleaseCapture(); BtnDnFlags = 0; nChar = g_tcBtnChar[i]; IIMCallback_SendCharEvents(pWndData->pIMCallback, g_BtnVChars[i], KeyStateDownFlag, 1, &dwShiftFlags, &nChar);
break; } // Decide how to repaint wnd. If only 1 btn changed, just // invalidate that rect. Otherwise, invalidate entire wnd. if ((wMsg == WM_MOUSEMOVE) && (BtnDnFlags !=pWndData->dwBtnDnFlags)) InvalidateRect (hWnd, NULL, FALSE); else { i = 3+GRIPWIDTH; // Compensate for the gripper on left side. SetRect (&rect, x*cx+i, y*cy, (x+1)*cx+i, (y+1)*cy); InvalidateRect (hWnd, &rect, FALSE); } pWndData->dwBtnDnFlags = BtnDnFlags; return 0; } //---------------------------------------------------------------------- // DoDestroySip - Process WM_DESTROY message for window. // LRESULT CALLBACK DoDestroySip (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { LPSIPWNDSTRUCT pWndData; pWndData = (LPSIPWNDSTRUCT)GetWindowLong (hWnd, GWL_USERDATA); LocalFree (pWndData); return 0; } //--------------------------------------------------------------------- // DrawButton - Draws a button // INT DrawButton (HDC hdc, RECT *prect, LPTSTR pChar, BOOL fPressed) { if (!fPressed) { SelectObject (hdc, GetStockObject (BLACK_PEN)); SelectObject (hdc, GetStockObject (WHITE_BRUSH)); SetBkColor (hdc, RGB (255, 255, 255)); SetTextColor (hdc, RGB (0, 0, 0)); } else { SelectObject (hdc, GetStockObject (BLACK_BRUSH)); SelectObject (hdc, GetStockObject (WHITE_PEN)); SetTextColor (hdc, RGB (255, 255, 255)); SetBkColor (hdc, RGB (0, 0, 0)); } Rectangle (hdc, prect->left, prect->top, prect->right, prect->bottom); Rectangle (hdc, prect->left+1, prect->top+1, prect->right+1, prect->bottom+1); DrawText (hdc, pChar, 1, prect, DT_CENTER|DT_VCENTER|DT_SINGLELINE); return 0; }
Although NumPanel is divided into two source files, both the IInputMethod2 interface and the NumPanel window procedure run in the same thread. In response to a call to the Select method of IInputMethod2, the NumPanel window class is registered and the window is created as a child of the IM’s window. The image lists used by the IM are also created here with the handles stored in member variables in the MyIInputMethod object. The only other work of interest performed by the code in IMCommon.cpp is the code for the GetInfo method. In this method, the image list handles are provided to the IM along with the requested dimensions of the undocked window. The dimensions of the docked window are provided by the system.
For four other methods, all MyIInputMethod does is post messages to the window procedure of the NumPanel window. In NumPanel.c, these messages are fielded in the MYMSG_METHCALL user-defined message. The four methods make available to the window a pointer to the IIMCallback and IIMCallback2 interfaces and notify the NumPanel window that the window is about to be displayed or that the state of the input panel is changing.
The other code in the NumPanel window draws the keys on the window and processes the stylus taps. The DoPaintSip routine handles the painting. The routine draws a grid of 3 rows and 5 columns of buttons. In each button, a character is drawn to label it. A separate bit array contains the up or down state of each button. If the button is down, the background of the button is drawn in reverse colors.
Two routinesDoMouseSip and HandleGripper handle the mouse messages. The mouse messages all initially go to DoMouseSip, which calls HandleGripper. If the routine determines that the mouse message is on the gripper or that the window is currently being dragged, HandleGripper handles the message. Otherwise, if the DoMouseSip routine determines that a mouse tap occurs on one of the buttons, it calls the SendCharEvent method of IIMCallback to send the character to the focus window.
When the window is dragged to a new location on the screen, the HandleGripper routine clears the SIPF_DOCKED flag and sets the new size and location of the SIP by calling the SetImInfo method of IIMCallback. When the user double-taps the gripper, HandleGripper sets the SIPF_DOCKED flag and sets the SIP rectangle to the original docked rectangle that was saved when the NumPanel window was first created.
In the next chapter, I step back from application programming and look at system programming issues. Chapter 16 explains how the different components of Windows CE work together while presenting a unified Win32-compatible API.
Previous
| Table of Contents
| Next
Last Updated: Saturday, July 7, 2001 |