win32程序值窗口程序,以及消息机制
一丶简介
通过上一讲.我们了解了窗口其实是绘制出来的.而且是不断绘制的过程. 所以窗口的本质是绘制. 但是我们现在看到的窗口程序.都可以点击关闭按钮. 使用鼠标点击会有反应.
而我们要怎么实现那.
其实鼠标点击是产生了一个消息. window把这个消息封装成了消息结构体. 发送给了我们的窗口程序. 那么windows怎么知道你点击的那个窗口那?
是这样的. 当我们点击的时候. 会记录点击坐标.消息.等等. windows系统会接受到. 然后遍历内核中的WINOBJ结构. 而这个结构中存储着窗口对象. 窗口对象对应着消息线程.
所以windows一层一层的遍历.则找到了对应的窗口以及窗口对应的线程.然后发送给我们的应用程序.
上面说的我们需要了解. 要知道消息怎么产生的. 怎么传递的.那么下面编程就明白了.
例如下图:
每个应用程序都有一个线程对象. 而这个线程对象如果创建窗口.那么内核中就有这个窗口对象.
如果我们有鼠标点击的消息.键盘消息等等.操作系统都会遍历窗口对象. 而窗口对象也会保存着创建这个窗口对象对应的线程对象. 而这个线程对象中则有消息队列.
这样的话操作系统则会封装消息发送给我们窗口对象.
二丶Wind窗口类结构.创建窗口程序.
1.进行窗口编程需要注意的问题
在Windows中进行窗口编程.入口点已经改成WinMain了. 有四个参数.
如以下代码所示
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, //窗口的实例句柄 hinstance代表模块意思 HWND代表窗口意思. HANDLE代表内核对象. HDC 设备上下文. _In_opt_ HINSTANCE hPrevInstance, //父窗口句柄.不需要. _In_ LPWSTR lpCmdLine, //命令行参数 _In_ int nCmdShow) //命令. 最大化命令.还是最小化命令. { return 0; }
wWinMain 因为有UNICODE跟ASCII区别. 所以我是UNICODE使用wWinMain. A版本就是用WinMain
2.进行Windows编程的调试手法
在Windows中我们调试程序不能简单的使用printf进行调试.或者打印输出了. 我们可以使用两个API进行操作.
1.Sprintf() 格式化字符串.
2.OutPutDebugString() 输出调试字符串.
具体两个API. 不再累赘.百度搜索即可.
因为OutPutDebugString() 只能打印固定字符串.所以使用sprintf进行格式化字符串.如下面代码.
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { TCHAR str[30] = { NULL }; wsprintf(str,TEXT( "%s"),TEXT("HelloWin32")); //因为是Unicode所以使用W版本. OutputDebugStringW(str); return 0; }
我们编译出程序之后.可以使用DebugView这款工具查看.
3.窗口编程的步骤
1.创建窗口类. windows提供的窗口样式.我们需要给定.
2.注册窗口类.创建了窗口我们需要注册到windows系统中.
3.创建窗口.如果注册窗口成功.那么我们需要创建出来这个窗口.并且显示跟更新.
4.消息处理
4.窗口编程需要的主要结构
窗口的创建Windows已经为我们提供了. 这个结构就是WNDCLASSEXW 结构
看下这个结构中的内容吧
typedef struct _WNDCLASSEX { UINT cbSize; 扩展的大小. 自己WndClass本身大小. UINT style; 风格 WNDPROC lpfnWndProc; 窗口回调.消息都要进入这个回调 int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; 实例句柄 HICON hIcon; 图标 HCURSOR hCursor; 光标 HBRUSH hbrBackground; 背景 LPCTSTR lpszMenuName; 菜单名称 LPCTSTR lpszClassName; 类名称 HICON hIconSm; 最小化图标 } WNDCLASSEX, *PWNDCLASSEX;
这个结构就是说.你的窗口是什么样式. 大小.是否有图标. 消息处理函数在哪里等等.需要我们给指定.
5.完整代码.
// WindoS.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include "WindoS.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 WCHAR szTitle[MAX_LOADSTRING] = TEXT("第一个我的窗口"); // 标题栏文本 WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow"); // 主窗口类名 // 此代码模块中包含的函数的前向声明: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { //1.自定义窗口样式 WNDCLASS wcex; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; //设置回调 wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; //设置模块实例句柄 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //设置背景颜色 wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS); wcex.lpszClassName = szWindowClass; //设置类名
//上面主要就是4个参数有用. 回调函数 背景颜色 模块句柄. 类名 //2.注册窗口类 BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW 扩展 RegisterClassExA /ExW if (bRet == FALSE) { return 0; } //3.创建窗口 并且显示跟更新窗口 HWND hWnd = CreateWindowW( szWindowClass, //我们的类名 szTitle, //我们自定义的窗口名称 WS_OVERLAPPEDWINDOW, //窗口的创建样式 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, //实例句柄 nullptr); //上面重要的也就是4个参数.其余参数查询下MSDN. if (!hWnd) { return FALSE; } ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //4.消息循环. MSG msg; /* 1参数是消息结构体.操作系统会往里面填写消息. 2 参数窗口句柄 因为每个线程可以有多个窗口.表示我要取那个窗口的消息 3.4 参数表示我要取这个窗口的那个消息. 后面三个参数属于过滤条件 */ while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); //键盘消息转换为小写. DispatchMessage(&msg); //分发消息.将我们的消息传递给我们的回调函数处理. 重要函数.此消息会将Windows的消息.发送给我们 定义窗口类的时候给的回调函数.这样我们就可以根据消息执行我们代码了. } } return 0; } // // 函数: 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); //取低两位为菜单ID.根据菜单ID可以进行操作我们的窗口 // 分析菜单选择: switch (wmId) { 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 的任何绘图代码... LineTo(hdc, 0, 100); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
创建完毕的结果
三丶消息类型
我们回调中有我们的消息类型.我们可以判断消息类型进行我们不同的操作.
比如菜单消息.
WM_COMMAND. 如果是这个消息.那么回调函数的 wparam等附加信息就是WM_COMMAND的附加消息了. 我们可以取低位得出操作的菜单ID.进而进行消息处理.
WM_PAINT 这个消息是绘制的消息.我们知道.窗口是不断绘制的.所以绘制消息会一直来.
WM_DESTROY 窗口关闭消息. 如果接受到这个消息.则调用API往消息队列中(MSG)中传递退出消息. 此时外层主线程就会结束.
具体API:
postQuitMessage(0);
当前具体的消息还要查询MSDN. 因为消息种类很多.
windows消息都是WM开头的.
比如查询WM_COMMAND消息
可以清楚的看到.她会告诉你如果是WM_COMMAND消息来了.那么回调函数的参数.分别代表的是什么意思.