【Qt源码笔记】从WinMain说起
Qt在各个平台下都是对平台API进行了一些包装。Windows下是对Win32API的封装。如果是Windows平台的GUI Application就一定是从WinMain开始。
不难发现WinMain就在qtmain_win.cpp中。
extern "C" int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR /*cmdParamarg*/, int /* cmdShow */){ 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程序的时候,一个很常见的套路是:
int main(int argv, char **args){ QApplication app(argv, args); //todo... return app.exec();}这个回调函数就是在app.exec()中被注册(准确的说回调函数是由在这个方法中调用的其他方法注册)。不难找到一个叫做qeventdispatcher_win.cpp文件,名字已经很明确了,就是处理Qt事件的。我们会找到一个类QEventDispatcherWin32。可以发现一个processEvents()方法。
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags){ Q_D(QEventDispatcherWin32);
if (!d->internalHwnd) { createInternalHwnd(); wakeUp(); // trigger a call to sendPostedEvents() }
//... 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()) { // process queued user input events haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events 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)) { // queue user input events for later processing d->queuedUserInputEvents.append(msg); continue; } //... } } //... if (haveMessage) { //... if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } //... 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);
// ... return retVal;}代码比较长,省略了一些暂时不关注的,在这里我们可以看到我们最熟悉的Win32的消息枚举和方法。现在问题来了DispatchMessage()以后,程序的调用会走到我们注册的回调函数,由我们自己来处理消息。所以要找到这个回调。
Qt的这个回调函数是qt_internal_proc()。那下一个问题就是在哪里注册的这个回调函数。
这个可以回顾一下Win32程序的一般套路:
WNDCLASSEX wc; HWND hwnd; MSG Msg; //Step 1: Registering the Window Class 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()的调用:
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));}可以看到一个名叫qt_create_internal_window()的方法,顾名思义。在此方法中会获取一个QWindowsMessageWindowClassContext,看一下他的构造函数,一目了然:
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 = 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()就是在这里注册的。
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的调用包装成了自己的调用。
如果觉得文章不错,欢迎赞赏
微信
支付宝