qt5.9.8源码:windows下获取消息进行消息循环

事件分发器创建:

 在

 C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp

这里每一个事件分发器创建后都会创建一个内部窗口,这个窗口虽然不显示,但是却是非常重要的,一些自定义消息都是在这个窗口的过程回调中处理

void QEventDispatcherWin32::createInternalHwnd()
{
    Q_D(QEventDispatcherWin32);

    if (d->internalHwnd)
        return;
    d->internalHwnd = qt_create_internal_window(this);

    installMessageHook();

    // start all normal timers
    for (int i = 0; i < d->timerVec.count(); ++i)
        d->registerTimer(d->timerVec.at(i));
}

注册窗口类别,并创建一个隐藏窗口 :大小为0x0   classname: QEventDispatcherWin32_Internal_Widget

QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext()
    : atom(0), className(0)
{
    // make sure that multiple Qt's can coexist in the same process
    const QString qClassName = QStringLiteral("QEventDispatcherWin32_Internal_Widget")
        + QString::number(quintptr(qt_internal_proc));
    className = new wchar_t[qClassName.size() + 1];
    qClassName.toWCharArray(className);
    className[qClassName.size()] = 0;

    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = qt_internal_proc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(0);
    wc.hIcon = 0;
    wc.hCursor = 0;
    wc.hbrBackground = 0;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;
    atom = RegisterClass(&wc);
    if (!atom) {
        qErrnoWarning("%s RegisterClass() failed", qPrintable(qClassName));
        delete [] className;
        className = 0;
    }
}

static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
    QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
    if (!ctx->atom)
        return 0;
    HWND wnd = CreateWindow(ctx->className,    // classname
                            ctx->className,    // window name
                            0,                 // style
                            0, 0, 0, 0,        // geometry
                            HWND_MESSAGE,            // parent   创建一个单纯的消息窗口,可以提供 HWND_MESSAGE 或提供一个己存在的消息窗口的句柄
                            0,                 // menu handle
                            GetModuleHandle(0),     // application
                            0);                // windows creation data.

    if (!wnd) {
        qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
        return 0;
    }

#ifdef GWLP_USERDATA
    SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher);
#else
    SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher);
#endif

    return wnd;
}


窗口的回调函数 qt_internal_proc()

LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
    if (message == WM_NCCREATE)
        return true;

    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();

    if (message == WM_QT_SOCKETNOTIFIER) {
        // socket notifier message
        int type = -1;
        switch (WSAGETSELECTEVENT(lp)) {
        case FD_READ:
        case FD_ACCEPT:
            type = 0;
            break;
        case FD_WRITE:
        case FD_CONNECT:
            type = 1;
            break;
        case FD_OOB:
            type = 2;
            break;
        case FD_CLOSE:
            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) {
        Q_ASSERT(d != 0);

        // 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()) {
            // 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
               // we also use a Windows timer to send posted events when the message queue is full
               || (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();
        }
        return 0;
    } else if (message == WM_TIMER) {
        Q_ASSERT(d != 0);
        d->sendTimerEvent(wp);
        return 0;
    }

    return DefWindowProc(hwnd, message, wp, lp);
}

这里处理内部创建

socket消息,也是Qt内部自定义的消息WM_QT_SOCKETNOTIFIER
postEvent到一些对象的消息,也是自定义消息WM_QT_SENDPOSTEDEVENTS
通过QObject注册的定时器消息WM_TIMER


安装WH_GETMESSAGE类型的钩子函数 qt_GetMessageHook()

void QEventDispatcherWin32::installMessageHook()
{
    Q_D(QEventDispatcherWin32);

    if (d->getMessageHook)
        return;

    // setup GetMessage hook needed to drive our posted events
    d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());Windows消息分为队列消息和非队列消息,WH_GETMESSAGE不能取到非队列消息,用SendMessage发送的消息不经过队列,用PostMessage发送的消息被放入队列,
即,WH_GETMESSAGE Hook只拦截由GetMessage or PostMessage PeekMessage的队列消息。
    if (Q_UNLIKELY(!d->getMessageHook)) {
        int errorCode = GetLastError();
        qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %s",
               errorCode, qPrintable(qt_error_string(errorCode)));
    }
}
LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
{
    QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
    Q_ASSERT(q != 0);

    if (wp == PM_REMOVE) {
        if (q) {
            MSG *msg = (MSG *) lp;
            QEventDispatcherWin32Private *d = q->d_func();
            const int localSerialNumber = d->serialNumber.load();
            static const UINT mask = inputTimerMask();
            if (HIWORD(GetQueueStatus(mask)) == 0) {
                // no more input or timer events in the message queue, we can allow posted events to be sent normally now
                if (d->sendPostedEventsWindowsTimerId != 0) {
                    // stop the timer to send posted events, since we now allow the WM_QT_SENDPOSTEDEVENTS message
                    KillTimer(d->internalHwnd, d->sendPostedEventsWindowsTimerId);
                    d->sendPostedEventsWindowsTimerId = 0;
                }
                (void) d->wakeUps.fetchAndStoreRelease(0);
                if (localSerialNumber != d->lastSerialNumber
                    // if this message IS the one that triggers sendPostedEvents(), no need to post it again
                    && (msg->hwnd != d->internalHwnd
                        || msg->message != WM_QT_SENDPOSTEDEVENTS)) {
                    PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
                }
            } else if (d->sendPostedEventsWindowsTimerId == 0
                       && localSerialNumber != d->lastSerialNumber) {
                // start a special timer to continue delivering posted events while
                // there are still input and timer messages in the message queue
                d->sendPostedEventsWindowsTimerId = SetTimer(d->internalHwnd,
                                                             SendPostedEventsWindowsTimerId,
                                                             0, // we specify zero, but Windows uses USER_TIMER_MINIMUM
                                                             NULL);
                // we don't check the return value of SetTimer()... if creating the timer failed, there's little
                // we can do. we just have to accept that posted events will be starved
            }
        }
    }
    return q->d_func()->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0;
}

