Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






HelloCE

One criticism of the typical SDK style of Windows programming has always been the huge switch statement in the window procedure. The switch statement parses the message to the window procedure so that each message can be handled independently. This standard structure has the one great advantage of enforcing a similar structure across almost all Windows applications, making it much easier for one programmer to understand the workings of another programmer's code. The disadvantage is that all the variables for the entire window procedure typically appear jumbled at the top of the procedure.

Over the years, I've developed a different style for my Windows programs. The idea is to break up the WinMain and WinProc procedures into manageable units that can be easily understood and easily transferred to other Windows programs. WinMain is broken up into procedures that perform application initialization, instance initialization, and instance termination. Also in WinMain is the ubiquitous message loop that's the core of all Windows programs.

I break the window procedure into individual procedures, with each handling a specific message. What remains of the window procedure itself is a fragment of code that simply looks up the message that's being passed to see whether a procedure has been written to handle that message. If so, that procedure is called. If not, the message is passed to the default window procedure.

This structure divides the handling of messages into individual blocks that can be more easily understood. Also, with greater isolation of one message-handling code fragment from another, you can more easily transfer the code that handles a specific message from one program to the next. I first saw this structure described a number of years ago by Ray Duncan in one of his old "Power Programming" columns in PC Magazine. Ray is one of the legends in the field of MS-DOS and OS/2 programming. I've since modified the design a bit to fit my needs, but Ray should get the credit for this program structure.


The Code


The source code for HelloCE is shown in Listing 1-4.

Listing 1-4: The HelloCE program







HelloCE.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
};
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);
//======================================================================
// HelloCE - A simple application for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "helloce.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT("HelloCE");
HINSTANCE hInst; // Program instance handle
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_PAINT, DoPaintMain,
WM_DESTROY, DoDestroyMain,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
// Initialize this instance.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0) return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
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;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, only allow one instance of the application
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application main window class.
wc.style = 0; // 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("HelloCE"), // Window title
// Style flags
WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
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 code 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);
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
RECT rect;
HDC hdc;
// Get the size of the client rectangle
GetClientRect (hWnd, &rect);
hdc = BeginPaint (hWnd, &ps);
DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}











If you look over the source code for HelloCE, you'll see the standard boilerplate for all programs in this book. A few variables defined globally follow the defines and includes. I know plenty of good arguments why no global variables should appear in a program, but I use them as a convenience that shortens and clarifies the example programs in the book. Each program defines an szAppName Unicode string to be used in various places in that program. I also use the hInst variable a number of places, and I'll mention it when I cover the InitInstance procedure. The final global structure is a list of messages along with associated procedures to process the messages. This structure is used by the window procedure to associate messages with the procedure that handles them.

In HelloCE, WinMain has two basic functions: it calls InitInstance (where the application initialization code is kept), processes the message in the message loop, and calls TerminateInstance when the message loop exits. In this program template, WinMain becomes a boilerplate routine that almost never changes. In general, the only changes that are made to WinMain concern modification of the processing of the message loop to process for keyboard accelerators, watch for modeless dialog box messages or other tasks.

InitInstance


Chapter 17.

These Pocket PC–specific lines are enclosed in #if and #endif lines. These lines tell the compiler to include them only if the condition of the #if statement is true—in this case, if the constant WIN32_PLATFORM_PSPC is defined. This constant is defined in the Project Settings for the project. A quick look at the C/C++ tab of the Project Settings dialog box shows an entry field for Preprocessor Definitions. In this field, one of the definitions is $(CePlatform), which is a placeholder for a registry value. Deep in the registry, under the key [HKEY_LOCAL_MACHINE]\Software\Microsoft\Windows CE Tools\Platform Manager, you can find series of registry keys, one for each target platform installed in eMbedded Visual C++. The CePlatform value is defined differently depending on the target project. For Pocket PC and old Palm-size PC projects, CePlatform is defined as WIN32_PLATFORM_PSPC.

