首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

qt 消息处理机制深入分析 (Qt消息机制与window程序消息的对比)

  • 23-09-22 20:03
  • 3858
  • 7648
blog.csdn.net

理解Qt消息机制刻不容缓,那我们从对比传统的windows消息处理机制对比来说起;
只有知道QT底层的消息处理、对我们理解并学习Qt有很大帮助;
下面我将对windows程序与Qt对比,并在核心代码处并给出注释进行对比、方便学习。

注意重点看代码中的注视进行对比:!

注意重点看代码中的注视进行对比:!

注意重点看代码中的注视进行对比:!


一:windows程序的消息处理 
windows程序的处理大概一致

如下:

1.0 windows 消息处理机制:

  1. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  2. PSTR szCmdLine, int iCmdShow)
  3. {
  4. static TCHAR szAppName[] = TEXT("Hello");
  5. HWND hwnd;
  6. MSG msg;
  7. WNDCLASS wndclass;
  8. //fill wndclass
  9. wndclass.lpfnWndProc = WndProc;
  10. ...
  11. RegisterClass(&wndclass);
  12. hwnd = CreateWindow( .... ); // creation parameters
  13. ShowWindow(hwnd, iCmdShow);
  14. UpdateWindow(hwnd);
  15. while(GetMessage(&msg, NULL, 0, 0)) { //这一块位置得到消息
  16. TranslateMessage(&msg);//转换消息
  17. DispatchMessage(&msg);//分发消息到系统处理
  18. }
  19. return msg.wParam;
  20. }

1.1 这是分发消息的回调函数熟悉windows程序应该不难看懂

  1. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. HDC hdc;
  4. PAINTSTRUCT ps;
  5. RECT rect;
  6. switch(message) {
  7. case WM_CREATE:
  8. return 0;
  9. case WM_PAINT://重绘、比如窗口大小拉伸
  10. ...
  11. return 0;
  12. case WM_DESTROY:
  13. PostQuitMessage(0);
  14. return 0;
  15. }
  16. return DefWindowProc(hwnd, message, wParam, lParam); // windows 操作系统内部的消息机制、系统调用函数;
  17. }

二 Qt消息处理机制

2.0 Qt的消息机制


QEventDispatcherWin32:
注册窗口类别,并创建一个隐藏窗口 (QEventDispatcherWin32_Internal_WidgetXXXX)
窗口的回调函数 qt_internal_proc()
安装WH_GETMESSAGE类型的钩子函数 qt_GetMessageHook()

  1. bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
  2. if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) 与上面的消息循环:的while一样 、得到过滤所有消息
  3. {
  4. TranslateMessage(&msg);//转换消息
  5. DispatchMessage(&msg); //分发消息
  6. }

DispatchMessage(&msg); //分发消息
分发消息的或回调函数、这个与windows程序的CALLBACK WndProc一样
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)

2.1 下面就是从系统获得的消息后Qt封装消息后所作的事情