事件循环

最终转成事件直接发送到相应的对象处理。

 现在有了事件分发器这个消息处理发动机,发动机是如何启动的呢?
在QApplication的exec()或者QEventLoop的exec()启动了事件循环

windows窗口所发生的一切都是通过消息传给窗口过程,然后窗口过程以某种形式对消息做出反应,或是把消息传递给DefWindowProc进行默认处理。windows的每个窗口线程都有各自的消息队列,线程可以循环的获取队列中的消息:

while(GetMessage(&msg,NULL,0,0))
  {
    TranlateMessage(&msg);
    DispatchMessage(&msg); //--->调用窗口过程
  }

 调用顺序

代码:

#include "vsQT.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    vsQT w;
    w.show();
    int  ret = a.exec();
    return ret;
}

在 int ret = a.exec()中加入断点

int QApplication::exec()

/*!
    Enters the main event loop and waits until exit() is called, then returns
    the value that was set to exit() (which is 0 if exit() is called via
    quit()).

    It is necessary to call this function to start event handling. The main
    event loop receives events from the window system and dispatches these to
    the application widgets.

    Generally, no user interaction can take place before calling exec(). As a
    special case, modal widgets like QMessageBox can be used before calling
    exec(), because modal widgets call exec() to start a local event loop.

    To make your application perform idle processing, i.e., executing a special
    function whenever there are no pending events, use a QTimer with 0 timeout.
    More advanced idle processing schemes can be achieved using processEvents().

    We recommend that you connect clean-up code to the
    \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in your
    application's \c{main()} function. This is because, on some platforms the
    QApplication::exec() call may not return. For example, on the Windows
    platform, when the user logs off, the system terminates the process after Qt
    closes all top-level windows. Hence, there is \e{no guarantee} that the
    application will have time to exit its event loop and execute code at the
    end of the \c{main()} function, after the QApplication::exec() call.

    \sa quitOnLastWindowClosed, QCoreApplication::quit(), QCoreApplication::exit(),
        QCoreApplication::processEvents(), QCoreApplication::exec()
*/
int QApplication::exec()
{
    return QGuiApplication::exec();
}


int QGuiApplication::exec()
{
#ifndef QT_NO_ACCESSIBILITY
    QAccessible::setRootObject(qApp);
#endif
    return QCoreApplication::exec();
}

        进入主事件循环并等待,直到调用exit(),然后返回设置为exit()的值(如果通过quit()调用exit(),则该值为0)。有必要调用此函数来启动事件处理。主事件循环从窗口系统接收事件,并将这些事件分派给应用程序小部件。一般来说,在调用exec()之前不会发生任何用户交互。作为一种特殊情况,像QMessageBox这样的模态小部件可以在调用exec()之前使用,因为模态小部件调用exec()来启动本地事件循环。要使您的应用程序执行空闲处理,即在没有挂起事件时执行一个特殊的函数,请使用超时为0的QTimer。可以使用processEvents()实现更高级的空闲处理方案。我们建议您将清理代码连接到aboutToQuit()信号,而不是将其放在应用程序的main()函数中。这是因为,在某些平台上,QApplication::exec()调用可能不会返回。例如,在Windows平台上,当用户注销时,系统会在Qt关闭所有顶级窗口后终止进程。因此,不能保证应用程序有时间退出其事件循环,并在调用QApplication::exec()之后在main()函数的末尾执行代码。
 

int QCoreApplication::exec()

/*****************************************************************************
  Main event loop wrappers
 *****************************************************************************/

/*!
    Enters the main event loop and waits until exit() is called.  Returns
    the value that was passed to exit() (which is 0 if exit() is called via
    quit()).

    It is necessary to call this function to start event handling. The
    main event loop receives events from the window system and
    dispatches these to the application widgets.

    To make your application perform idle processing (by executing a
    special function whenever there are no pending events), use a
    QTimer with 0 timeout. More advanced idle processing schemes can
    be achieved using processEvents().

    We recommend that you connect clean-up code to the
    \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in
    your application's \c{main()} function because on some platforms the
    exec() call may not return. For example, on Windows
    when the user logs off, the system terminates the process after Qt
    closes all top-level windows. Hence, there is no guarantee that the
    application will have time to exit its event loop and execute code at
    the end of the \c{main()} function after the exec()
    call.

    \sa quit(), exit(), processEvents(), QApplication::exec()
*/
int QCoreApplication::exec()
{
    if (!QCoreApplicationPrivate::checkInstance("exec"))//如果在执行状态,则返回错误
        return -1;

    QThreadData *threadData = self->d_func()->threadData;//返回主线程的线程id
    if (threadData != QThreadData::current()) {  当前线程和主线程的id不一样。则报错显示当前线程类的不是从主线程调用的
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    if (!threadData->eventLoops.isEmpty()) {//判断当前事件循环不是空的,则报错
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }

    threadData->quitNow = false;//设置主线程的立即停止位false
    QEventLoop eventLoop; //定义新的事件循环 
    self->d_func()->in_exec = true;/将当前的事件循环的(正在执行置为true),这样当再次执行改程序时,可判断当前正在执行状态中,便可在第一个if中返回
    self->d_func()->aboutToQuitEmitted = false;//将事件循环的将要停止发射设置为false,使得可以发射信号
    int returnCode = eventLoop.exec();//进入事件循环
    threadData->quitNow = false;

    if (self)
        self->d_func()->execCleanup();

    return returnCode;
}

C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\corelib\kernel\qeventloop.cpp

int QEventLoop::exec(ProcessEventsFlags flags)

/*!
    Enters the main event loop and waits until exit() is called.
    Returns the value that was passed to exit().

    If \a flags are specified, only events of the types allowed by
    the \a flags will be processed.

    It is necessary to call this function to start event handling. The
    main event loop receives events from the window system and
    dispatches these to the application widgets.

    Generally speaking, no user interaction can take place before
    calling exec(). As a special case, modal widgets like QMessageBox
    can be used before calling exec(), because modal widgets
    use their own local event loop.

    To make your application perform idle processing (i.e. executing a
    special function whenever there are no pending events), use a
    QTimer with 0 timeout. More sophisticated idle processing schemes
    can be achieved using processEvents().

    \sa QCoreApplication::quit(), exit(), processEvents()
*/
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    //we need to protect from race condition with QThread::exit
    QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex);//获取当前主线程的锁
    if (d->threadData->quitNow)///如果线程的状态为停止,则返回
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }

    struct LoopReference {
        QEventLoopPrivate *d;定义事件循环指针
        QMutexLocker &locker; //定义一个锁

        bool exceptionCaught;
        LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
        {
            d->inExec = true; // 将事件循环状态设置为正在执行中
            d->exit.storeRelease(false);// 将事件停止设置为false 
            ++d->threadData->loopLevel; //将当前事件的优先级加1
            d->threadData->eventLoops.push(d->q_func());  // 将事件加入线程事件循环中
            locker.unlock();//   解锁
        }

        ~LoopReference()
        {
            if (exceptionCaught) {
                qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                         "exceptions from an event handler is not supported in Qt.\n"
                         "You must not let any exception whatsoever propagate through Qt code.\n"
                         "If that is not possible, in Qt 5 you must at least reimplement\n"
                         "QCoreApplication::notify() and catch all exceptions there.\n");
            }
            locker.relock();
            QEventLoop *eventLoop = d->threadData->eventLoops.pop();
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --d->threadData->loopLevel;
        }
    };
    LoopReference ref(d, locker);

    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())   //判断app的线程和事件线程是否为一个线程
        QCoreApplication::removePostedEvents(app, QEvent::Quit);

    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);//进行事件分发

    ref.exceptionCaught = false;
    return d->returnCode.load();
}

