[翻译]-WinCE 程序设计 (3rd 版)--1.7 HelloCE

HelloCE
Windows 编程中典型的SDK风格饱受责难的地方就是在窗口过程中总是使用巨大的switch语句。switch语句分析传给窗口过程的消息,这样每个消息可以被独立的处理。这种标准结果的优势之一是强制把一个类似的结构加到几乎所有Windows应用程序中,这使一个程序员可以更容易理解另一个人的代码。劣势是 整个窗口过程的所有的变量通常会比较杂乱的出现在过程的开头。

这么多年来,我为我的Windows程序探索出一个不同的风格。主要想法是将WinMain和WinProc过程分解成更易理解和更易转换到其它Windows程序中的可管理单元。WinMain被分解成几个过程,包括执行应用程序初始化、实例初始化和实例终止。作为所有Windows程序核心的消息循环也在WinMain里。

窗口过程被分解为几个独立过程,每个处理一个具体的消息。窗口过程自身是一个代码框架,只是查找传入的消息,看是否有过程来处理这个消息。如果有,则调用该过程;如果没有,则把消息传递给默认的窗口过程。

把对消息的处理划分成独立的块,使该结构更容易被理解。并且,由于将一个消息处理代码段同另一个极大的隔离开来,可以使您更容易把处理特定消息的代码从一个程序转换到另一个程序。还是在很多年前,我从Ray Duncan在《PC杂志》的“编程力量”专栏里,第一次看到对这种结构的描述。在MS-DOS和OS/2编程领域里,Ray是传奇人物之一。虽然为了适合我的需要,我对这种设计做了一些修改,但这还是应该归功于Ray。

代码
HelloCE的源代码如清单1-4所示:
清单1-4 HelloCE程序
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;
}

如果您浏览HelloCE的源代码,会从中看到本书中所有程序都使用的标准模板。在包含的头文件和宏定义之后是一些全局变量。我知道有很多好的建议劝说全局变量不应该出现在程序里,但我使用他们是为了便于简化本书中的例子,并使它们更条理清晰。每个程序都定义了一个Unicode字符串szAppName,它用在程序的很多地方。hInst 也同样用在很多地方,并且在详述InitInstance 过程时,我会提到它。最后那个全局结构是一个消息列表,包含处理相关消息的过程。窗口过程使用这个结构将消息及处理消息的过程关联到一起。

HelloCE中,WinMain有两各基本功能:调用InitInstance(放有应用程序初始化的代码),在消息循环中处理消息,当消息循环退出时调用TerminateInstance。在这个程序模版里,WinMain成为一个基本不用修改的例程。总的来说,WinMain唯一的改变之处是对消息循环处理过程的修改,涉及对键盘加速键的处理以及对无模式对话框消息及其他任务的监控。

实例初始化
InitInstance 的主要任务是注册主窗口的窗口类,创建应用程序主窗口,按传给WinMain的nCmdShow 参数指定的格式显示主窗口。如果是为Pocket PC编译,则还有一些条件编译代码,用来防止同一时刻运行一个程序的多个实例。

InitInstance做的第一个工作就是把程序实例句柄hInstance 保存在全局变量hInst中。程序的实例句柄在Windows应用程序的很多地方是很有用的。我之所以在这里保存这个值是因为此时已经知道实例句柄并且这是程序中保存实例句柄的一个很方便的地方。

当在Pocket PC上运行时,HelloCE用FindWindow来查看是否有自身的副本正在运行。该函数在顶层窗口中搜索,看是否有类名或窗口标题匹配或者两者都匹配的窗口。如果找到匹配的,用SetFroegroundWindow函数将窗口放到最前面,之后该例程退出,返回码为0,这将使WinMain退出,终止应用程序。我将花更多的时间在17章里讨Pocket PC相关的代码。

Pocket PC相关的代码包含在#if and #endif行里。这些行告诉编译器只有当#if语句的条件为真的时候才包含它们,在本例中,如果定义了常量WIN32_PLATFORM_PSPC,则条件为真。该常量定义在[项目设置]中。快速浏览一下[项目设置]中的 C/C++ 页,可以看到完整的关于预处理器定义的区域。在这个区域里,定义之一是$(CePlatform-Ce平台),$注册值占位符。在Key [HKEY_LOCAL_MACHINE]/Software/Microsoft/Windows CE Tools/Platform Manager 里,你可以找到一系列注册key,每个代表一个在eVC++中安装的目标平台。CePlatform(Ce平台)值取决于目标项目的不同。对Pocket PC 和老式Palm-size PC 项目,CePlatform定义为WIN32_PLATFORM_PSPC。