这个就是Qt的消息回调:

  1. LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
  2. {
  3. if (message == WM_NCCREATE)
  4. return true;
  5. // MSG windows 消息结构:
  6. MSG msg;
  7. msg.hwnd = hwnd;
  8. msg.message = message;
  9. msg.wParam = wp;
  10. msg.lParam = lp;
  11. QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
  12. long result;
  13. if (!dispatcher) {
  14. if (message == WM_TIMER)
  15. KillTimer(hwnd, wp);
  16. return 0;
  17. } else if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) {
  18. return result;
  19. }
  20. #ifdef GWLP_USERDATA
  21. QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  22. #else
  23. QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA);
  24. #endif
  25. QEventDispatcherWin32Private *d = 0;
  26. if (q != 0)
  27. d = q->d_func();
  28. // 下面 WM_QT_SOCKETNOTIFIER socket Qt 事件底层的处理机制、处理网络的消息事件
  29. if (message == WM_QT_SOCKETNOTIFIER) {
  30. // socket notifier message
  31. int type = -1;
  32. switch (WSAGETSELECTEVENT(lp)) { //在非阻塞模式下利用socket事件的消息机制,Server端与Client端之间的通信处于异步状态下
  33. case FD_READ: //socket 文件描述符 read 、有数据到达时发生
  34. case FD_ACCEPT: //socket 文件描述符 接收连接 、 作为客户端连接成功时发生
  35. type = 0;
  36. break;
  37. case FD_WRITE: //socket 文件描述符写 、有数据发送时产生
  38. case FD_CONNECT: //socket 文件描述符发起连接 、 作为服务端等待连接成功时发生
  39. type = 1;
  40. break;
  41. case FD_OOB: //socket 文件描述符收到数据 、 收到外带数据时发生
  42. type = 2;
  43. break;
  44. case FD_CLOSE: //socket 文件描述符关闭断开连接 、套接口关闭时发生
  45. type = 3;
  46. break;
  47. }
  48. if (type >= 0) {
  49. Q_ASSERT(d != 0);
  50. QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
  51. QSNDict *dict = sn_vec[type];
  52. QSockNot *sn = dict ? dict->value(wp) : 0;
  53. if (sn == nullptr) {
  54. d->postActivateSocketNotifiers();
  55. } else {
  56. Q_ASSERT(d->active_fd.contains(sn->fd));
  57. QSockFd &sd = d->active_fd[sn->fd];
  58. if (sd.selected) {
  59. Q_ASSERT(sd.mask == 0);
  60. d->doWsaAsyncSelect(sn->fd, 0);
  61. sd.selected = false;
  62. }
  63. d->postActivateSocketNotifiers();
  64. // Ignore the message if a notification with the same type was
  65. // received previously. Suppressed message is definitely spurious.
  66. const long eventCode = WSAGETSELECTEVENT(lp);
  67. if ((sd.mask & eventCode) != eventCode) {
  68. sd.mask |= eventCode;
  69. QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);
  70. QCoreApplication::sendEvent(sn->obj, &event);
  71. }
  72. }
  73. }
  74. return 0;
  75. } else if (message == WM_QT_ACTIVATENOTIFIERS) { // 处理postEvent事件
  76. Q_ASSERT(d != 0);
  77. // postEvent() 事件,因为是队列的存储的方式,邮递发送消息、直接返回
  78. // Postpone activation if we have unhandled socket notifier messages
  79. // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of
  80. // event processing.
  81. MSG msg;//异步调用
  82. if (!PeekMessage(&msg, d->internalHwnd,
  83. WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)
  84. && d->queuedSocketEvents.isEmpty()) { // d->queuedSocketEvents 消息队列如果消息有很多的时候都会加入这个队列、这个就是关于Qt::connect();参数:地址
  85. // register all socket notifiers
  86. for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();
  87. it != end; ++it) {
  88. QSockFd &sd = it.value();
  89. if (!sd.selected) {
  90. d->doWsaAsyncSelect(it.key(), sd.event);
  91. // allow any event to be accepted
  92. sd.mask = 0;
  93. sd.selected = true;
  94. }
  95. }
  96. }
  97. d->activateNotifiersPosted = false;
  98. return 0;
  99. } else if (message == WM_QT_SENDPOSTEDEVENTS // 处理 Qt sendPostEvent()发送事件
  100. // we also use a Windows timer to send posted events when the message queue is full
  101. // WM_QT_SENDPOSTEDEVENTS : 这个消息是我们Qt程序大部分走的事件,特殊情况除外。
  102. || (message == WM_TIMER
  103. && d->sendPostedEventsWindowsTimerId != 0
  104. && wp == (uint)d->sendPostedEventsWindowsTimerId)) {
  105. const int localSerialNumber = d->serialNumber.load();
  106. if (localSerialNumber != d->lastSerialNumber) {
  107. d->lastSerialNumber = localSerialNumber;
  108. q->sendPostedEvents();//因为sendevent是同步所以直接进入进行调用
  109. }
  110. return 0;
  111. } else if (message == WM_TIMER) {//系统定时器超时
  112. Q_ASSERT(d != 0);
  113. d->sendTimerEvent(wp);
  114. return 0;
  115. }
  116. return DefWindowProc(hwnd, message, wp, lp); // 这个与windows程序一样的地方。
  117. }