bool QEventLoop::processEvents(ProcessEventsFlags flags)

void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher.load())
        return;

    QElapsedTimer start;
    start.start();
    while (processEvents(flags & ~WaitForMoreEvents)) {
        if (start.elapsed() > maxTime)
            break;
    }
}

/*!
    Processes pending events that match \a flags until there are no
    more events to process. Returns \c true if pending events were handled;
    otherwise returns \c false.

    This function is especially useful if you have a long running
    operation and want to show its progress without allowing user
    input; i.e. by using the \l ExcludeUserInputEvents flag.

    This function is simply a wrapper for
    QAbstractEventDispatcher::processEvents(). See the documentation
    for that function for details.
*/
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher.load())
        return false;
调用不同平台下的事件分派器来处理事件
    return d->threadData->eventDispatcher.load()->processEvents(flags);
}

C:\Qt\Qt5.9.8\5.9.8\Src\qtbase\src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp 调用:最后还是事件分发器一直处理事件:

bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    const QEventLoop::ProcessEventsFlags oldFlags = m_flags;
    m_flags = flags;
    const bool rc = QEventDispatcherWin32::processEvents(flags);
    m_flags = oldFlags;
    return rc;
}

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd) {
        createInternalHwnd();//内部数据创建。registerClass注册窗口类,createwindow创建窗体。
    //注册socket notifiers,启动所有的normal timers
        wakeUp(); // trigger a call to sendPostedEvents()
    }

    d->interrupt = false;
    emit awake();

    bool canWait;
    bool retVal = false;
    bool seenWM_QT_SENDPOSTEDEVENTS = false;
    bool needWM_QT_SENDPOSTEDEVENTS = false;
    do {
        DWORD waitRet = 0;
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);

            MSG msg;
            bool haveMessage;
            // // process queued user input events处理用户输入事件,放入队列中
            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // process queued user input events
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {/ process queued socket events 处理socket事件,放入队列中
                // process queued socket events
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {
                //从消息队列中取消息,同PeekMessage
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage) {
                    if ((flags & QEventLoop::ExcludeUserInputEvents)
                        && ((msg.message >= WM_KEYFIRST
                             && msg.message <= WM_KEYLAST)
                            || (msg.message >= WM_MOUSEFIRST
                                && msg.message <= WM_MOUSELAST)
                            || msg.message == WM_MOUSEWHEEL
                            || msg.message == WM_MOUSEHWHEEL
                            || msg.message == WM_TOUCH
#ifndef QT_NO_GESTURES
                            || msg.message == WM_GESTURE
                            || msg.message == WM_GESTURENOTIFY
#endif
                            || msg.message == WM_CLOSE)) {
                        // queue user input events for later processing
                        d->queuedUserInputEvents.append(msg);
                        continue;
                    }
                    if ((flags & QEventLoop::ExcludeSocketNotifiers)
                        && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                        // queue socket events for later processing
                        d->queuedSocketEvents.append(msg);
                        continue;
                    }
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects
                for (int i=0; i<(int)nCount; i++)
                    pHandles[i] = d->winEventNotifierList.at(i)->handle();
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
                    // a new message has arrived, process it
                    continue;
                }
            }

 //事件队列中有事件需要处理。
            if (haveMessage) {
                // WinCE doesn't support hooks at all, so we have to call this by hand :(
                if (!d->getMessageHook)
                    (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);

                if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
                    if (seenWM_QT_SENDPOSTEDEVENTS) {
                        // when calling processEvents() "manually", we only want to send posted
                        // events once
                        needWM_QT_SENDPOSTEDEVENTS = true;
                        continue;
                    }
                    seenWM_QT_SENDPOSTEDEVENTS = true;
                } else if (msg.message == WM_TIMER) {
                    // avoid live-lock by keeping track of the timers we've already sent
                    bool found = false;
                    for (int i = 0; !found && i < processedTimers.count(); ++i) {
                        const MSG processed = processedTimers.constData()[i];
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                    }
                    if (found)
                        continue;
                    processedTimers.append(msg);
                } else if (msg.message == WM_QUIT) {//退出事件
                    if (QCoreApplication::instance())
                        QCoreApplication::instance()->quit();
                    return false;
                }
                //消息分发处理。
                if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            } else if (waitRet - WAIT_OBJECT_0 < nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        canWait = (!retVal
                   && !d->interrupt
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
            for (int i=0; i<(int)nCount; i++)
                pHandles[i] = d->winEventNotifierList.at(i)->handle();

            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
            emit awake();
            if (waitRet - WAIT_OBJECT_0 < nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
                retVal = true;
            }
        }
    } while (canWait);

    if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {
        // when called "manually", always send posted events
        sendPostedEvents();
    }

    if (needWM_QT_SENDPOSTEDEVENTS)
        PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);

    return retVal;
}

