Qt在各个平台下都是对平台API进行了一些包装。Windows下是对Win32API的封装。如果是Windows平台的GUI Application就一定是从WinMain
开始。
不难发现WinMain
就在qtmain_win.cpp 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 extern "C" int APIENTRY WinMain (HINSTANCE, HINSTANCE, LPSTR , int ) { int argc; wchar_t **argvW = CommandLineToArgvW (GetCommandLineW (), &argc); if (!argvW) return -1 ; char **argv = new char *[argc + 1 ]; for (int i = 0 ; i < argc; ++i) argv[i] = wideToMulti (CP_ACP, argvW[i]); argv[argc] = Q_NULLPTR; LocalFree (argvW); const int exitCode = main (argc, argv); for (int i = 0 ; i < argc && argv[i]; ++i) delete [] argv[i]; delete [] argv; return exitCode; }
在这里的WinMain
仅仅充当一个入口,所有对命令行参数 这些 都交由main
来处理。而这个main
就是我们在自己的主程序中写的main
。
入口找到以后,在Windows下的程序还有一个很重要的东西,那就是消息循环。Win32中经典的PeekMessage()
、DispatchMessage()
和TranslateMessage()
。这些东西在程序中注册的回调函数中被调用,用来处理和解析消息。Qt本身也要依赖这些,只不过在上边进行了一些封装。调到我们自己的程序里看到的就是winEvent()
或者是一些QEvent
了。
我们写Qt程序的时候,一个很常见的套路是:
1 2 3 4 5 6 int main(int argv, char **args) { QApplication app(argv, args); //todo... return app.exec(); }
这个回调函数就是在app.exec()
中被注册(准确的说回调函数是由在这个方法中调用的其他方法注册)。不难找到一个叫做qeventdispatcher_win.cpp 文件,名字已经很明确了,就是处理Qt事件的。我们会找到一个类QEventDispatcherWin32
。可以发现一个processEvents()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 bool QEventDispatcherWin32::processEvents (QEventLoop::ProcessEventsFlags flags) { Q_D (QEventDispatcherWin32); if (!d->internalHwnd) { createInternalHwnd (); wakeUp (); } 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; if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty ()) { haveMessage = true ; msg = d->queuedUserInputEvents.takeFirst (); } else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty ()) { haveMessage = true ; msg = d->queuedSocketEvents.takeFirst (); } else { 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)) { d->queuedUserInputEvents.append (msg); continue ; } } } if (haveMessage) { if (!filterNativeEvent (QByteArrayLiteral ("windows_generic_MSG" ), &msg, 0 )) { TranslateMessage (&msg); DispatchMessage (&msg); } } retVal = true ; } 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); return retVal; }
代码比较长,省略了一些暂时不关注的,在这里我们可以看到我们最熟悉的Win32的消息枚举和方法。现在问题来了DispatchMessage()
以后,程序的调用会走到我们注册的回调函数,由我们自己来处理消息。所以要找到这个回调。
Qt的这个回调函数是qt_internal_proc()
。那下一个问题就是在哪里注册的这个回调函数。 这个可以回顾一下Win32程序的一般套路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 WNDCLASSEX wc; HWND hwnd; MSG Msg; wc.cbSize = sizeof (WNDCLASSEX); wc.style = 0 ; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0 ; wc.cbWndExtra = 0 ; wc.hInstance = hInstance; wc.hIcon = LoadIcon (NULL , IDI_APPLICATION); wc.hCursor = LoadCursor (NULL , IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1 ); wc.lpszMenuName = NULL ; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon (NULL , IDI_APPLICATION); if (!RegisterClassEx (&wc)){ MessageBox (NULL , "Window Registration Failed!" , "Error!" , MB_ICONEXCLAMATION | MB_OK); return 0 ; }
如果要写一个Win32的程序,都要先注册一个Windows Class
,就是在lpfnWndProc
中指明我们的回调方法。再回过头去看processEvents()
方法中createInternalHwnd()
的调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void QEventDispatcherWin32::createInternalHwnd () { Q_D (QEventDispatcherWin32); if (d->internalHwnd) return ; d->internalHwnd = qt_create_internal_window (this ); installMessageHook (); for (int i = 0 ; i < d->timerVec.count (); ++i) d->registerTimer (d->timerVec.at (i)); }
可以看到一个名叫qt_create_internal_window()
的方法,顾名思义。在此方法中会获取一个QWindowsMessageWindowClassContext
,看一下他的构造函数,一目了然:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext () : atom (0 ), className (0 ) { 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 = qWinAppInst (); 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 ; } }
不难发现回调函数qt_internal_proc()
就是在这里注册的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } return result; }
Qt就是这样将Win32的调用包装成了自己的调用。