win32
一、windows消息机制
1、 消息是什么
Windows程序的运行是依靠外部的事件来驱动。换句话说,程序不断等待,等待任何可能的输入,然后做出判断,再做适当的处理。前面的“输入”是指操作系统发送给程序的消息。消息,其实就是系统内设的一种数据结构。
typedef struct MSG
{
HWND hwnd;//hwnd 是窗口的句柄,这个参数将决定由哪个窗口过程函数对消息进行处理
UINT message; //message是一个消息常量,用来表示消息的类型
WPARAM wParam; //32 位的附加信息,具体表示什么内容,要视消息的类型而定
LPARAM lParam; //32 位的附加信息,具体表示什么内容,要视消息的类型而定
Dword time; //time 是消息发送的时间
POINT pt; //消息发送时鼠标所在的位置
}
2、 消息类型的分类
消息类型可以分为两大类:系统定义消息和用户自定义消息。
从上面的消息定义可以看出,消息类型其实就是一个UINT类型的变量。系统定义消息值的范围是:0x0000-0x03ff,用户自定义消息值的范围是:0x0400-0x07ff,为了便于使用,系统定义了一个宏WM_USER来表示用户自定义消息的起始值,#define WM_USER 0x0400
。
系统定义消息
系统定义消息分为:窗口消息、命令消息、控件通知消息。
- 窗口消息:即与窗口的内部运作有关的消息,如创建窗口,绘制窗口,销毁窗口等
可以是一般的窗口,也可以是MainFrame,Dialog,控件等。 如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLcolor, WM_Hscroll等
- 命令消息:当用户从菜单选中一个命令项目、按下一个快捷键、点击工具栏上的一个按钮或者点击控件都将发送WM_commaND命令消息。通过消息结构中的wParam和lParam成员就能清楚得知道消息的来源。
LOWORD(wParam):代表菜单ID、或控件ID,或快捷键ID;
HIWORD(wParam):表示通知码,当消息是从菜单发出时,则这个值为0,当消息是从快捷键发出时,这个值为1,当消息是从控件发出时,这个值为通知码,比如按钮的通知码:BN_CLICKED, BN_DBLCLK等;
lParam:当消息从菜单和快捷键发出时,这个值为0,当从控件发出时,为控件的句柄。
- 通知消息:随着控件的种类越来越多,越来越复杂(如列表控件、树控件等),仅仅将wParam,lParam将视为一个32位无符号整数,已经装不下太多信息了。 为了给父窗口发送更多的信息,微软定义了一个新的WM_NOTIFY消息来扩展WM_COMMAND消息。 WM_NOTIFY消息仍然使用MSG消息结构,只是此时wParam为控件ID,lParam为一个NMHDR指针,不同的控件可以按照规则对NMHDR进行扩充,因此WM_NOTIFY消息传送的信息量可以相当的大。
3、 队列消息和非队列消息
前文有提起,应用程序会不断等待操作系统消息的输入。其实就是,程序中有一个获取消息的循环代码,会不断的从操作系统中获取消息。
- 队列消息
一般,程序都是从消息队列中获取消息。消息会先保存在消息队列中,消息循环会从此队列中取出消息并分发到各窗口处理 如:WM_PAINT,WM_TIMER,WM_CREATE,WM_QUIT,以及鼠标,键盘消息等。其中,WM_PAINT,WM_TIMER只有在队列中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。
系统中维护着一个全局的系统消息队列,还会为每一个UI线程维护一个UI线程消息队列。当系统消息队列中存在消息时,系统会根据消息所属的UI线程,分发到应用程序对应的UI线程消息队列中去。
- 非队列消息
但是还有一部分消息会绕过消息队列,直接发送到窗口过程进行处理 。如WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR,WM_WINDOWPOSCHANGED
二、简单的Win32程序实现
Windows程序设计中,消息机制是最重要的部分,至于窗口的产生和显示,有专门的Windows API负责,比较简单。
#include <windows.h>
#include "resource.h"
#define LDS_MAXLENGHT 100
Hinstance hInst; //应用程序实例
WCHAR lpsztitle[LDS_MAXLENGHT]; //窗口标题
WCHAR lpszWndClass[LDS_MAXLENGHT]; //窗口类名称
BOOL InitAPPlication(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK About(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPreInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow)
{
LoadString(hInstance, IDS_APPTITLE, lpszTitle, LDS_MAXLENGHT);
LoadString(hInstance, IDS_WNDCLASS, lpszWndClass, LDS_MAXLENGHT);
InitApplication(hInstance);
if (!InitInstance(hInstance, nCmdShow))
return false;
MSG msg;
HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDA_WINHELLO));
while (GetMessage(&msg, nullptr, 0, 0))
{
/**
* TranslateAccelerator函数
*/
if (!TranslateAccelerator(msg.hwnd, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (msg.wParam); //传回PostQuitMessage的参数
}
/**
* @brief 注册窗口类
*/
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof WNDCLASSEX;
wcex.style = CS_VREDRAW | CS_HREDRAW; //CS_HREDRAW当窗口水平方向的宽度变化时重绘整个窗口.CS_VREDRAW 当窗口垂直方向的宽度变化时重绘整个窗口.
wcex.hInstance = hInstance; //应用程序实例
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.lpfnWndProc = WndProc; //窗口过程函数
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINHELLO)); //图标
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //光标,LoadCursor第一个参数为NULL,表示采用系统默认光标类型
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //背景颜色
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_WINHELLO); //菜单名称
wcex.lpszClassName = lpszWndClass; //窗口类名称
wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINHELLO));
return (RegisterClassEx(&wcex));
}
/**
* @brief 创建窗口
*/
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = createwindow(
lpszWndClass, lpszTitle,
WS_overlapPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
hInstance,
nullptr
);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
/**
* @brief 主窗口函数
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
//LOWORD(wParam)表示ID
WORD wId = LOWORD(wParam);
WORD wHi = HIWORD(wParam);
switch (wId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT),
hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
/**
* @brief 对话框函数,类似窗口函数
*/
INT_PTR CALLBACK About(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; //返回TRUE表示消息已处理
case WM_COMMAND:
{
if (LOWORD(wParam) == IDOK ||
LOWORD(wParam) == IDCANCEL)
{
EndDialog(hWnd, (INT_PTR)TRUE);
return (INT_PTR)TRUE;
}
break;
}
}
return (INT_PTR)FALSE; //返回FALSE表示消息未处理
}
解读:
1. 上面的代码,是一个完整的、最简单的Windows应用程序。其中涉及到的一些资源,菜单、字符串、图标、对话框、加速键等都是采用vs2015中的资源编辑器编辑,如下图所示,
2. 程序的起始和结束
a. 在注册窗口类后,程序调用CreateWindow,为程序建立了一个窗口,作为程序的主体界面。CreateWindow产生窗口之后,会送出WM_CREATE直接给窗口函数,后者于是可以在此时做些初始化操作;
b. 在程序运行的过程中,不断以GetMessage从消息队列中抓取消息。如果这个消息是WM_QUIT,GetMessage会传回0而结束while循环,进而结束整个程序;
c. DispatchMessage 通过Windows系统,把消息分派至窗口函数。消息将在该出被判别并处理;
d. 程序不断的进行b和c的动作;
e. 当使用者按下系统菜单中的Close命令项时,系统送出WM_CLOSE。通常程序的窗口函数不会拦截此消息,于是DefWindowProc处理它;
f. DefWindowProc收到WM_CLOSE后,调用DestroyWindow把窗口清除。DestroyWindow本身又会送出WM_DESTROY消息;
g. 程序对WM_DESTROY的标准反应是调用PostQuitMessage;
h. PostQuitMessage 没有什么其他操作,就只送出WM_QUIT消息,准备让消息循环中的GetMessage取得,如步骤b,结束消息循环。
如果在窗口函数中拦截WM_DESTROY,但是不调用PostQuitMessage(0),在选择Close后,就会出现窗口消失了,但是应用程序本身并没有结束,因为消息循环没有结束。
3.GetMessage和PeekMessage的区别?
- GetMessage的主要功能是从消息队列中“取出”消息,消息被取出以后,就从消息队列中将其删除;而PeekMessage的主要功能是“窥视”消息,如果有消息,就返回true,否则返回false。也可以使用PeekMessage从消息队列中取出消息,这要用到它的一个参数(UINT wRemoveMsg),如果设置为PM_REMOVE,消息则被取出并从消息队列中删除;如果设置为PM_NOREMOVE,消息就不会从消息队列中取出。
- 如果GetMessage从消息队列中取不到消息,则线程就会被操作系统挂起,等到OS重新调度该线程时,两者的性质不同:使用GetMessage线程仍会被挂起,使用PeekMessage线程会得到cpu的控制权,运行一段时间。
- GetMessage每次都会等待消息,直到取到消息才返回;而PeekMessage只是查询消息队列,没有消息就立即返回,从返回值判断是否取到了消息。
相关阅读
大部分程序员入门的第一个小程序都是Hello World,还记得当时大一的时候,我室友还特意为了这个发了个说说,现在想起来好有意思,哈
使用加密狗进行程序的加密保护是非常成熟的加密方案。目前市面上比较常见的加密锁:深思的精锐5,威步,safenet。国内比较老牌的加密锁
GTK+简介 GTK 最初为GIMP(一个图像处理软件,类似Photoshop)的工具包 后来GTK重写为面向对象的GTK+GTK+ 并非是用C++写的,而是用C语言
将微软网盘,做成一个共享网站,好处就是可以分享大文件,而且还不限制下载速度。如果你受够了百度网盘的限速,可以试试这个!来看看常见的
[摘要]虽然人工智能正在不断地发展,但是它还没有发展到具备“语言加工能力”的程度;没有人类编写的程序,它是绝对做不到自己创作的。