注意到关键的Windows消息循环的API:
PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
从消息队列里面取消息
TranslateMessage(&msg);
转化消息
DispatchMessage(&msg);
分发消息到窗口过程函数

 事件循环:

事件循环一般以exec调用开始,例如QApplication::exec()、QDialog::exec()、QMenu::exec()…,其实他们最后都依赖于QEventLoop来创建一个事件循环:

void test()
{
    QEventLoop loop;
...
    loop.exec(); 
}

上面的loop就是一个嵌套的事件循环,loop的嵌套使得test函数在loop结束之前不会返回,函数栈也就一直存在,所以在函数体中创建的栈对象得以长时间的存在。

局部的QEventLoop和QApplication创建的QEventLoop的功能是没差别的,局部的事件循环嵌套在上一层的事件循环中,可以替代外层的循环处理事件:

  class MyClass : public QWidget
  {
    Q_OBJECT //如果不需要信号和槽功能,则可以将Q_OBJECT宏去掉
  public:
    MyClass(QWidget *parent = 0):QWidget(parent){}
  protected:
    void mousePressEvent(QMouseEvent * event)
        {
          static int level = 0;
          m_label.setText(QString("Enter : %1").arg(++level));
        }
  private:
    QLabel m_label;
  };


  class Widget : public  QWidget
  {
  public:
    Widget(QWidget *_p = nullptr) :QWidget(_p){ }

  protected:

    void mousePressEvent(QMouseEvent *e)
    {
        static int level = 0;
        m_label.setText(QString("Enter : %1").arg(++level));
        //创建并启动一个局部的事件循环作为线程当前的事件循环
        QEventLoop loop;
        loop.exec();
    }
  QLabel m_label;
  };

  int main(int argc, char *argv[])
  {
    QApplication a(argc, argv);
    MyClass w;
    Widget cw(&w);
    w.show();
    return a.exec();
  }

上面的程序创建了两个窗口,在点击窗口cw之前,只有一个最外层的循环,是QApplication创建的。这时点击w产生的QMouseEvent是通过这个循环传递给w的。第一次点击cw时产生的QMouseEvent也是这个循环传递的。之后,点击w获得的QMouseEvent则是来自于cw的mousePressEvent创建的局部事件循环。

QEventLoop 做了什么

先看构造函数:

QEventLoop::QEventLoop(QObject *parent)
    : QObject(*new QEventLoopPrivate, parent)
{
    Q_D(QEventLoop);
    //QApplication是所有线程共享的对象,全局且唯一  
    if (!QCoreApplication::instance()) {
        qWarning("QEventLoop: Cannot be used without QApplication");
    } else if (!d->threadData->eventDispatcher.load()) {
    //如果当前线程还没有事件派发器,那就创建一个
        QThreadPrivate::createEventDispatcher(d->threadData);
    }
}

        一,QApplication是所有线程共享的对象,全局且唯一 ;

        二,一个线程有且只有一个eventDispatcher,如果不存在,则创建一个。而且,由于QEventLoop不能在QApplication之前创建,所以,如果QEventLoop是在GUI线程中构造,那么eventDispatcher早在QApplication构造时就被创建了,所以免了自己创建eventDispatcher的步骤。如果QEventLoop是在非GUI线程中构造呢?这种情况肯定是存在的,因为非GUI线程可能也需要处理事件,这些事件不是来自可见窗口,而是来自自己或其他线程。例如,使用跨线程的信号和槽。下面看看在非GUI下的QEventLoop:

class MyThread :public QThread
{
    void run() Q_DECL_OVERRIDE{
        //start a event loop
        this->exec();
    }

};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyThread t;
    t.start();

    return a.exec();
}

        现在,程序有两个线程,一个是GUI线程,一个是线程t。GUI线程有一个事件循环,在a.exec中创建并启动,线程t也有一个事件循环,在t.exec中创建并启动。

        就像在GUI线程中的事件循环需要使用一个事件派发器一样,任何一个线程中的事件循环都需要一个派发器。GUI线程中的事件派发器是在构造QApplication时创建的,是一个QWindowsGuiEventDispatcher类的派发器,在这个派发器的构造函数中同时还创建了一个message-only窗口。

        对于需要事件循环的非GUI线程,message-only窗口是不可或缺的,因为没有他,线程就没有消息队列,何谈消息循环,除非Qt使用另外的机制而非消息循环机制来支持非GUI线程的事件循环,不过这完全没必要。我们来看看这些步骤在非GUI线程中是怎么完成的:

        第一步,创建一个事件派发器

事件派发器在t.start中被创建:

·

unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
{
    QThread *thr = reinterpret_cast<QThread *>(arg);
    QThreadData *data = QThreadData::get2(thr);

    qt_create_tls();
    TlsSetValue(qt_current_thread_data_tls_index, data);
    data->threadId = reinterpret_cast<Qt::HANDLE>(GetCurrentThreadId());
  ...
    if (data->eventDispatcher.load()) // custom event dispatcher set?
        data->eventDispatcher.load()->startingUp();
    else
        createEventDispatcher(data); //创建事件派发器
  ...
}

        创建的是一个QEventDispatcherWin32类的事件派发器,它并不像QWindowsGuiEventDispatcher一样在构造的同时还创建 message-only 窗口。

