Windows消息机制学习笔记(二)—— 窗口与线程

  • 要点回顾
  • 消息从哪里来?
    • 实验一:Spy++捕获消息
    • 实验二:消息捕获
  • 消息到哪里去?
  • 窗口在哪?
    • 实验:分析CreateWindowExW
  • 窗口对象
  • 总结

要点回顾

一个GUI线程对应一个消息队列

本篇要解决的问题:

  1. 消息从哪里来?
  2. 消息到哪里去?
  3. 谁来做这些事情?

消息从哪里来?

1)Spy++ 捕捉消息:鼠标消息、键盘消息
2)程序发送消息

实验一:Spy++捕获消息

1)编译并运行以下代码

#include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam
){return DefWindowProc(hwnd, uMsg, wParam, lParam);
}int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd
){//窗口的类名TCHAR className[] = "My First Window";//创建一个自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注册RegisterClass(&wndclass);//创建窗口HWND hwnd = CreateWindow(className,TEXT("我的第一个窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//显示窗口ShowWindow(hwnd, SW_SHOW);//消息循环MSG msg;while(GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}

运行结果

2)使用spy++定位窗口



3)在窗口中进行任意操作,例如鼠标移动,鼠标点击,键盘敲击等,观察消息列表

实验二:消息捕获

1)进程A运行以下代码

#include <windows.h>LRESULT CALLBACK WindowProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam
){switch(uMsg){case 0x401:MessageBoxA(NULL, "接收到消息", "新消息", MB_OK);return false;}return DefWindowProc(hwnd, uMsg, wParam, lParam);
}int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd
){//窗口的类名TCHAR className[] = "My First Window";//创建一个自己的窗口WNDCLASS wndclass = {0};wndclass.hbrBackground = (HBRUSH)COLOR_MENU;wndclass.lpfnWndProc = WindowProc;wndclass.lpszClassName = className;wndclass.hInstance = hInstance;//注册RegisterClass(&wndclass);//创建窗口HWND hwnd = CreateWindow(className,TEXT("我的第一个窗口"),WS_OVERLAPPEDWINDOW,10,10,600,300,NULL,NULL,hInstance,NULL);if(hwnd == NULL)return 0;//显示窗口ShowWindow(hwnd, SW_SHOW);//消息循环MSG msg;while(GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}

2)进程B运行以下代码

#include <stdio.h>
#include <windows.h>int main()
{HWND hwnd = FindWindow("My First Window", "我的第一个窗口");SendMessage(hwnd, 0x401, 0, 0);return 0;
}

执行结果

消息到哪里去?

描述

  1. 当我们使用鼠标某个窗口进行点击与滑动时,都会产生一个消息,消息会进入当前窗口对应线程的消息队列中
  2. 当我们编写程序时,并不会去特地启动两个线程去监控鼠标和键盘,w32k.sys负责了这个事情

当初始化w32k.sys这个模块时,会调用一个叫做InitInputImpl的函数
这个函数会启动两个线程,分别用来监控鼠标和键盘,这两个线程都是0环的线程
平时我们的电脑遭遇“死机”时,常常是屏幕动不了,鼠标还能动,这正式由于鼠标是有一个独立的线程在监控它的行动

