Windows编程_Lesson004_Windows消息机制
来源:互联网 发布:mac好玩的免费游戏 编辑:程序博客网 时间:2024/06/06 00:23
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消息机制
- Windows编程_Lesson004_Windows I/O操作
- windows编程--Windows消息机制要点
- Windows编程入门-Windows消息机制
- (Windows编程)Windows的消息机制(消息的结构、类型)
- 【Windows编程】Step.2 消息循环机制
- windows编程笔记(一)消息机制
- windows编程(消息机制、消息队列、消息映射、线程同步)笔记windows编程(消息机制、消息队列、消息映射、线程同步)笔记
- 从零开始WindowsAPI编程---Windows消息的处理机制
- Windows编程基础--第4节 MFC消息映射机制
- 浅析Windows消息机制
- windows消息机制
- windows消息机制
- Windows消息机制
- [转]Windows 消息机制
- 理解Windows消息机制
- Windows 消息机制(1)
- Windows消息机制要点
- FreeMarker语法知识
- hibernate主配置文件
- ViewPager无限自动轮播小圆点+网络加载图片+活动倒计时
- [bzoj1500] 维修数列
- Android学习之获取外置SD卡读写路径方法详解
- Windows编程_Lesson004_Windows消息机制
- 21 idea_maven 下载不了代码
- sql-server 创建
- C语言指针
- 经典sql面试题
- 【java web】实现判断是由手机访问还是由电脑访问
- 红外图像非均匀矫正——一点矫正
- 开发windows服务
- :网友年龄 某君新认识一网友。 当问及年龄时,他的网友说: “我的年龄是个2位数,我比儿子大27岁, 如果把我的年龄的两位数字交换位置,刚好就是我儿子的年龄” 请你计算:网友的年龄一共有多少