第二步,创建一个 message-only 窗口

        如果在启动事件循环的过程中发现当前的事件派发器还没有创建 message-only 窗口的话,那就会为其创建一个这样的窗口。

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
      Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd) {
        createInternalHwnd();
        wakeUp(); // trigger a call to sendPostedEvents()
    }
 ...
}

        对比GUI线程创建事件派发器和message-only窗口的一步到位,非GUI线程采用延迟的方式来处理。为什么要这样做呢?像GUI线程一步到位不行吗?当然可以,但是没必要,因为创建一个 message-only 窗口是要占用内核资源的,GUI线程一定需要一个消息循环来实现事件循环,所以一步到位的创建没毛病,但是非GUI线程可能根本就不需要一个事件循环,所以,白白浪费资源干嘛呢?

跨线程的信号和槽与事件循环

#include "Mythread.h"
#include "testUI.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyThread t;//线程t,在gui线程中创建

    TestUI ui; //ui界面,在gui线程中创建
    QObject::connect(&ui, SIGNAL(sendChangedeSignal(int)), 
                     &t, SLOT(updateNumber(int)));
    QObject::connect(&t, SIGNAL(sendChangedeSignal(int)), 
                     &ui, SLOT(updateNumber(int)));
    ui.show();

    //启动线程
    t.start();

    return a.exec();
}

        跨线程的槽调用不是同步的,而是异步的调用,保证了一个对象的槽函数一定会在这个对象所属的线程中执行,而不是其他线程。这里ob属于t线程,所以ob的槽函数就是在t线程中执行的。

        实际上,跨线程的槽是以事件的形式异步调用的,调用事件的派发使用;

void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)

完成,这个函数将从这个线程的post事件队列(不是线程的消息队列而是QThreadData::postEventList)中取出事件,然后派送给出去
bool QObject::event(QEvent *e)
{
  ....
      case QEvent::MetaCall:
        {
            QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);

            QConnectionSenderSwitcher sw(this, const_cast<QObject*>(mce->sender()), mce->signalId());

            //转到qt_static_metacall,根据下标调用相应的槽函数
            mce->placeMetaCall(this); 
            break;
        }

  ...
}


void QMetaCallEvent::placeMetaCall(QObject *object)
{
    if (slotObj_) {
        slotObj_->call(object, args_);
    } else if (callFunction_ && method_offset_ <= object->metaObject()->methodOffset()) {
        //调用qt_static_metacall
        callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
    } else {
        QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
    }
}


void TestOb::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        TestOb *_t = static_cast<TestOb *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->sendChangedeSignal((*reinterpret_cast< int(*)>(_a[1]))); break;
         //调用updateNumber  
        case 1: _t->updateNumber((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (TestOb::*_t)(int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestOb::sendChangedeSignal)) {
                *result = 0;
            }
        }
    }
}

那么qt在什么地方决定这个信号槽调用要以事件的形式进行异步而不是同步的调用呢?毕竟我们没有明确的告诉qt这个信号槽的链接是异步的啊。这就要从信号的发射说起,信号发射的主要实现在这里:
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
  ...
    ConnectionListsRef connectionLists = sender->d_func()->connectionLists;
    const QObjectPrivate::ConnectionList *list;
    list = &connectionLists->at(signal_index);

  do {
     // !
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;
    do {
     // !
         if (!c->receiver)
            continue;
        QObject * const receiver = c->receiver;

        //发送方和接收方是否在同一个线程中
        const bool receiverInSameThread = 
        currentThreadId == receiver->d_func()->threadData->threadId;

        // determine if this connection should be sent immediately or
        // put into the event queue
        if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
            || (c->connectionType == Qt::QueuedConnection))
          {
            //列队发送
            queued_activate(sender, signal_index, c, argv ?
                            argv : empty_argv, locker);
                continue;
            }else if (c->connectionType == Qt::BlockingQueuedConnection){
            //postEvent
                locker.unlock();
                if (receiverInSameThread) {
                    qWarning("Qt: Dead lock detected while activating a"   
                             "BlockingQueuedConnection: "
                             "Sender is %s(%p), receiver is %s(%p)",
                    sender->metaObject()->className(), sender,
                    receiver->metaObject()->className(), receiver);
                }
                QSemaphore semaphore;
                QMetaCallEvent *ev = c->isSlotObject ?
                    new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, 
                                       argv ? argv : empty_argv, &semaphore) :
                    new QMetaCallEvent(c->method_offset, c->method_relative, 
                                       c->callFunction, sender, signal_index, 0, 0, 
                                       argv ? argv : empty_argv, &semaphore);
                //post到事件队列中    
                QCoreApplication::postEvent(receiver, ev);
                semaphore.acquire();
                locker.relock();
                continue;
            }

         //直接调用关联的槽
          QConnectionSenderSwitcher sw;
          if (receiverInSameThread) {
              sw.switchSender(receiver, sender, signal_index);
            }
         const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
         const int method_relative = c->method_relative;
         if (c->isSlotObject) {
           ...
            QScopedPointer<QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
             obj->call(receiver, argv ? argv : empty_argv);
           ...
         }else if (callFunction && c->method_offset <= 
                   receiver->metaObject()->methodOffset()) {
           ...
            callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, 
                         argv ? argv : empty_argv);
           ...
         }else {
                const int method = method_relative + c->method_offset;
           ...
             metacall(receiver, QMetaObject::InvokeMetaMethod, method, 
                      argv ? argv : empty_argv);
           ...
         }

    }while(c != last && (c = c->nextConnectionList) != 0); //!

    if (connectionLists->orphaned)
            break;

  }while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));//!
}


关联到某个信号的所有槽都在这里的得到处理,要么立即被调用,这是发送者和接收者在同一个线程中时的默认处理方式;要么列队发送,这是发送者和接收者不在同一个线程中时的默认处理方式。

static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv,
                            QMutexLocker &locker)
{
  ...
     QMetaCallEvent *ev = c->isSlotObject ?
     new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
     new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, 
                        sender, signal, nargs, types, args);

    //post事件到接收者所属线程的QThreadData::postEventList队列中
     QCoreApplication::postEvent(c->receiver, ev);
  ...
}







