Windows编程_Lesson004_Windows消息机制

2024-06-06 12:18

本文主要是介绍Windows编程_Lesson004_Windows消息机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WinMain函数

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR    lpCmdLine,_In_ int       nCmdShow);

Windows的调用约定:

#define CALLBACK    __stdcall
#define WINAPI      __stdcall
#define WINAPIV     __cdecl
#define APIENTRY    WINAPI
#define APIPRIVATE  __stdcall
#define PASCAL      __stdcall

这些调用约定即使不写的话,也可以通过编译。这些调用约定会决定函数的传参、平栈的方式变得不同。这是因为:
函数是以栈的方式进行参数传递,参数传递完成后,栈帧进行平栈。
__stdcall表示(参考百度百科)
1.参数从右向左压入堆栈
2.函数被调用者修改堆栈
3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见。
__cdecl __fastcall与__stdcall,三者都是调用约定(Calling convention),它决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法。
1、__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。
2、__cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。
3、__fastcall调用约定:它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。
4、thiscall仅仅应用于”C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
5、nakedcall采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

In HINSTANCE hInstance

本身的进程实例句柄。
hInstance是一个传入的参数,那么为什么我们自己的进程还需要从外部传入呢?
实际上我们的wWinmain函数是一个被操作系统所调用的函数,如下图所示:

这里写图片描述

从图上也可以看出,我们进程本身是不能创建自己的,只能通过操作系统来对这个进程进行创建,创建完成之后,我自己也不知道自己的进程编号,所以只能够由外部来传入。

那么疑问就来了,我们以前写的控制台程序中的main函数里面也可以没有参数呀!更别说要传递进程编号了,那么是不是这两个主函数就是两个不同的函数呢?其实并不是这样的,main函数是c语言中的,因为c语言出现的时候,还没有操作系统这个概念,所以一个main函数就是一个进程,并不需要外部的参数传入。但是我们如果使用的是c语言的main函数,在Windows下,还是照样能够通过系统的API来获取自己的实例句柄的。

In_opt HINSTANCE hPrevInstance

父进程的实例句柄,启动本进程的那个进程,可以做一些简单的检测。

In LPWSTR lpCmdLine

命令行参数
可以使外部和进程进行通信。我们可以在exe程序的快捷方式里面加上一些参数,来进行命令行的传递。例如:

这里写图片描述

In int nCmdShow

显示方式,Windows所特有的。
和外部进行通信。

窗口创建

    // 初始化全局字符串LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);LoadStringW(hInstance, IDC_WIN32WINDOWSDEMO, szWindowClass, MAX_LOADSTRING);

IDS_APP_TITLE和IDC_WIN32WINDOWSDEMO代表资源表中的信息。
MyRegisterClass(hInstance);
这个函数的主要作用是将WNDCLASSEXW结构体用RegisterClassExW(&wcex)进行注册,从而得到自己想要的风格的窗口。
需要注意的是,只要遇到cbSize这个参数,就一定要正确的填充,否则会出现错误或者异常。

WNDCLASSEXW 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(hInstance, MAKEINTRESOURCE(IDI_WIN32WINDOWSDEMO));wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WIN32WINDOWSDEMO);wcex.lpszClassName  = szWindowClass;wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

通过调用注册窗口类来生成一个窗口模板。

保存实例句柄并创建主窗口
    // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)){return FALSE;}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{hInst = hInstance; // 将实例句柄存储在全局变量中HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);if (!hWnd){return FALSE;}ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}

窗口过程函数

在看窗口过程函数之前,我们先再来看一下NT结构。
这里写图片描述
细心的你会发现从用户态到核心态再到硬件层,没有一个箭头是向上的,都是从上而下的,也就是说数据的传递都是从上而下的,而没有从下到上的数据传递。并且核心态又不能直接操作用户态,那么问题就来了,当硬件发生改变时,我们用户态的软件又是如何得知这一改变呢?请往下看:
当硬件发生改变时,内核会将这个改变写到一块固定的区域里面,这块区域会不停地被Win32子系统去扫描,当扫描到硬件发生改变时,Win32子系统就会将这个改变翻译成一个消息(为什么不在内核态来做呢?原因很简单,是遵循内核精简原则才这么设计的)

主消息循环

每个应用程序都有一个消息循环,也就是一个消息队列,

    // 主消息循环: while (GetMessage(&msg, nullptr, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}

当消息队列里面接收到消息时,就会调用下面的窗口过程函数,进行消息处理。

//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{switch (message){case WM_COMMAND:{int wmId = LOWORD(wParam);// 分析菜单选择: switch (wmId){case IDM_ABOUT:DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;case WM_PAINT:{PAINTSTRUCT ps;HDC hdc = BeginPaint(hWnd, &ps);// TODO: 在此处添加使用 hdc 的任何绘图代码...EndPaint(hWnd, &ps);}break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}

浅析MFC

在MFC中,我们调用的所有函数都是MFC中的函数,我们调用MessageBoxW(L”“)函数,它的函数原型如下所示,最多需要传递三个参数。

int _AFX_FUNCNAME(MessageBox)(LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK);
而Win32中调用MessageBoxW()函数,它的函数原型如下所示:
MessageBoxW(_In_opt_ HWND hWnd,_In_opt_ LPCWSTR lpText,_In_opt_ LPCWSTR lpCaption,_In_ UINT uType);

如果我们想要在MFC中调用全局的Win32函数,我们只需要在函数前面加上::全局域作用符。

这篇关于Windows编程_Lesson004_Windows消息机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1036082

相关文章

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

AOP编程的基本概念与idea编辑器的配合体验过程

《AOP编程的基本概念与idea编辑器的配合体验过程》文章简要介绍了AOP基础概念,包括Before/Around通知、PointCut切入点、Advice通知体、JoinPoint连接点等,说明它们... 目录BeforeAroundAdvise — 通知PointCut — 切入点Acpect — 切面

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

C#异步编程ConfigureAwait的使用小结

《C#异步编程ConfigureAwait的使用小结》本文介绍了异步编程在GUI和服务器端应用的优势,详细的介绍了async和await的关键作用,通过实例解析了在UI线程正确使用await.Conf... 异步编程是并发的一种形式,它有两大好处:对于面向终端用户的GUI程序,提高了响应能力对于服务器端应

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、