The registering of the window class and the creation of the main window are quite similar to those in the Hello3 example. The only difference is the use of the global string szAppName as the class name of the main window class. Each time I use this template, I change the szAppName string to match the program name. This keeps the window class names somewhat unique for the different applications, enabling the FindWindow code in HelloCE to work.

That completes the InitInstance function. At this point, the application's main window has been created and updated. So even before we have entered the message loop, messages have been sent to the main window's window procedure. It's about time to look at this part of the program.

MainWndProc


You spend most of your programming time with the window procedure when you're writing a Windows program. The window procedure is the core of the program, the place where the actions of the program's windows create the personality of the program.

It's in the window procedure that my programming style differs significantly from most Windows programs written without the help of a class library such as MFC. For almost all of my programs, the window procedure is identical to the one previously shown in HelloCE. Before continuing, I repeat: this program structure isn't specific to Windows CE. I use this style for all my Windows applications, whether they are for Windows 3.1, Windows Me, Windows XP, or Windows CE.

This style reduces the window procedure to a simple table lookup function. The idea is to scan the MainMessages table defined early in the C file for the message value in one of the entries. If the message is found, the associated procedure is then called, passing the original parameters to the procedure processing the message. If no match is found for the message, the DefWindowProc function is called. DefWindowProc is a Windows function that provides a default action for all messages in the system, which frees a Windows program from having to process every message being passed to a window.

The message table associates message values with a procedure to process it. The table is listed below:

// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_PAINT, DoPaintMain,
WM_DESTROY, DoDestroyMain,
};

The table is defined as a constant, not just as good programming practice but also because it's helpful for memory conservation. Since Windows CE programs can be executed in place in ROM, data that doesn't change should be marked constant. This allows the Windows CE program loader to leave such constant data in ROM instead of loading a copy into RAM, thus saving precious RAM.

The table itself is an array of a simple two-element structure. The first entry is the message value, followed by a pointer to the function that processes the message. While the functions could be named anything, I'm using a consistent structure throughout the book to help you keep track of them. The names are composed of a Do prefix (as a bow to object-oriented practice), followed by the message name and a suffix indicating the window class associated with the table. So DoPaintMain is the name of the function that processes WM_PAINT messages for the main window of the program.

DoPaintMain and DoDestroyMain


The two message processing routines in HelloCE are DoPaintMain and DoDestroyMain. They mimic the function of the case clauses in Hello3. The advantage of the separate routines is that the code and their local variables are isolated to the routine. In Hello3's window procedure, the local variables specific to the paint code are bundled at the top of the routine. The encapsulation of the code makes it easy to cut and paste the code into the next application you write.


Running HelloCE


After you've entered the program into eMbedded Visual C++ and built it, you can execute it remotely from inside Visual C++ by selecting Execute Figure 1-3. Figure 1-4 shows HelloCE running on a Pocket PC. Tapping on the Close button on the title bar causes Windows CE to send a WM_CLOSE message to the window. Although HelloCE doesn't explicitly process the WM_CLOSE message, the DefWindowProc procedure enables default processing by destroying the main window. As the window is being destroyed, a WM_DESTROY message is sent, which causes PostQuitMessage to be called.


Figure 1-3: The HelloCE window on an embedded Windows CE system

As I said, HelloCE is a very basic Windows CE program, but it gives you a skeleton application on which you can build. If you look at the file Figure 1-3 representing HelloCE has no icon displayed next to the text. Adding a custom icon to a program and how the DrawText function works are a couple of the topics I'll address in the next few chapters.


Figure 1-4: The HelloCE window on a Pocket PC

Figure 1-4 shows a problem that HelloCE has running on a Pocket PC. The HelloCE window extends to the bottom of the screen. Depending on how you switch between applications, the button to display the SIP may appear over the top of the HelloCE window. Applications designed specifically for the Pocket PC will create a menu bar at the bottom of the screen that among other things contains the button necessary to display the soft keyboard. It must also resize its window manually to avoid covering, or being covered, by the menu bar. We'll see later in the book how to design an application specifically for the Pocket PC user interface. Rest assured that the lessons covering Windows CE in the early parts of the book apply as much to Pocket PC devices as to other Windows CE systems.

/ 169