另外还有一点很重要的:大家都知道Qt消息处理比windows处理的块,Qt程序通过一通处理才调用DefWindowProc传给系统所以Qt系统的消息机制是比windows的消息慢的原因之一;
不过他也有它优点:qt的信号与槽函数让我们让我们编程更方便。

2.3 消息全局通知事件

另外我们都知道我们开发的系统、所有的消息都会到这个类QApplication::notify事件过滤所有的事件:
其实QApplication继承QGuiApplication类;

  1. bool QGuiApplication::notify(QObject *object, QEvent *event)
  2. {
  3. if (object->isWindowType())
  4. QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast(object), event);
  5. return QCoreApplication::notify(object, event);
  6. }

2.4 消息的组装

下面是所有的消息类型处理:在我们开发的系统中别人使用processEvent发送消息是比较高效的。
原因:这个消息直达经过的处理的少。

  1. void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
  2. {
  3. switch(e->type) {
  4. case QWindowSystemInterfacePrivate::FrameStrutMouse:
  5. case QWindowSystemInterfacePrivate::Mouse:
  6. QGuiApplicationPrivate::processMouseEvent(static_cast(e));
  7. break;
  8. case QWindowSystemInterfacePrivate::Wheel:
  9. QGuiApplicationPrivate::processWheelEvent(static_cast(e));
  10. break;
  11. case QWindowSystemInterfacePrivate::Key:
  12. QGuiApplicationPrivate::processKeyEvent(static_cast(e));
  13. break;
  14. case QWindowSystemInterfacePrivate::Touch:
  15. QGuiApplicationPrivate::processTouchEvent(static_cast(e));
  16. break;
  17. case QWindowSystemInterfacePrivate::GeometryChange:
  18. QGuiApplicationPrivate::processGeometryChangeEvent(static_cast(e));
  19. break;
  20. case QWindowSystemInterfacePrivate::Enter:
  21. QGuiApplicationPrivate::processEnterEvent(static_cast(e));
  22. break;
  23. case QWindowSystemInterfacePrivate::Leave:
  24. QGuiApplicationPrivate::processLeaveEvent(static_cast(e));
  25. break;
  26. case QWindowSystemInterfacePrivate::ActivatedWindow:
  27. QGuiApplicationPrivate::processActivatedEvent(static_cast(e));
  28. break;
  29. case QWindowSystemInterfacePrivate::WindowStateChanged:
  30. QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast(e));
  31. break;
  32. case QWindowSystemInterfacePrivate::WindowScreenChanged:
  33. QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast(e));
  34. break;
  35. case QWindowSystemInterfacePrivate::ApplicationStateChanged: {
  36. QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast(e);
  37. QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); }
  38. break;
  39. case QWindowSystemInterfacePrivate::FlushEvents: {
  40. QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast(e);
  41. QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); }
  42. break;
  43. case QWindowSystemInterfacePrivate::Close:
  44. QGuiApplicationPrivate::processCloseEvent(
  45. static_cast(e));
  46. break;
  47. case QWindowSystemInterfacePrivate::ScreenOrientation:
  48. QGuiApplicationPrivate::reportScreenOrientationChange(
  49. static_cast(e));
  50. break;
  51. case QWindowSystemInterfacePrivate::ScreenGeometry:
  52. QGuiApplicationPrivate::reportGeometryChange(
  53. static_cast(e));
  54. break;
  55. case QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInch:
  56. QGuiApplicationPrivate::reportLogicalDotsPerInchChange(
  57. static_cast(e));
  58. break;
  59. case QWindowSystemInterfacePrivate::ScreenRefreshRate:
  60. QGuiApplicationPrivate::reportRefreshRateChange(
  61. static_cast(e));
  62. break;
  63. case QWindowSystemInterfacePrivate::ThemeChange:
  64. QGuiApplicationPrivate::processThemeChanged(
  65. static_cast(e));
  66. break;
  67. case QWindowSystemInterfacePrivate::Expose:
  68. QGuiApplicationPrivate::processExposeEvent(static_cast(e));
  69. break;
  70. case QWindowSystemInterfacePrivate::Tablet:
  71. QGuiApplicationPrivate::processTabletEvent(
  72. static_cast(e));
  73. break;
  74. case QWindowSystemInterfacePrivate::TabletEnterProximity:
  75. QGuiApplicationPrivate::processTabletEnterProximityEvent(
  76. static_cast(e));
  77. break;
  78. case QWindowSystemInterfacePrivate::TabletLeaveProximity:
  79. QGuiApplicationPrivate::processTabletLeaveProximityEvent(
  80. static_cast(e));
  81. break;
  82. #ifndef QT_NO_GESTURES
  83. case QWindowSystemInterfacePrivate::Gesture:
  84. QGuiApplicationPrivate::processGestureEvent(
  85. static_cast(e));
  86. break;
  87. #endif
  88. case QWindowSystemInterfacePrivate::PlatformPanel:
  89. QGuiApplicationPrivate::processPlatformPanelEvent(
  90. static_cast(e));
  91. break;
  92. case QWindowSystemInterfacePrivate::FileOpen:
  93. QGuiApplicationPrivate::processFileOpenEvent(
  94. static_cast(e));
  95. break;
  96. #ifndef QT_NO_CONTEXTMENU
  97. case QWindowSystemInterfacePrivate::ContextMenu:
  98. QGuiApplicationPrivate::processContextMenuEvent(
  99. static_cast(e));
  100. break;
  101. #endif
  102. case QWindowSystemInterfacePrivate::EnterWhatsThisMode:
  103. QGuiApplication::postEvent(QGuiApplication::instance(), new QEvent(QEvent::EnterWhatsThisMode));
  104. break;
  105. default:
  106. qWarning() << "Unknown user input event type:" << e->type;
  107. break;
  108. }
  109. }

