zoukankan      html  css  js  c++  java
  • qt 消息处理机制深入分析 (Qt消息机制与window程序消息的对比)

    https://blog.csdn.net/liuzhezhe111/article/details/82154673

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

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

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

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

    一:windows程序的消息处理
    windows程序的处理大概一致
    如下:
    1.0 windows 消息处理机制:

        int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                PSTR szCmdLine, int iCmdShow)
        {
            static TCHAR szAppName[] = TEXT("Hello");
            HWND   hwnd;
            MSG    msg;
            WNDCLASS wndclass;
            //fill wndclass
            wndclass.lpfnWndProc  = WndProc;
            ...
            RegisterClass(&wndclass);
            hwnd = CreateWindow( .... );      // creation parameters
            ShowWindow(hwnd, iCmdShow);
            UpdateWindow(hwnd);
            while(GetMessage(&msg, NULL, 0, 0))  { //这一块位置得到消息
                TranslateMessage(&msg);//转换消息
                DispatchMessage(&msg);//分发消息到系统处理
            }
            return msg.wParam;
        }

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

        LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        {
            HDC hdc;
            PAINTSTRUCT ps;
            RECT rect;
            switch(message) {
                case WM_CREATE:
                    return 0;
                case WM_PAINT://重绘、比如窗口大小拉伸
                    ...
                    return 0;
                case WM_DESTROY:
                    PostQuitMessage(0);
                    return 0;
            }
            return DefWindowProc(hwnd, message, wParam, lParam); // windows 操作系统内部的消息机制、系统调用函数;
        }

    二 Qt消息处理机制
    2.0 Qt的消息机制


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

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

    DispatchMessage(&msg); //分发消息
    分发消息的或回调函数、这个与windows程序的CALLBACK WndProc一样
    LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
    2.1 下面就是从系统获得的消息后Qt封装消息后所作的事情

    这个就是Qt的消息回调:

        LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
        {
            if (message == WM_NCCREATE)
                return true;
            // MSG windows 消息结构:
            MSG msg;
            msg.hwnd = hwnd;
            msg.message = message;
            msg.wParam = wp;
            msg.lParam = lp;
            QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
            long result;
            if (!dispatcher) {
                if (message == WM_TIMER)
                    KillTimer(hwnd, wp);
                return 0;
            } else if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) {
                return result;
            }
         
        #ifdef GWLP_USERDATA
            QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        #else
            QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA);
        #endif
            QEventDispatcherWin32Private *d = 0;
            if (q != 0)
                d = q->d_func();
            // 下面 WM_QT_SOCKETNOTIFIER socket Qt 事件底层的处理机制、处理网络的消息事件
            if (message == WM_QT_SOCKETNOTIFIER) {
                // socket notifier message
                int type = -1;
                switch (WSAGETSELECTEVENT(lp)) { //在非阻塞模式下利用socket事件的消息机制,Server端与Client端之间的通信处于异步状态下
                case FD_READ: //socket 文件描述符 read 、有数据到达时发生
                case FD_ACCEPT: //socket 文件描述符 接收连接 、 作为客户端连接成功时发生
                    type = 0;
                    break;
                case FD_WRITE: //socket 文件描述符写 、有数据发送时产生
                case FD_CONNECT:  //socket 文件描述符发起连接 、 作为服务端等待连接成功时发生    
                    type = 1;
                    break;
                case FD_OOB: //socket 文件描述符收到数据 、 收到外带数据时发生
                    type = 2;
                    break;
                case FD_CLOSE: //socket 文件描述符关闭断开连接 、套接口关闭时发生
                    type = 3;
                    break;
                }
                if (type >= 0) {
                    Q_ASSERT(d != 0);
                    QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
                    QSNDict *dict = sn_vec[type];
         
                    QSockNot *sn = dict ? dict->value(wp) : 0;
                    if (sn == nullptr) {
                        d->postActivateSocketNotifiers();
                    } else {
                        Q_ASSERT(d->active_fd.contains(sn->fd));
                        QSockFd &sd = d->active_fd[sn->fd];
                        if (sd.selected) {
                            Q_ASSERT(sd.mask == 0);
                            d->doWsaAsyncSelect(sn->fd, 0);
                            sd.selected = false;
                        }
                        d->postActivateSocketNotifiers();
         
                        // Ignore the message if a notification with the same type was
                        // received previously. Suppressed message is definitely spurious.
                        const long eventCode = WSAGETSELECTEVENT(lp);
                        if ((sd.mask & eventCode) != eventCode) {
                            sd.mask |= eventCode;
                            QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);
                            QCoreApplication::sendEvent(sn->obj, &event);
                        }
                    }
                }
                return 0;
            } else if (message == WM_QT_ACTIVATENOTIFIERS) { // 处理postEvent事件
                Q_ASSERT(d != 0);
                // postEvent() 事件,因为是队列的存储的方式,邮递发送消息、直接返回
                
                // Postpone activation if we have unhandled socket notifier messages
                // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of
                // event processing.
                MSG msg;//异步调用
                if (!PeekMessage(&msg, d->internalHwnd,
                                 WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)
                    && d->queuedSocketEvents.isEmpty()) { // d->queuedSocketEvents 消息队列如果消息有很多的时候都会加入这个队列、这个就是关于Qt::connect();参数:地址
                    // register all socket notifiers
                    for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();
                         it != end; ++it) {
                        QSockFd &sd = it.value();
                        if (!sd.selected) {
                            d->doWsaAsyncSelect(it.key(), sd.event);
                            // allow any event to be accepted
                            sd.mask = 0;
                            sd.selected = true;
                        }
                    }
                }
                d->activateNotifiersPosted = false;
                return 0;
            } else if (message == WM_QT_SENDPOSTEDEVENTS  // 处理 Qt sendPostEvent()发送事件
                       // we also use a Windows timer to send posted events when the message queue is full
                       // WM_QT_SENDPOSTEDEVENTS : 这个消息是我们Qt程序大部分走的事件,特殊情况除外。
                       || (message == WM_TIMER
                           && d->sendPostedEventsWindowsTimerId != 0
                           && wp == (uint)d->sendPostedEventsWindowsTimerId)) {
                const int localSerialNumber = d->serialNumber.load();
                if (localSerialNumber != d->lastSerialNumber) {
                    d->lastSerialNumber = localSerialNumber;
                    q->sendPostedEvents();//因为sendevent是同步所以直接进入进行调用
                }
                return 0;
            } else if (message == WM_TIMER) {//系统定时器超时
                Q_ASSERT(d != 0);
                d->sendTimerEvent(wp);
                return 0;
            }
         
            return DefWindowProc(hwnd, message, wp, lp); // 这个与windows程序一样的地方。
        }

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

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

        bool QGuiApplication::notify(QObject *object, QEvent *event)
        {
            if (object->isWindowType())
                QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event);
            return QCoreApplication::notify(object, event);
        }

    2.4 消息的组装

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

        void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e)
        {
            switch(e->type) {
            case QWindowSystemInterfacePrivate::FrameStrutMouse:
            case QWindowSystemInterfacePrivate::Mouse:
                QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Wheel:
                QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Key:
                QGuiApplicationPrivate::processKeyEvent(static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Touch:
                QGuiApplicationPrivate::processTouchEvent(static_cast<QWindowSystemInterfacePrivate::TouchEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::GeometryChange:
                QGuiApplicationPrivate::processGeometryChangeEvent(static_cast<QWindowSystemInterfacePrivate::GeometryChangeEvent*>(e));
                break;
            case QWindowSystemInterfacePrivate::Enter:
                QGuiApplicationPrivate::processEnterEvent(static_cast<QWindowSystemInterfacePrivate::EnterEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Leave:
                QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ActivatedWindow:
                QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::WindowStateChanged:
                QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::WindowScreenChanged:
                QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ApplicationStateChanged: {
                QWindowSystemInterfacePrivate::ApplicationStateChangedEvent * changeEvent = static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e);
                QGuiApplicationPrivate::setApplicationState(changeEvent->newState, changeEvent->forcePropagate); }
                break;
            case QWindowSystemInterfacePrivate::FlushEvents: {
                QWindowSystemInterfacePrivate::FlushEventsEvent *flushEventsEvent = static_cast<QWindowSystemInterfacePrivate::FlushEventsEvent *>(e);
                QWindowSystemInterface::deferredFlushWindowSystemEvents(flushEventsEvent->flags); }
                break;
            case QWindowSystemInterfacePrivate::Close:
                QGuiApplicationPrivate::processCloseEvent(
                        static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ScreenOrientation:
                QGuiApplicationPrivate::reportScreenOrientationChange(
                        static_cast<QWindowSystemInterfacePrivate::ScreenOrientationEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ScreenGeometry:
                QGuiApplicationPrivate::reportGeometryChange(
                        static_cast<QWindowSystemInterfacePrivate::ScreenGeometryEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInch:
                QGuiApplicationPrivate::reportLogicalDotsPerInchChange(
                        static_cast<QWindowSystemInterfacePrivate::ScreenLogicalDotsPerInchEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ScreenRefreshRate:
                QGuiApplicationPrivate::reportRefreshRateChange(
                        static_cast<QWindowSystemInterfacePrivate::ScreenRefreshRateEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::ThemeChange:
                QGuiApplicationPrivate::processThemeChanged(
                            static_cast<QWindowSystemInterfacePrivate::ThemeChangeEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Expose:
                QGuiApplicationPrivate::processExposeEvent(static_cast<QWindowSystemInterfacePrivate::ExposeEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::Tablet:
                QGuiApplicationPrivate::processTabletEvent(
                            static_cast<QWindowSystemInterfacePrivate::TabletEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::TabletEnterProximity:
                QGuiApplicationPrivate::processTabletEnterProximityEvent(
                            static_cast<QWindowSystemInterfacePrivate::TabletEnterProximityEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::TabletLeaveProximity:
                QGuiApplicationPrivate::processTabletLeaveProximityEvent(
                            static_cast<QWindowSystemInterfacePrivate::TabletLeaveProximityEvent *>(e));
                break;
        #ifndef QT_NO_GESTURES
            case QWindowSystemInterfacePrivate::Gesture:
                QGuiApplicationPrivate::processGestureEvent(
                            static_cast<QWindowSystemInterfacePrivate::GestureEvent *>(e));
                break;
        #endif
            case QWindowSystemInterfacePrivate::PlatformPanel:
                QGuiApplicationPrivate::processPlatformPanelEvent(
                            static_cast<QWindowSystemInterfacePrivate::PlatformPanelEvent *>(e));
                break;
            case QWindowSystemInterfacePrivate::FileOpen:
                QGuiApplicationPrivate::processFileOpenEvent(
                            static_cast<QWindowSystemInterfacePrivate::FileOpenEvent *>(e));
                break;
        #ifndef QT_NO_CONTEXTMENU
                case QWindowSystemInterfacePrivate::ContextMenu:
                QGuiApplicationPrivate::processContextMenuEvent(
                            static_cast<QWindowSystemInterfacePrivate::ContextMenuEvent *>(e));
                break;
        #endif
            case QWindowSystemInterfacePrivate::EnterWhatsThisMode:
                QGuiApplication::postEvent(QGuiApplication::instance(), new QEvent(QEvent::EnterWhatsThisMode));
                break;
            default:
                qWarning() << "Unknown user input event type:" << e->type;
                break;
            }
        }

    2.5 常用的消息过滤事件

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

        virtual void mouseDoubleClickEvent(QMouseEvent *event)
        virtual void mouseMoveEvent(QMouseEvent *event)
        virtual void mousePressEvent(QMouseEvent *event)
        virtual void mouseReleaseEvent(QMouseEvent *event)
        virtual void moveEvent(QMoveEvent *event)
        virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result)
        virtual void paintEvent(QPaintEvent *event)
        virtual void resizeEvent(QResizeEvent *event)
        virtual void showEvent(QShowEvent *event)
        virtual void tabletEvent(QTabletEvent *event)
        virtual void wheelEvent(QWheelEvent *event)

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

    总结:

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

  • 相关阅读:
    201621123028《Java程序设计》第二周学习总结
    tensorflow——乘法
    最近做的事儿
    BlurUse Compute Shader
    又翻出来老电视剧看了看....
    exercise: toon shader
    semantic SV_
    SimpleDateFormat使用详解
    各种数字类型转换成字符串型:
    android 代码混淆配置
  • 原文地址:https://www.cnblogs.com/zhoug2020/p/14330639.html
Copyright © 2011-2022 走看看