设为首页 加入收藏

TOP

深入Windows内核――C++中的消息机制(二)
2015-11-21 00:59:29 来源: 作者: 【 】 浏览:6
Tags:深入 Windows 内核 消息 机制
te = true; InvalidateRect(g_hWnd, NULL, FALSE); //刷新窗口 } //按下hBtGray时的事件 void OnButtonGray() { g_bWhite = false; InvalidateRect(g_hWnd, NULL, FALSE); //刷新窗口 } //绘制事件(每次刷新时重新绘制图像) void OnDraw(HDC hdc) { POINT oldPoint; SetViewportOrgEx(hdc, 0, 0, &oldPoint); RECT rcView; GetWindowRect(g_hWnd, &rcView); // 获得句柄的画布大小 HBRUSH hbrWhite = (HBRUSH)GetStockObject(WHITE_BRUSH); HBRUSH hbrGray = (HBRUSH)GetStockObject(GRAY_BRUSH); if (g_bWhite) { FillRect(hdc, &rcView, hbrWhite); } else { FillRect(hdc, &rcView, hbrGray); } SetViewportOrgEx(hdc, oldPoint.x, oldPoint.y, NULL); }

在上面这个例子中,消息的流经过程如下:

消息的流经过程
图 3 :消息的流经过程

这与《编程思想之消息机制》中图1(消息机制原理)是相吻合的,这就是Windows消息机制的核心部分,也是Windows API开发的核心部分。 Windows系统和Windows下的程序都是以消息为基础,以事件为驱动。
RegisterClassEx的作用是注册一个窗口,在调用CreateWindow创建一个窗口前必须向windows系统注册获惟一的标识。

?

while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

这个while循环就是消息循环,不断地从消息队列中获取消息,并通过DispatchMessage(&msg)将消息分发出去。消息队列是在Windows操作系统中定义的(我们无法看到对应定义的代码),对于每一个正在执行的Windows应用程序,系统为其建立一个“消息队列”,即应用程序队列,用来存放该程序可能创建的各种窗口的消息。DispatchMessage会将消息传给窗口函数(即消息处理函数)去处理,也就是WndProc函数。WndProc是一个回调函数,在注册窗口时通过wcex.lpfnWndProc将其传给了操作系统,所以DispatchMessage分发消息后,操作系统会调用窗口函数(WndProc)去处理消息。关于回调函数可参考:回调函数。
每一个窗口都应该有一个函数负责消息处理,程序员必须负责设计这个所谓的窗口函数WndProc。LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 中的四个参数就是消息的相关信息(消息来自的句柄、消息类型等),函数中通过switch/case根据不同的消息类型分别进行不同的处理。在收到相应类型的消息之后,可调用相应的函数去处理,如OnButtonWhite、OnButtonGray、OnDraw,这就是事件处理的雏形。 在default中调用了DefWindowProc,DefWindowProc是操作系统定义的默认消息处理函数,这是因为所有的消息都必须被处理,应用程序不处理的消息需要交给操作系统处理。


消息的定义和类型

Windows消息都以WM_为前缀,意思是”Windows Message”,如WM_CREATE、WM_PAINT等。消息的定义如下:

typedef struct tagMsg
{   
    HWND    hwnd;           //接受该消息的窗口句柄
    UINT    message;        //消息常量标识符,也就是我们通常所说的消息号
    WPARAM  wParam;         //32位消息的特定附加信息,确切含义依赖于消息值
    LPARAM  lParam;         //32位消息的特定附加信息,确切含义依赖于消息值
    DWORD   time;           //消息创建时的时间
    POINT   pt;             //消息创建时的鼠标/光标在屏幕坐标系中的位置
}MSG;

消息主要有三种类型:
1. 命令消息(WM_COMMAND):命令消息是程序员需要程序做某些操作的命令。凡UI对象产生的消息都是这种命令消息,可能来自菜单、加速键或工具栏按钮等,都以WM_COMMAND呈现。
2. 标准窗口消息:除WM_COMMAND之处,任何以WM_开头的消息都是这一类。标准窗口消息是系统中最为常见的消息,它是指由操作系统和控制其他窗口的窗口所使用的消息。例如CreateWindow、DestroyWindow和MoveWindow等都会激发窗口消息,以及鼠标移动、点击,键盘输入都是属于这种消息。
3. Notification:这种消息由控件产生,为的是向其父窗口(通常是对话框窗口)通知某种情况。当一个窗口内的子控件发生了一些事情,而这些是需要通知父窗口的,此刻它就上场啦。通知消息只适用于标准的窗口控件如按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。

队列消息和非队列消息

Windows中有一个系统消息队列,对于每一个正在执行的Windows应用程序,系统为其建立一个“消息队列”,即应用程序队列,用来存放该程序可能创建的各种窗口的消息。
(1)队列消息(Queued Messages)
消息会先保存在消息队列中,通过消息循环从消息队列中获取消息并分发到各窗口函数去处理,如鼠标、键盘消息就属于这类消息。
(2)非队列消息(NonQueued Messages)
就是消息会直接发送到窗口函数处理,而不经过消息队列。 如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED就属于此类。

PostMessage与SendMessage的区别

PostMessage发送的消息是队列消息,它会把消息Post到消息队列中; SendMessage发送的消息是非队列消息, 被直接送到窗口过程处理,等消息被处理后才返回。

这里写图片描述
图 4 :消息队列示意图

?<??http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPs6q1qTD99Xi0ru5/bPMo6zO0sPHv8nS1LjEtq/Su8/Cyc/D5rXE1eK49sD919OhozxiciAvPg0KMS7U2ldpbjMyVGVzdC5o1tDM7bzTSURfQlVUVE9OX1RFU1S1xLao0uU8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> #define ID_BUTTON_TEST 1002

2.在OnButtonWhite中分别用SendMessage和PostMessage发送消息
//按下hBtWhite时的事件

void OnButtonWhite()
{
    g_bWhite = true;
    InvalidateRect(g_hWnd, NULL, FALSE);    //刷新窗口
    SendMessage(g_hWnd, WM_COMMAND, ID_BUTTON_TEST, 0);
    //PostMessage(g_hWnd, WM_COMMAND, ID_BUTTON_TEST, 0);
}

3.在消息循环中增加ID_BUTTON_TEST的判断

while (GetMessage(&msg, NULL, 0, 0))
    {
        if (LOWORD(msg.wParam) == ID_BUTTON_TEST)
        {
            OutputDebugString(LThis is a ID_BUTTON_TEST message.);    // [BreakPoint1]
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

4.在窗口处理函数WndProc增加ID_BUTTON_TEST的判断

case ID_BUTTON_TEST:
    {
        OutputDebugString(LThis is a ID_BUTTON_TEST message.);        // [BreakPoint2]
    }
    break;
case ID_BUTTON_DRAW:
    OnButtonWhite();
    break;
case ID_BUTTON_SWEEP:
    OnButtonGray();
    break;

用断点调试的方式我们发现,用SendMessage发送的ID_BUTTON_TEST消息只会进入BreakPoint2,而PostMessage发送的ID_BUTTON_TEST会进入到BreakPoint1和BreakPoint2。

?

首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇HDU Wooden Sticks (贪心) 下一篇HDU 2923 Einbahnstrasse(最短路..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: