深入Windows内核——C++中的消息机制

编程思想之消息机制》一文中我们讲了消息的相关概念和消息机制的模拟,本文将进一步聊聊C++中的消息机制。


从简单例子探析核心原理

在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色。其效果如下:

白色窗口 灰色窗口
图 2 :效果图

Win32Test.h

#pragma once

#include <windows.h>
#include <atltypes.h>
#include <tchar.h>

//资源ID
#define ID_BUTTON_DRAW      1000
#define ID_BUTTON_SWEEP     1001

// 注册窗口类
ATOM AppRegisterClass(HINSTANCE hInstance);
// 初始化窗口
BOOL InitInstance(HINSTANCE, int);
// 消息处理函数(又叫窗口过程)
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
// (白色背景)按钮事件
void OnButtonWhite();
// (灰色背景)按钮事件
void OnButtonGray();
// 绘制事件
void OnDraw(HDC hdc);

Win32Test.cpp


#include "stdafx.h"
#include "Win32Test.h"


//字符数组长度
#define MAX_LOADSTRING 100

//全局变量
HINSTANCE hInst;                                            // 当前实例
TCHAR g_szTitle[MAX_LOADSTRING] = TEXT("Message process");  // 窗口标题
TCHAR g_szWindowClass[MAX_LOADSTRING] = TEXT("AppTest");    // 窗口类的名称
HWND g_hWnd;                                                // 窗口句柄
bool g_bWhite = false;                                      // 是否为白色背景

//WinMain入口函数
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    // 注册窗口类
    if(!AppRegisterClass(hInstance))
    {
         return (FALSE);
    }
    // 初始化应用程序窗口
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}



// 注册窗口类
ATOM AppRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = g_szWindowClass;
    wcex.hIconSm        = NULL;

    return RegisterClassEx(&wcex);
}



// 保存实例化句柄并创建主窗口
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{  
   hInst = hInstance; // 保存handle到全局变量
   g_hWnd = CreateWindow(g_szWindowClass, g_szTitle, WS_OVERLAPPEDWINDOW, 0, 0, 400, 300, NULL, NULL, hInstance, NULL);
   // 创建按钮
   HWND hBtWhite = CreateWindowEx(0, L"Button", L"白色", WS_CHILD | WS_VISIBLE | BS_TEXT, 100, 100, 50, 20, g_hWnd, (HMENU)ID_BUTTON_DRAW, hInst, NULL);
   HWND hBtGray = CreateWindowEx(0, L"Button", L"灰色", WS_CHILD | WS_VISIBLE | BS_CENTER, 250, 100, 50, 20, g_hWnd, (HMENU)ID_BUTTON_SWEEP, hInst, NULL);

   if (!g_hWnd)
   {
      return FALSE;
   }
   ShowWindow(g_hWnd, nCmdShow);
   UpdateWindow(g_hWnd);

   return TRUE;
}



// (窗口)消息处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        //wmEvent = HIWORD(wParam);

        switch (wmId)
        {
        case ID_BUTTON_DRAW:
            OnButtonWhite();
            break;
        case ID_BUTTON_SWEEP:
            OnButtonGray();
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        OnDraw(hdc);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}



//事件处理

