windows消息机制
1.创建基本的窗口程序
/*
基本的窗口程序的步骤:
1.注册窗口类(RegisterClass()),就像事件驱动一样,只有在相应的事件驱动里面注册了,分发的机制才会在事件触发时调用已经注册的对象,此对象成为实例对象hInstance
2.创建窗口( CreateWindow()),为实例对象hInstance创建一个窗口
3.显示窗口
4.接下来进入消息等待阶段,当所注册的事件被触发,那么在这个事件注册的过程将会被调用
*/
#include <windows.h>
#include <string>
#include <iostream>
#define WM_TEST_MSG (WM_USER+1)
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
//如果自己不进行处理就交给默认的处理函数处理,当然自己处理之后还是可以交给默认的处理函数
bool b_ret = false;
do {
switch (message) {
case WM_TEST_MSG: {
int recv_data = (int)wParam;
std::cout << "recv_data:" << recv_data;
return (LRESULT)recv_data;//将接收的数据返回给sendMessage
break;
}
case WM_PAINT:
break;
case WM_DESTROY: {
MessageBoxA(NULL, "exit", "tips", 0);
//b_ret = true;//这样就不交给默认处理函数处理,如果需要默认函数处理这里进行注释就好
PostQuitMessage(0);//使用这个停止消息队列,不然只是关闭了窗口,但是消息队列仍然存在,程序未退出
break;
}
}
} while (false);
if (!b_ret) b_ret = DefWindowProc(hwnd, message, wParam, lParam); //默认的处理函数
return b_ret;
}
int main() {
static TCHAR szAppName[] = TEXT("MyWindow");
auto hInstance = GetModuleHandleA(NULL);
HWND hwnd;
MSG msg;
WNDCLASS wndclass; //声明一个窗口类对象
//以下为窗口类对象wndclass的属性
wndclass.lpfnWndProc = WndProc; //窗口处理函数
wndclass.hInstance = hInstance; //窗口实例句柄
wndclass.lpszClassName = szAppName; //窗口类名
wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口样式
wndclass.lpszMenuName = NULL; //窗口菜单:无
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //窗口背景颜色
wndclass.cbWndExtra = 0; //窗口实例扩展:无
wndclass.cbClsExtra = 0; //窗口类扩展:无
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //窗口最小化图标:使用缺省图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //窗口采用箭头光标
if (!RegisterClassW(&wndclass))
{ //注册窗口类, 如果注册失败弹出错误提示
MessageBox(NULL, TEXT("窗口注册失败"), TEXT("错误"), MB_OK | MB_ICONERROR);
return 0;
}
hwnd = CreateWindowW( //创建窗口
szAppName, //窗口类名
TEXT("test_windows"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口的风格
CW_USEDEFAULT, //窗口初始显示位置x:使用缺省值
CW_USEDEFAULT, //窗口初始显示位置y:使用缺省值
CW_USEDEFAULT, //窗口的宽度:使用缺省值
CW_USEDEFAULT, //窗口的高度:使用缺省值
NULL, //父窗口:无
NULL, //子菜单:无
hInstance, //该窗口应用程序的实例句柄
NULL
);
ShowWindow(hwnd, SW_NORMAL); //显示窗口
while (GetMessageW(&msg, nullptr, 0, 0)) //从消息队列中获取消息,null则获取该进程所有的窗口的消息
{
TranslateMessage(&msg); //将虚拟键消息转换为字符消息
DispatchMessage(&msg); //分发到回调函数(过程函数)
}
return msg.wParam;
}
2.消息传递的原理
(1)消息类型
<1> 所有线程产生时并没有消息队列,在第一次调用GDI时会产生线程消息队列.
<2> 队列消息到达系统队列消息,然后再到达线程队列消息.
<3> 非队列消息直接传递给窗口过程
(2)消息的传递
<1> 例如鼠标的单击,鼠标的驱动程序会把事件换成相应的消息,然后传到系统队列消息,系统会根据HWND,把消息传递到相应的窗口的线程队列消息.而进程使用GetMessage获取线程队列消息,然后进行翻译TranslateMessage,分发DispatchMessage,分发的过程就是将此消息传递给所有在进程这里注册的窗口类,然后就会触发窗口类绑定的回调函数
<2> 一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。
(3)相应的发送消息的函数SendMessage/PostMessage
<1>SendMessage 一直等到消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。如果是其他进程发送的则会加入到消息队列,等待执行,但是调用sendmessage的进程还是得继续等待返回.
<2> PostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。需要注意的是,如果hWnd参数为 HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMessage函数。
//程序1,SendMessagge
#include <iostream>
#include "log/logx.h"
#include <windows.h>
#include <unordered_map>
using namespace std;
#define WM_TEST_MSG (WM_USER+1)
int main(int argc, char** argv) {
std::string window_name("test_windows");
HWND window = FindWindowA(NULL, "test_windows");
LOGXD(VAR_DATA(window),VAR_DATA(window_name));
UINT value = 123;
auto ret= SendMessageA(window, WM_TEST_MSG,(WPARAM)value ,NULL);
LOGXD(VAR_DATA(ret));
return 0;
}
/*
[+] log construction, can use it C:\Desktop\code\test\exe/_log/log.html
[0]08:12:40[debg][9628] [main] window: 00E70506 window_name: test_windows (wtest_main.cpp:12)
[1]08:12:40[debg][9628] [main] ret: 123 (wtest_main.cpp:15)
[+] ~log deconstruction
*/
//程序2 接收消息的窗口(例子1.创建基本的窗口程序)
/*
recv_data:123
*/