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

相关文章

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Python+Tkinter实现Windows Hosts文件编辑管理工具

《Python+Tkinter实现WindowsHosts文件编辑管理工具》在日常开发和网络调试或科学上网场景中,Hosts文件修改是每个开发者都绕不开的必修课,本文将完整解析一个基于Python... 目录一、前言:为什么我们需要专业的Hosts管理工具二、工具核心功能全景图2.1 基础功能模块2.2 进

Python+PyQt5开发一个Windows电脑启动项管理神器

《Python+PyQt5开发一个Windows电脑启动项管理神器》:本文主要介绍如何使用PyQt5开发一款颜值与功能并存的Windows启动项管理工具,不仅能查看/删除现有启动项,还能智能添加新... 目录开篇:为什么我们需要启动项管理工具功能全景图核心技术解析1. Windows注册表操作2. 启动文件

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

JVM垃圾回收机制之GC解读

《JVM垃圾回收机制之GC解读》:本文主要介绍JVM垃圾回收机制之GC,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、死亡对象的判断算法1.1 引用计数算法1.2 可达性分析算法二、垃圾回收算法2.1 标记-清除算法2.2 复制算法2.3 标记-整理算法2.4

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

使用Python创建一个功能完整的Windows风格计算器程序

《使用Python创建一个功能完整的Windows风格计算器程序》:本文主要介绍如何使用Python和Tkinter创建一个功能完整的Windows风格计算器程序,包括基本运算、高级科学计算(如三... 目录python实现Windows系统计算器程序(含高级功能)1. 使用Tkinter实现基础计算器2.

Windows系统宽带限制如何解除?

《Windows系统宽带限制如何解除?》有不少用户反映电脑网速慢得情况,可能是宽带速度被限制的原因,只需解除限制即可,具体该如何操作呢?本文就跟大家一起来看看Windows系统解除网络限制的操作方法吧... 有不少用户反映电脑网速慢得情况,可能是宽带速度被限制的原因,只需解除限制即可,具体该如何操作呢?本文

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.