窗口类的注册和主窗口的创建过程与Hello3中的过程很相似。唯一的不同是使用了用做主窗口类类名的全局字符串szAppName。每次我用这个模板,我将szAppName 改成程序的名字即可。这可以使不同的应用程序窗口类名保持唯一,方便FindWindow工作。

到此InitInstance 算是完成了,应用程序主窗口已经创建和更新了,因为在进入消息循环之前,已经有消息发送给窗口的窗口过程了。现在是时候看看这部分程序了。

窗口过程
当你写Windows程序的时候,编程的大部分时间是花在窗口过程上的。窗口过程是程序的核心,也是窗口行为创建程序个性的地方。

同大部分不使用MFC等类库的Windows程序相比,我的编程风格明显不同的地方正是在窗口过程里。对于我的几乎所有程序,窗口过程都和前面HelloCE里的一样。在进一步深入之前,我重申:这种程序结构并不特别用在Windows CE中。我的所有Windows应用程序都使用这种风格,不管是Windows 3.1、Me、XP,还是CE。

这种风格将窗口构成简化成一个简单的表查找函数。主要思想是在早先定义在C文件里的MainMessages表里查找消息值。如果消息被找到,关联的过程就被调用,并把原始参数传递给处理该消息的过程。如果没有找到,调用默认过程DefWindowProc。DefWindowProc是Windows函数,为系统里所有消息提供默认行为,这使得Windows程序不用处理传递给窗口的每个消息。

消息表将消息值和处理消息的过程关联在一起。该表如下所示:
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
    WM_PAINT, DoPaintMain,
    WM_DESTROY, DoDestroyMain,
};
该表定义为常量,这不仅仅是好的编程习惯,而且因为这样可以帮助节约内存。因为Windows CE程序可以在ROM里执行,不变的数据应该标记为常量。这样允许Windows CE程序装载器将常量数据放在ROM里,而不是装载一个副本到RAM里,因此节约了宝贵的RAM。

表自身是一个简单的结构数组,该结构包含2个原素。第一项是消息值,第二项是指向处理消息的函数的指针。因为函数可以起任何名字,所以在整本书里我使用了一致的结构,好帮助您理解它们。名字由Do前缀(符合面向对象的经验),后跟一个消息名及一个用来指明与表相关的窗口类的后缀。例如DoPaintMain是为程序的主窗口处理Wm_PAINT消息的函数名。

DoPaintMain 和DoDestroyMain
HelloCE里两个消息处理例程是PaintMain 和DoDestroyMain。它们和Hello3里case子句的功能很类似。独立例程的好处是代码和本地局部变量是例程隔离的。而在Hello3的窗口过程里,针对绘画代码的局部变量则都集中在窗口过程的顶部。代码的封装使您很容易复制代码到您的下一个应用程序里。

运行HelloCE
当在eVC++中输入和编译本程序后,可以在VC++里选择 [Build]->[Execute] HelloCE.exe或者按Ctrl+F5远程执行本程序。程序在空白窗口的中间显示一行"Hello Windows CE"字样,图1-3和图1-4显示了HelloCE运行在PocketPC上的样子。点一下标题栏上的[Close]按钮会让Windwos CE给窗口发送WM_CLOSE消息。虽然HelloCE没有明确的处理WM_CLOSE消息,但DefWindowProc过程使用销毁主窗口作为默认的处理方式。因为窗口正在销毁,会发出WM_DESTORY消息,这又会导致对PostQuitMessage的调用。
图1-3(略)
运行在嵌入式Windows CE系统上的HelloCE窗口。

正象我说过的,HelloCE是一个非常基本的Windows CE程序,但它给您展示了一个应用程序基本框架,您可以在上面添加更多内容。如果您用浏览器看HelloCE.exe文件,会发现该程序使用的是通用图标。当HelloCE运行的时候,任务条上HelloCE对应的按钮里,文字旁边也没有图标。关于给程序增加定制图标以及DrawText函数是如何工作的,我将在后面几章作为两个主题来讲解。

图1-4(略)
运行在Pocket PC上的HelloCE窗口

图1-4显示了HelloCE在Pocket PC上运行时的问题。HelloCE窗口伸展到了屏幕底部。和程序间具体切换方式有关,SIP(输入法软键盘)按钮可能显示在HelloCE窗口的上面。为Pocket PC设计的应用程序会在屏幕底部创建一个菜单条,用来显示软键盘等事物的按钮会包含在上面。

必须人工调整窗口尺寸,避免覆盖菜单条或者被菜单条覆盖。稍后我们会讨论如何为Pocket PC的用户界面设计应用程序。可以确信的是:书中头几章关于Windows CE的内容一样适用于Pocket PC设备及其它Windows CE系统。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值