//按下hBtWhite时的事件
void OnButtonWhite()
{
    g_bWhite = 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 :消息队列示意图

为证明这一过程,我们可以改动一下上面的这个例子。
1.在Win32Test.h中添加ID_BUTTON_TEST的定义

#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(L"This is a ID_BUTTON_TEST message.");    // [BreakPoint1]
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

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

case ID_BUTTON_TEST:
    {
        OutputDebugString(L"This 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。



如果您有什么疑惑和想法,请在评论处给予反馈,您的反馈就是最好的测评师!由于本人技术和能力有限,如果本博文有错误或不足之处,敬请谅解并给出您宝贵的建议!




========================编程思想系列文章回顾========================
编程思想之消息机制
编程思想之日志记录
编程思想之异常处理
编程思想之正则表达式
编程思想之迭代器
编程思想之递归
编程思想之回调

  • 19
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: rootkits——windows内核的安全防护是一本针对Windows操作系统的内核级别安全防护的PDF文档。这本文档主要涵盖了rootkits的定义、特征、工作原理、攻击方式以及防御方法等内容。 首先,rootkits是一种恶意软件,通过植入操作系统内核来隐藏自身并实施攻击。文档详细介绍了rootkits的特征,例如可以修改系统内核代码、隐藏进程和文件等,从而使其在操作系统隐蔽存在。 文档还解释了rootkits的工作原理,即通过劫持系统调用、Hook函数和修改系统数据结构等方式来实现恶意行为。同时,文档还介绍了rootkits使用的主要攻击方式,如键盘记录、远程控制、网络欺骗等。 为了帮助用户保护自己的系统免受rootkits的攻击,该文档提供了一些防御方法。例如,通过实时监控系统进程、文件和注册表的变化,及时检测和阻止rootkits的安装和运行;使用可信任的安全软件进行系统扫描和漏洞修复;定期更新操作系统和应用程序的安全补丁等。 此外,文档还提供了一些用于检测和清除rootkits的工具和技术,如反rootkit软件、恶意代码扫描器等。这些工具可以帮助用户及时发现和清除系统已经存在的rootkits。 总的来说,rootkits——windows内核的安全防护这本PDF文档详细介绍了rootkits的定义、工作原理、攻击方式以及防御方法。对于那些希望了解和加强Windows系统安全性的用户来说,这本文档是一份宝贵的参考资料。 ### 回答2: rootkits是一种恶意软件,能够隐藏在操作系统内核,并且在被感染的计算机上实施恶意活动。它们可以完全控制系统,并且能够绕过传统的安全防护措施。为了确保计算机系统的安全,Windows内核的安全防护PDF提供了一种防范rootkits的方法。 首先,PDF文件详细介绍了rootkits的工作原理和其入侵系统的方式。这使得用户能够了解到rootkits的特征以及它们可能对计算机造成的危害。了解敌人才能更好地进行防范。 其次,PDF文件提供了关于如何检测和清除rootkits的指南。通过使用一些专业的工具和技术,用户可以发现并清除被rootkits感染的系统。这些工具可以扫描内核并查找任何异常行为,以及寻找rootkits常用的隐藏技术。通过及时发现和清除rootkits,可以最大限度地保护系统免受恶意活动的威胁。 此外,PDF文件还提供了一些加强系统安全的建议。比如,更新操作系统和应用程序,使用强密码保护帐户和网络连接,以及限制对敏感文件和设置的访问权限等等。这些措施可以帮助用户防止rootkits和其他恶意软件的入侵。 最后,PDF文件还介绍了一些未来的趋势和建议,以进一步提高Windows内核的安全性。这些建议包括加强内核的隔离性,采用虚拟化技术进行保护,以及实施更严格的访问控制等。这些措施将有助于加强系统的安全性,并提高对rootkits的抵御能力。 总的来说,Windows内核的安全防护PDF是一份有价值的文档,提供了对抗rootkits的指南和建议。通过采取相应的措施,用户可以保护系统免受rootkits的威胁,并提高整体的安全性。 ### 回答3: rootkits是一种针对Windows操作系统的恶意软件,它们通过在操作系统内核植入恶意代码,可以隐藏自身的存在并在系统启动时加载运行。rootkits通过修改关键的系统文件、进程和注册表项等方式,使恶意软件难以被发现和清除。 《rootkits——windows内核的安全防护》这本PDF书籍探讨了如何保护Windows内核免受rootkits的攻击。书首先介绍了rootkits的原理和工作方式,包括rootkits的分类、入侵方式和隐藏技术等。然后,书深入讲解了一些重要的内核安全技术,如代码签名、驱动程序的安全性和安全的加载和验证过程等。书还提供了一些常见的rootkits检测和清除工具,以帮助用户发现和清除系统的恶意软件。 此外,书还介绍了一些预防rootkits攻击的措施和最佳实践。例如,强化系统安全性、定期更新操作系统和安全软件、限制管理员权限、使用防火墙和入侵检测系统等。这些措施可以有效减少系统被rootkits攻击的风险。 总之,《rootkits——windows内核的安全防护》这本PDF书籍全面讲解了如何保护Windows内核免受rootkits攻击的方法和技术。它可以帮助用户了解rootkits的工作原理,并提供了一些重要的内核安全技术和预防措施,以增强系统的安全性和防御能力。对于IT安全从业人员和对计算机安全感兴趣的人来说,这本书是一本不可或缺的参考资料。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌尘(MoChen)

爱打赏的人技术成长更开哦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值