2.5 常用的消息过滤事件

通过2.4组装的消息最终到达、此处。经过这些之后:之前的windows消息现在都已映射为QT类型的消息、进入Qt消息处理机制中来、才有了我们的消息过滤等等消息事件键盘鼠标等等;

  1. virtual void mouseDoubleClickEvent(QMouseEvent *event)
  2. virtual void mouseMoveEvent(QMouseEvent *event)
  3. virtual void mousePressEvent(QMouseEvent *event)
  4. virtual void mouseReleaseEvent(QMouseEvent *event)
  5. virtual void moveEvent(QMoveEvent *event)
  6. virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result)
  7. virtual void paintEvent(QPaintEvent *event)
  8. virtual void resizeEvent(QResizeEvent *event)
  9. virtual void showEvent(QShowEvent *event)
  10. virtual void tabletEvent(QTabletEvent *event)
  11. virtual void wheelEvent(QWheelEvent *event)

重新实现上面的消息事件做我们的功能;

总结:

通过windows消息的运行机制与Qt的消息机制进行了对比。其实Qt在windows平台上的消息传递依赖的是windows消息机制。只不过在开始时进行注册消息拦截等一些封装处理、加工成Qt消息在Qt程序中进行传递。

注:本文转载自blog.csdn.net的liuzhezhe111的文章"https://blog.csdn.net/liuzhezhe111/article/details/82154673"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top