来看看这个作为基础设施的postEvent函数:

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
     if (receiver == 0) {
        qWarning("QCoreApplication::postEvent: Unexpected null receiver");
        delete event;
        return;
    }

    QThreadData * volatile * pdata = &receiver->d_func()->threadData;
    QThreadData *data = *pdata;
    if (!data) {
        // posting during destruction? just delete the event to prevent a leak
        delete event;
        return;
    }

      // lock the post event mutex
    data->postEventList.mutex.lock();
      // if object has moved to another thread, follow it
    while (data != *pdata) {
        data->postEventList.mutex.unlock();

        data = *pdata;
        if (!data) {
            // posting during destruction? just delete the event to prevent a leak
            delete event;
            return;
        }
        //尝试获取锁
        data->postEventList.mutex.lock();
    }

  //防止资源泄漏
   QMutexUnlocker locker(&data->postEventList.mutex);

    //如果这个事件和post队列中的事件可以合并,那就合并,提高事件吞吐量
      // if this is one of the compressible events, do compression
   if (receiver->d_func()->postedEvents
        && self && self->compressEvent(event, receiver, &data->postEventList)) {
        return;
   }

  //如果是一个删除事件并且接收者和发送者位于同一线程,则在event中记录当前线程的事件循环层次
   if (event->type() == QEvent::DeferredDelete && data == QThreadData::current())
   {
        // remember the current running eventloop for DeferredDelete
        // events posted in the receiver's thread
        static_cast<QDeferredDeleteEvent *>(event)->level = data->loopLevel;
    }

    // delete the event on exceptions to protect against memory leaks till the 
    // event is properly owned in the postEventList
    QScopedPointer<QEvent> eventDeleter(event);

    //将事件添加到postEventList中
    data->postEventList.addEvent(QPostEvent(receiver, event, priority));
    eventDeleter.take();
    event->posted = true;
    ++receiver->d_func()->postedEvents;
    data->canWait = false;
    locker.unlock();

    //唤醒接收者所在线程的事件派发器:
    //所谓唤醒,其实就是利用windows的消息循环机制,向接收者线程的dispatcher的message-only窗
    //口句柄发送一个1025号消息。dispatcher的线程的消息循环Peek到这条消息后,就会去处理他的队
    //列消息
    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
    if (dispatcher)
        dispatcher->wakeUp();
}

模态窗口

有时候,需要一个窗口屏蔽掉其他窗口的消息,在windows上,这样的窗口叫模态窗口。创建模态窗口后,在关闭它之前,不允许用户操作其他窗口。模态窗口可以和局部事件循环结合,从而允许在栈上创建一个模态窗口:

class Widget : public  QWidget
{
public:
    Widget(QWidget *_p = nullptr) :QWidget(_p){ }

protected:

    void mousePressEvent(QMouseEvent *e)
    {
        MyClass w;
        //设置窗口为模态窗口,模态窗口一定是个native widget
        w.setAttribute(Qt::WA_ShowModal, true);
        //创建QWindow+QWindowsWindow,调用Win32API创建窗口并显示窗口
        w.show();
        //开启局部事件循环
        QEventLoop loop;
        loop.exec();
    }

};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

Native widget or Alien widget

Native widget是指拥有windows窗口句柄的widget,占用了内核资源,Alien widget是指依附在某个Native widget上的子窗口,没有windows窗口句柄,不占用内核资源。在qt4.4之前,所有的widget都是Native widget,而且伴随着被人诟病的闪烁现象。qt4.4之后,使用了Alien widget,子窗口默认是一个Alien widget,除非必要,qt不会创建一个Native widget。qt想尽可能地把对窗口的处理从内核转移到qt上,从而拥有更大的自主权。widget的Alien widget和Native widget属性是可以配置的,如果你确实需要一个Native widget,你可以对属性进行显示的配置

setAttribute(Qt::WA_NativeWindow);

创建Native widget

如果一个窗口是Alien widget,没有窗口句柄,那他怎么得到windows的消息呢?不能,可以肯定的是一个没有窗口句柄的Alien widget是无法被操作系统感知的。所以,在windows之上,qt建立了自己的窗口系统。然而,qt程序逃避不了的一个事实是:它需要获取windows的消息。要获取操作系统消息的前提是拥有窗口句柄,所以,qt的窗口程序一般是这样的:

一个Native widget作为顶层窗口,一些Alien widget窗口作为顶层窗口的后代,依附其上。

这样形式的qt程序,对于操作系统来说,它只看到了Native widget,所以它就认为消息是传递给这个Native widget的;对于qt的窗口系统来说,它看到的是依附于Native widget上绘制出来的数目众多的Alien widget,qt内部再确定该事件正真的目的地是哪个窗口。

对,没毛病,windows的观点没错,qt的观点就更对了。

创建一个Native widget就意味着需要注册一个窗口类或者使用同名的已经注册过的窗口类,然后创建一个内核窗口对象,并返回窗口句柄。

QWidget注册窗口类时使用的窗口过程为qWindowsWndProc

extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result;
    const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam);

    const bool handled = 
      QWindowsContext::instance()->windowsProc(hwnd, message, et, 
                                               wParam, lParam, &result); 
...
    //qt不感兴趣的消息,直接交由DefWindowProc处理
    if (!handled)
        result = DefWindowProc(hwnd, message, wParam, lParam);
    return result;
}

例如,当你点击窗口时,windows感知到了用户对native widget的点击操作,并将消息发送给native widget所在的线程的消息队列,在qt的消息循环中能够GetMessage获取消息,qt使用GetMessage的非阻塞版PeekMessage获取线程队列中的消息,然后通过DispatchMessage将消息转给Native widget的这个窗口过程qWindowsWndProc

窗口过程将消息映射到系统事件类型WindowsEventType,case各种类型,最后将封装好的WindowSystemEvent格式的系统事件放入QWindowSystemInterfacePrivate的全局系统事件队列中。

对,一般都是将事件放入windowSystemEventQueue队列中排队。然后就一步步的返回了,最后从DispatchMessage回到事件循环。

