QEventDispatcherWin32 笔记

晨曦之光 发布于 2012/05/08 10:14
阅读 310
收藏 0

额,还是从一个window程序的基本结构看起吧

Win32程序基本结构

  • 注册窗口类别 RegisterClass

  • 创建窗口 CreateWindow

  • 启动由GetMessage和DispatchMessage构成的事件循环

  • 被注册的回调函数 WndProc 负责相应各类事件

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

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;
}

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为当前执行的每个Windows程式维护一个消息队列,通过PostMessage、PostThreadMessage可以把消息放入队列,在事件循环中通过GetMessage、PeekMessage可以或者队列中的消息并可通过DispatchMessage将消息派发到相应的窗口回调函数。

  • 消息队列为空时,GetMessage 会阻塞,而PeekMessage不会。现在一般都使用后者

  • SendMessage发送消息直接到窗口回调函数,不进入消息队列

  • WH_GETMESSAGE类型的钩子可以截获GetMessage和PeekMessage的消息

  • 截获SendMessage发送的消息,需要WH_CALLWNDPROC或WH_CALLWNDPROCRET类型的钩子

QEventDispatcherWin32

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

这3个比较容易和前面的对应上。但是... 该怎么叙述呢...

事件循环

Qt中的事件循环式通过 QEventLoop::exec() 来启动的。(它通常以QCoreApplication::exec()、QDialog::exec()等身份出现)

在 QEventLoop::exec() 内,是一个while 循环。

int QEventLoop::exec(ProcessEventsFlags flags)
{
    while (!d->exit)
       //...

该循环内,调用的就是 QEventDispatcherWin32::processEvents() 函数

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
...
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
...
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
...

这样一来,消息被派发到窗口回调函数中。进而可以被

  • 回调函数 qt_internal_proc()
  • 钩子 qt_GetMessageHook()

处理

相关函数

  • bool QAbstractEventDispatcher::hasPendingEvents ()

Qt事件队列以及程序的消息队列是否有非空

  • void QAbstractEventDispatcher::wakeUp ()

processEvents() 可能会处于一个阻塞状态,比如调用MsgWaitForMultipleObjectsEx(),此时需要唤醒它。

比如QCoreApplication::postEvent()往队列放入新的事件时,会调用该函数。

  • void QAbstractEventDispatcher::interrupt ()

打断processEvents() ,使其尽快返回,即使队列中还有很多东西。

定时器

主要是定时器的注册与反注册,相关的成员函数

  • int QAbstractEventDispatcher::registerTimer ( int interval, QObject * object )
  • void QAbstractEventDispatcher::registerTimer ( int timerId, int interval, QObject * object )
  • QList<TimerInfo> QAbstractEventDispatcher::registeredTimers ( QObject * object )

  • bool QAbstractEventDispatcher::unregisterTimer ( int timerId )
  • bool QAbstractEventDispatcher::unregisterTimers ( QObject * object )

在Windows层面,

  • 普通定时器由 SetTimer()、KillTimer() 开启和关闭,在窗口回调函数中接收 WM_TIMER 消息,进而转换成Qt事件

  • 多媒体定时器由 timeSetEvent() 和 timeKillEvent() 开启和关闭,在 timeSetEvent参数中指定回调函数qt_fast_timer_proc()用以处理定时器事件,并转换成Qt事件

Socket Notifier

  • void QAbstractEventDispatcher::registerSocketNotifier ( QSocketNotifier * notifier )
  • void QAbstractEventDispatcher::unregisterSocketNotifier ( QSocketNotifier * notifier )

在源码中,你可以看到 WSAAsyncSelect 这个函数的身影,但是它不是Windows的同名的api函数。而是一个本地的函数。

在Qt下,它是通过创建新线程,并在线程内调用 ::select() 函数来实现的。线程内通过SendMessage将消息传递到窗口的回调函数中。

int select(
  __in     int nfds,
  __inout  fd_set *readfds,
  __inout  fd_set *writefds,
  __inout  fd_set *exceptfds,
  __in     const struct timeval *timeout
);
  • The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

QSocketNotifier::Read

FD_READ

有可读消息通知

FD_CLOSE

关闭消息通知

FD_ACCEPT

链接请求消息通知

QSocketNotifier::Write

FD_WRITE

有可写消息通知

FD_CONNECT

希望得到connect或多点join操作完成信息通知

QSocketNotifier::Exception

FD_OOB

有外带消息通知

Win Event Notifer

  • bool registerEventNotifier(QWinEventNotifier *notifier);
  • void unregisterEventNotifier(QWinEventNotifier *notifier);
  • void activateEventNotifiers();

这是windows下特有的一个。当某个事件内核对象被触发时,我们可以以异步的方式得到通知。

在是在

QEventDispatcherWin32::processEvents()

函数内,通过调用

MsgWaitForMultipleObjectsEx()

来实现的。

Qt 事件队列

恩,似乎还少点这部分的东西。Qt的事件都是要派发到 QObject::event() 这个函数中去的。

bool QCoreApplication::sendEvent ( QObject * receiver, QEvent * event )

直接将直接派发到 QObject::event()

void QCoreApplication::postEvent ( QObject * receiver, QEvent * event )

确实将事件放置到Qt的事件队列中。可是这些事件是怎么取出来,并派发到 QObject::event() 中去的呢?

负责这个工作的是:

void QCoreApplication::sendPostedEvents()
void QCoreApplication::sendPostedEvents ( QObject * receiver, int event_type )

将事件队列中的事件取出,并使用QCoreApplication::sendEvent ()将其派发出去。

恩,exec() 启动的事件循环调用 event dispatcher 的 processEvents(),该函数负责调用前面的QCoreApplication::sendPostedEvents()函数。

  • 它会启动一个内部的定时器
  • 它会通过PostMessage向程序消息队列放置WM_QT_SENDPOSTEDEVENTS消息

这样工作又转移到了一开始提到窗口回调函数中了。



原文链接:http://blog.csdn.net/dbzhang800/article/details/6973720
加载中
返回顶部
顶部