QEventDispatcherWin 32ノート


ええ、やはりwindowプログラムの基本構造から見ましょう.

Win 32プログラム基本構造

  • 登録ウィンドウカテゴリ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

  • ウィンドウカテゴリを登録し、非表示ウィンドウ(QEventDispatcherWin 32_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)
           //...

    このループ内で呼び出されるのがQEventDispatcherWin 32::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 ()

  • プロセスEvents()を中断し、キューにまだ多くのものがある場合でも、できるだけ早く戻るようにします.

    タイマ


    主にタイマの登録と逆登録、関連するメンバー関数
  • int QAbstractEventDispatcher::registerTimer ( int interval, QObject * object )
  • void QAbstractEventDispatcher::registerTimer ( int timerId, int interval, QObject * object )
  • QList 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 )

  • ソースコードでは、WAAsyncSelectという関数の姿が見えますが、Windowsの同じ名前のapi関数ではありません.ローカルの関数です
    Qtでは、新しいスレッドを作成し、スレッド内で::select()関数を呼び出すことで実現します.スレッド内のメッセージは、ウィンドウのコールバック関数に渡されます.
    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
    接続またはマルチポイントjoin操作完了情報の通知を取得したい
    QSocketNotifier::Exception
    FD_OOB
    外部からのお知らせがあります

    Win Event Notifer

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

  • これはwindowsの下特有の1つです.イベントカーネルオブジェクトがトリガーされると、非同期で通知できます.
    にある
    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メッセージ
  • このような作業は、最初にウィンドウコールバック関数に移行しました.