如果遇到qt不干兴趣的消息,他不会将其放入windowSystemEventQueue队列,而是从windowsProc返回false,交给DefWindowProc进行默认处理。

void QWindowSystemInterfacePrivate::handleWindowSystemEvent(
  QWindowSystemInterfacePrivate::WindowSystemEvent *ev)
{
    if (synchronousWindowsSystemEvents) 
    {
     //同步处理
        QGuiApplicationPrivate::processWindowSystemEvent(ev);//直接处理
    } else 
    {
     //异步处理

        windowSystemEventQueue.append(ev);//消息排队

        QAbstractEventDispatcher 
          *dispatcher = QGuiApplicationPrivate::qt_qpa_core_dispatcher();
        if (dispatcher)
            dispatcher->wakeUp();//发送WM_QT_SENDPOSTEDEVENTS(1025)给QApplication的
                                //message-only窗口
    }
}

Windows消息有两种传送方式,一种是Post语义的传送方式,还有一种是Send语义的传送方式,Post将消息排队到线程的消息队列中,Send则直接将消息传送到窗口的窗口过程。

将系统事件排队之后,需要保证线程消息队列中一定存在且只存在一条到d->internalHwnd窗口的1025号消息WM_QT_SENDPOSTEDEVENTS

创建QApplication的message-only窗口

在定义一个QApplication的变量时,构造函数就在内部创建一个专门用来处理qt事件的message-only窗口,这个窗口有窗口句柄,但是不显示。这个窗口类注册的窗口过程是qt_internal_proc, 通过qt_create_internal_window创建,整个流程是这样的:

static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
    QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
    if (!ctx->atom)
        return 0;
#ifdef Q_OS_WINCE
    HWND parent = 0;
#else
    HWND parent = HWND_MESSAGE; //message-only
#endif
    HWND wnd = CreateWindow(ctx->className,    // classname
                            ctx->className,    // window name
                            0,                 // style
                            0, 0, 0, 0,        // geometry
                            parent,            // parent
                            0,                 // menu handle
                            qWinAppInst(),     // application
                            0);                // windows creation data.

    if (!wnd) {
        qErrnoWarning("%s: CreateWindow() for QEventDispatcherWin32 internal window failed", Q_FUNC_INFO);
        return 0;
    }

  //关联一个32位的值: 事件派发器的地址eventDispatcher
#ifdef GWLP_USERDATA
    SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher);
#else
    SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher);
#endif

    return wnd;
}

qWindowsMessageWindowClassContext构造完成的同时注册了窗口类,这个窗口类的窗口过程代码如下

LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, 
                                         WPARAM wp, LPARAM lp)
{
    if (message == WM_NCCREATE)
        return true;

    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();

    ...

  if (message == WM_QT_SENDPOSTEDEVENTS //WM_QT_SENDPOSTEDEVENTS
               // we also use a Windows timer to send posted events when the message 
               //queue is full
               || (message == WM_TIMER  //WM_TIMER
                   && d->sendPostedEventsWindowsTimerId != 0
                   && wp == (uint)d->sendPostedEventsWindowsTimerId)) 
    {
        const int localSerialNumber = d->serialNumber.load();
        if (localSerialNumber != d->lastSerialNumber) 
        {
            d->lastSerialNumber = localSerialNumber;

           //事件派发器派发事件
            q->sendPostedEvents();
        }
        return 0;
    } else if (message == WM_TIMER) {
        Q_ASSERT(d != 0);
        d->sendTimerEvent(wp);
        return 0;
    }

   //默认处理
    return DefWindowProc(hwnd, message, wp, lp);
}

QAbstractEventDispatcher::instance(QThread *thread)获取存储在指定线程的TLS中的QThreadData的eventDispatcher成员,如果没有指定线程,默认为当前线程。

如果当前线程存在dispatcher,则先使用当前线程的dispatcher对原始消息调用安装到dispatcher上的nativeEventFilter进行过滤处理。对未过滤的消息使用message-only窗口关联的dispatcher。但其实,这两个dispatcher实际上是同一个。

就拿GUI线程来说:

QCoreApplication::init()
 {
  ...
     // use the event dispatcher created by the app programmer (if any)
    if (!QCoreApplicationPrivate::eventDispatcher)
        QCoreApplicationPrivate::eventDispatcher = 
      d->threadData->eventDispatcher.load();
    // otherwise we create one
    if (!QCoreApplicationPrivate::eventDispatcher)
      /*
      *  创建一个QWindowsGuiEventDispatcher;
      *  调用createInternalHwnd创建一个message-only窗口;
      *  SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher)与窗口关联
      */
        d->createEventDispatcher();
    Q_ASSERT(QCoreApplicationPrivate::eventDispatcher != 0);
  ...
    //将刚创建的eventDispatcher存储在d->threadData->eventDispatcher中
    d->threadData->eventDispatcher = QCoreApplicationPrivate::eventDispatcher;
  ...
 } 

回调过程调用派发器的sendPostedEvents方法处理排队事件:

void QWindowsGuiEventDispatcher::sendPostedEvents()
{
    //派发postEventList中的QEvent
    QEventDispatcherWin32::sendPostedEvents();
    //处理,封装windowSystemEventsQueued中的WindowSystemEvent并派发
    QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}

注意,GUI线程使用的派发器是QWindowsGuiEventDispatcher,所以除了处理postEventList队列之外,还会处理windowSystemEventsQueued。但是非GUI线程使用的派发器是QEventDispatcherWin32,所以非GUI线程就只负责处理他的postEventList队列。

QEventDispatcherWin32::sendPostedEvents将Post到当前线程QThreadData::postEventList中的QPostEvent包装着的QEvent发送给QPostEvent**指定的对象**。