//FROM ReactOS v3.12
InitInputImpl(VOID)
{...Status = PsCreateSystemThread(&RawInputThreadHandle,       //监控鼠标THREAD_ALL_ACCESS,NULL,NULL,&RawInputThreadId,RawInputThreadMain,NULL);if (!NT_SUCCESS(Status)){DPRINT1("Win32K: Failed to create raw thread.\n");}Status = PsCreateSystemThread(&KeyboardThreadHandle,        //监控键盘THREAD_ALL_ACCESS,NULL,NULL,&KeyboardThreadId,KeyboardThreadMain,NULL);if (!NT_SUCCESS(Status)){DPRINT1("Win32K: Failed to create keyboard thread.\n");}...
}

思考w32k.sys如何区分用户点击的是哪个窗口/消息要去哪个线程

窗口在哪?

当调用CreateWindow时,该函数实际上是一个宏,其CreateWindowA实际对应CreateWindowExA函数,CreateWindowW对应CreateWindowExW函数,可在编辑器中跟踪观察

实验:分析CreateWindowExW

函数位于user32.dll
总结:窗口在0环被画出(由w32k.sys实现)

窗口对象

描述

  1. 每个窗口对应一个WINDOW_OBJECT结构体,位于0环,包含当前窗口所有信息
  2. 除了大窗口之外,窗口中的每个控件也都是一个窗口,也分别对应一个WINDOW_OBJECT结构体
  3. 一个窗口内包含着许多个窗口,按钮,对话框也都属于窗口,属于同一个线程
  4. 一个线程可以包含多个窗口,但每个窗口只能属于一个线程
//FROM ReactOS v3.12
typedef struct _WINDOW_OBJECT
{...PWND Wnd;           //包含大量窗口信息PTHREADINFO pti;  //线程...
}
//FROM ReactOS v3.12
typedef struct _WND
{.../* Style. */DWORD style;        //包含窗口风格/后一个窗口/前一个窗口/父窗口/子窗口等信息...
} WND, *PWND;

消息进入窗口消息队列的过程

  1. 当使用鼠标在某个窗口上点击时,鼠标监控线程检测到点击的窗口对象
  2. 根据窗口对象成员,找到窗口对应线程
  3. 将消息放入该线程的消息队列中

注意:在3环得到的窗口的句柄只是一个索引(参考句柄表章节)

总结

  1. 窗口在0环创建
  2. 窗口句柄是全局的
  3. 一个线程可以使用多个窗口,但每个窗口只能属于一个线程

Windows消息机制学习笔记(二)—— 窗口与线程相关推荐

  1. Windows消息机制学习笔记(三)—— 消息的接收与分发

    Windows消息机制学习笔记(三)-- 消息的接收与分发 要点回顾 消息循环 消息队列 消息的接收 GetMessage 实验1:理解GetMessage 第一步:编译并运行程序A 第二步:编译并运 ...

  2. Windows消息机制学习笔记(一)—— 消息队列

    Windows消息机制学习笔记(一)-- 消息队列 基本概念 实验一:使用代码画出最简单窗口 第一步:编译并运行以下代码 第二步:查看运行结果 第三步:使用其它窗口对其进行覆盖,观察效果 总结 消息队 ...

  3. 消息机制学习笔记(四)—— 内核回调机制

    消息机制学习笔记(四)-- 内核回调机制 要点回顾 内核调用 实验1:理解内核调用 第一步:编译并运行以下代码 第二步:修改窗口过程函数,重新运行 KeUserModeCallback 实验2:在OD ...

  4. Android消息机制学习笔记

    Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑: MessageQueue:消息队列,它的内存存储了一组消息,以队 ...

  5. 注意力机制学习笔记二(Attention-GAN、SAGAN、YLG-SAGAN)

    一.Attention-GAN 论文地址:Attention-GAN for Object Transfiguration in Wild Images Object Transfiguration不 ...

  6. Windows编程课程学习笔记

    一. Windows程序内部运行机制--Windows编程课程学习笔记 二. MFC框架程序分析--Windows编程课程学习笔记 三. 简单绘图--Windows编程课程学习笔记 四. 文本编程-- ...

  7. wxpython应用程序对象与顶级窗口_wxPython学习笔记(二)

    如何创建和使用一个应用程序对象? 任何wxPython应用程序都需要一个应用程序对象.这个应用程序对象必须是类wx.App或其定制的子类的一个实例.应用程序对象的主要目的是管理幕后的主事件循环. 父类 ...

  8. Windows事件等待学习笔记(二)—— 线程等待与唤醒

    Windows事件等待学习笔记(二)-- 线程等待与唤醒 要点回顾 等待与唤醒机制 可等待对象 可等待对象的差异 线程与等待对象 一个线程等待一个对象 实验 第一步:编译并运行以下代码 第二步:在Wi ...

  9. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

最新文章

  1. BREW 计费模式概览
  2. MIT与商汤科技成立人工智能联盟
  3. 计算机计量g代表,计算机中的有些计量单位例如G、MB是表示什么意思?
  4. linux HZ Tick Jiffies
  5. 配置kubernetes服务basic auth
  6. 修改程序配置文件 以及写一个结构体数组到文件(以及整数和结构体)
  7. 结婚戒指为什么戴在无名指上的原因
  8. Scala代码案例: StdIn和if..else
  9. SwaggerUI看烦了,IGeekFan.AspNetCore.Knife4jUI 帮你换个新皮肤
  10. 最佳的开源云项目有哪些?
  11. 连接Android与ASP.NET Core Web API的完整教程
  12. C/C++获取文件大小
  13. Android 仿PhotoShop调色板应用(一)概述
  14. IBM与红帽联手构建开源混合云环境
  15. DirectX修复工具使用技巧之一——解除被占用的文件,完整修复C++
  16. Yate架构分析概要
  17. Android下磁盘分区表损坏,硬盘分区表丢失错误怎么修复TestDisk使用教程
  18. vscode + prettier 专治代码洁癖
  19. 抖音做外卖会颠覆美团吗?
  20. 计算机台式和电脑的区别吗,直观:工业计算机和家用台式计算机有什么区别

热门文章

  1. DL之DNN:基于Tensorflow框架对神经网络算法进行参数初始化的常用九大函数及其使用案例
  2. TF:利用是Softmax回归+GD算法实现MNIST手写数字图片识别(10000张图片测试得到的准确率为92%)
  3. os_mutex.c(全)
  4. PL/SQL的快捷键设置
  5. 【转载】[OS X笔记]安装MacPorts
  6. 怎样在android平台上使用第三方jar包
  7. MPDU 和 MSDU 的区别及关系
  8. VS2017-MFC-生成二维码测试小程序
  9. C++ Primer 5th笔记(chap 17 标准库特殊设施)bitset类型
  10. 深度探索C++ 对象模型(2)-类的对象的内存大小_2