void QEventDispatcherWin32::sendPostedEvents()
{
    Q_D(QEventDispatcherWin32);
    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
}
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
        //只能发送事件给属于该线程的对象
      if(receiver && receiver->d_func()->threadData != data)
       {
        qWarning("QCoreApplication::sendPostedEvents: Cannot send "
                 "posted events for objects in another thread");
        return;
      }

      ++data->postEventList.recursion;
      QMutexLocker locker(&data->postEventList.mutex);

      data->canWait = (data->postEventList.size() == 0);

    /*
    * 如果线程上没有QPostEvent列队,或者接收者不存在,或者接收者的postedEvents数目为0,则
      返回
    */
      if (data->postEventList.size() == 0 || 
         (receiver && !receiver->d_func()->postedEvents)) {
        --data->postEventList.recursion;
        return;
    }

     data->canWait = true;

    //postEventList是一个特殊的循环队列
    int startOffset = data->postEventList.startOffset;
    int &i = (!event_type && !receiver) ? 
                                data->postEventList.startOffset : startOffset;
    data->postEventList.insertionOffset = data->postEventList.size();

     //负责退栈之后的清理
     CleanUp cleanup(receiver, event_type, data);

  //循环处理
  while (i < data->postEventList.size()) {
       // avoid live-lock:由于当前线程处理的同时,其他线程还可以继续向这个线程
       // 的postEventList中列队事件,所以不能以消费普通的循环队列的方式消费这个
       // 这个队列的事件,这有可能造成活锁
        if (i >= data->postEventList.insertionOffset)
            break;

        const QPostEvent &pe = data->postEventList.at(i);
        ++i;

        if (!pe.event)
            continue;

        if ((receiver && receiver != pe.receiver) || 
            (event_type && event_type != pe.event->type())) 
        {
            data->canWait = false;
            continue;
        }
        ...
        pe.event->posted = false;
        QEvent *e = pe.event;
        QObject * r = pe.receiver;

        --r->d_func()->postedEvents;
        Q_ASSERT(r->d_func()->postedEvents >= 0); 
        const_cast<QPostEvent &>(pe).event = 0;

        struct MutexUnlocker
        {
            QMutexLocker &m;
            MutexUnlocker(QMutexLocker &m) : m(m) { m.unlock(); }
            ~MutexUnlocker() { m.relock(); }
        };
        MutexUnlocker unlocker(locker);

        // will delete the event (with the mutex unlocked)
        QScopedPointer<QEvent> event_deleter(e);

        // after all that work, it's time to deliver the event.
        QCoreApplication::sendEvent(r, e);
  }//  while !

} 

QWindowSystemInterface::sendWindowSystemEvents将Post到windowSystemEventsQueued上的WindowSystemEvent封装成QEvent,然后发送给特定的对象。

bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
{
    int nevents = 0;
    //循环处理,直到队列为空
    while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {
        QWindowSystemInterfacePrivate::WindowSystemEvent *event =
            (flags & QEventLoop::ExcludeUserInputEvents) ?
                QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
                QWindowSystemInterfacePrivate::getWindowSystemEvent();
        if (!event)
            break;
        nevents++;

       //进行封装派发
        QGuiApplicationPrivate::processWindowSystemEvent(event);
        delete event;
    }

    return (nevents > 0);
}

派发事件的公共基础方法

掠过细节不谈,瞧瞧与搞Qt界面开发的程序员最接近的一个相关函数:

bool QApplication::notify(QObject *receiver, QEvent *e)
 {
    if (Q_UNLIKELY(!receiver)) {
            return true;
        }

  ... 
    switch (e->type()) {
        ... //省略各种事件case...

        //以滚轮事件为例:  
        case QEvent::Wheel:
        {
            //进行强制类型转换
            QWidget* w = static_cast<QWidget *>(receiver);
            QWheelEvent* wheel = static_cast<QWheelEvent*>(e);

            // ignore wheel events when a popup (QComboBox) is open.
            if (const QWidget *popup = QApplication::activePopupWidget()) {
                if (w->window() != popup)
                    return true;
            }

            QPoint relpos = wheel->pos();
            bool eventAccepted = wheel->isAccepted();

            if (e->spontaneous() && wheel->phase() == Qt::ScrollUpdate)
                QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, e, relpos);

           /*
           * 循环,直到事件被某个接收者w正确的处理
           */
            while (w) {
               //事件拷贝到变量we
                QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), 
                               wheel->angleDelta(), wheel->delta(), 
                               wheel->orientation(), wheel->buttons(),
                               wheel->modifiers(), wheel->phase(), wheel->source());
                we.spont = wheel->spontaneous();

               /*
               * 将事件传递下去,进行处理,如果返回值res为真且事件被接收,或者到了顶层窗口,
               * 或者w窗口设置了Qt::WA_NoMousePropagation,则停止事件的传递,退出循环。
               * 完成对该事件的处理。否则,将事件转发给w的父窗口处理:
               * w = w->parentWidget();
               */
                res = d->notify_helper(w, w == receiver ? wheel : &we);
                eventAccepted = ((w == receiver) ? wheel : &we)->isAccepted();
                e->spont = false;
                if ((res && eventAccepted)
                    || w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
                    break;

                relpos += w->pos();
                w = w->parentWidget();
            }   // while (w)!

            wheel->setAccepted(eventAccepted);
        } break;  

        ... //省略各种事件case...

    } //switch (e->type())! 


        return res;
 }

不过,上面的代码也足以说明 QApplication::notify的主要功能了,再来看看notify_helper:

bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{ 
    /*
    * 首先将事件交由安装到QApplication上的事件过滤器进行处理。如果事件被过滤器处理并返回
    * true,则从这里直接返回true
    */
    // send to all application event filters
    if (sendThroughApplicationEventFilters(receiver, e))
        return true;

    if (receiver->isWidgetType()) {
        QWidget *widget = static_cast<QWidget *>(receiver);
      ...

     /*
     * 在事件交由receiver的event处理之前,先将它交给安装到receiver上的过滤器处理。如果事件
     * 被过滤器处理并返回true,则从这里直接返回true
     */   
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e))
        return true;

    /*
    * 最后,交给receiver的event进行处理,返回event的返回值
    */  
    // deliver the event
    bool consumed = receiver->event(e);
    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值