本文以一个Win32的helloworld程序开篇,
· 程序入口WinMain
- 注册窗口类别
- 建立窗口,在屏幕上显示
- 进入事件循环,不断从事件队列中取出消息来处理
而后尝试解释前述各部分分别隐藏在Qt何处:
main() |
程序入口 |
Qt提供一个WinMain来调用main |
QWidget::show() |
注册窗口类别 |
第一次使用时会注册类别 |
显示窗体 |
和hide、setHidden都是setVisble的马甲 |
|
QApplication::exec() |
进入事件循环 |
核心是 QEventDispatcherWin32 |
声明,我对Win32编程不了解,本文只是Qt学习过程中的学习笔记,本文提到内容以及用词描述可能会不太准确。
这是Win32编程的一个hello world程序:
· 包含头文件,定义入口函数WinMain
[cpp]
1. #include <windows.h>
2. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
3. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
4. PSTR szCmdLine, int iCmdShow)
5. {
6. static TCHAR szAppName[] = TEXT("Hello");
7. HWND hwnd;
8. MSG msg;
· 创建窗口类别(注意里面的WndProc是我们后面定义的回调函数),并注册窗口类别
[cpp]
1. WNDCLASS wndclass;
2. wndclass.style = CS_HREDRAW | CS_VREDRAW;
3. wndclass.lpfnWndProc = WndProc;
4. wndclass.cbClsExtra = 0;
5. wndclass.cbWndExtra = 0;
6. wndclass.hInstance = hInstance;
7. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
8. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
9. wndclass.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
10. wndclass.lpszMenuName = NULL;
11. wndclass.lpszClassName= szAppName;
12. if(!RegisterClass(&wndclass))
13. {
14. MessageBox( NULL, TEXT("This program requires Windows NT!"),
15. szAppName, MB_ICONERROR);
16. return 0;
17. }
- 创建窗体,并显示与更新窗体
[cpp]
1. hwnd = CreateWindow( szAppName, // window class name
2. TEXT("The Hello Program"), // window caption
3. WS_OVERLAPPEDWINDOW, // window style
4. CW_USEDEFAULT, // initial x position
5. CW_USEDEFAULT, // initial y position
6. CW_USEDEFAULT, // initial x size
7. CW_USEDEFAULT, // initial y size
8. NULL, // parent window handle
9. NULL, // window menu handle
10. hInstance, // program instance handle
11. NULL); // creation parameters
12. ShowWindow(hwnd, iCmdShow);
13. UpdateWindow(hwnd);
- 启动事件循环
[cpp]
1. while(GetMessage(&msg, NULL, 0, 0))
2. {
3. TranslateMessage(&msg);
4. DispatchMessage(&msg);
5. }
6. return msg.wParam;
7. }
以上我们所讨论的都是必要的东西:注册窗口类别,建立窗口,在屏幕上显示窗口,进入事件循环,不断从事件队列中取出消息来处理。
实际的动作发生在消息处理函数中。该函数确定了在视窗的显示区域中显示些什么以及怎样响应用户的输入等。
- 消息处理函数(回调函数)
[cpp]
1. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2. {
3. HDC hdc;
4. PAINTSTRUCT ps;
5. RECT rect;
6. switch(message)
7. {
8. case WM_CREATE:
9. return 0;
10. case WM_PAINT:
11. hdc = BeginPaint(hwnd, &ps);
12. GetClientRect(hwnd, &rect);
13. DrawText(hdc, TEXT("Hello, Windows!"), -1, &rect,
14. DT_SINGLELINE | DT_CENTER | DT_VCENTER);
15. EndPaint(hwnd, &ps);
16. return 0;
17. case WM_DESTROY:
18. PostQuitMessage(0);
19. return 0;
20. }
21. return DefWindowProc(hwnd, message, wParam, lParam);
22. }
用cl或gcc编译该程序:
[cpp]
1. gcc hello.c -o hello -lgdi32 -Wl,-subsystem,windows
2. cl hello.c user32.lib gdi32.lib
既然是 hello world,也就基本是最简单的Win32程序了。但看着还是挺复杂,难怪大家都不怎么喜欢它。
Qt(或其他的图形库/框架)多简单啊,简单几行代码一个程序就出来了。可是... 简单的表象下面呢?我们如何把一个Windows下的 Qt 程序和上面的代码对应上?
[cpp]
1. #include <QtGui/QApplication>
2. #include <QtGui/QLabel>
3.
4. int main(int argc, char *argv[])
5. {
6. QApplication app(argc, argv);
7. QLabel w("Hello world!");
8. w.show();
9. return app.exec();
10. }
· 入口函数 WinMain
在Qt中我们只写main函数,不写WinMain,挺有意思哈,不过在Qt Windows下链接子系统与入口函数(终结版) 中我们已经详细讨论过这个问题了(简单地说:就是qtmain.lib或libqtmain.a提供了一个WinMain,它会调用我们的main)
- 创建并注册窗体类别与创建并显示窗体部分
这个东西挺隐蔽的哈,当你对一个QWidget调用setVisble()或者它的众多马甲(比如show() )之一时,会执行这部分代码。源码位于qwidget_win.cpp 和 qapplication_win.cpp 文件中
- 事件循环
这个循环体现在Qt中就是 QApplication::exec()。核心代码在qeventdispatcher_win.cpp 中。
当第一次调用setVisible()时,会进行窗口类别的创建,这最终会调用 qapplication_win.cpp 中的下面一个函数:
[cpp]
1. const QString qt_reg_winclass(QWidget *w)
2. {
3. ...
4. WNDCLASS wc;
5. wc.style = style;
6. wc.lpfnWndProc = (WNDPROC)QtWndProc;
7. wc.cbClsExtra = 0;
8. wc.cbWndExtra = 0;
9. wc.hInstance = qWinAppInst();
10. ...
11. wc.lpszMenuName = 0;
12. wc.lpszClassName = (wchar_t*)cname.utf16();
13. ATOM atom = RegisterClass(&wc);
14. ...
恩,和前面的比对一下,应该是一样的吧(注意这儿注册的回调函数函数名:QtWndProc)
附:调用关系:
- QWidget::create()
· ==>QWidgetPrivate::sys_create()
· ==>qt_reg_winclass()
接前面,不妨直接看看QtWndProc这个回调函数(在同一个文件内), 尽管我们都知道它里面是一个大大的switch语句,我还是贴一点它的代码出来:
[cpp]
1. //
2. // QtWndProc() receives all messages from the main event loop
3. //
4. extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5. {
6. MSG msg;
7. msg.hwnd = hwnd; // create MSG structure
8. msg.message = message; // time and pt fields ignored
9. msg.wParam = wParam;
10. msg.lParam = lParam;
11. msg.pt.x = GET_X_LPARAM(lParam);
12. msg.pt.y = GET_Y_LPARAM(lParam);
13. // If it's a non-client-area message the coords are screen coords, otherwise they are
14. // client coords.
15. if (message < WM_NCMOUSEMOVE || message > WM_NCMBUTTONDBLCLK)
16. ClientToScreen(msg.hwnd, &msg.pt);
17. ...
18. // send through app filter
19. if (qApp->filterEvent(&msg, &res))
20. return res;
21. ...
22. res = 0;
23. if (widget->winEvent(&msg, &res)) // send through widget filter
24. RETURN(res);
25. ...
26. if (qt_is_translatable_mouse_event(message)) {
27. ...
28. }else{
29. switch (message) {
30. ...
31. case WM_MOUSEWHEEL:
32. case WM_MOUSEHWHEEL:
33. result = widget->translateWheelEvent(msg);
34. break;
35. ...
36. }
37. ...
注意:里面出现两处msg消息的过滤。可以对照 Manual 看。
- QCoreApplication::filterEvent()
- QWidget::winEvent()
接下来以whell事件为例,看一下Windows事件如果变成Qt中的事件,并进入Qt自身的消息循环的:
[cpp]
1. bool QETWidget::translateWheelEvent(const MSG &msg)
2. {
3. ...
4. // if there is a widget under the mouse and it is not shadowed
5. // by modality, we send the event to it first
6. int ret = 0;
7. QWidget* w = QApplication::widgetAt(globalPos);
8. if (!w || !qt_try_modal(w, (MSG*)&msg, ret)) {
9. //synaptics touchpad shows its own widget at this position
10. //so widgetAt() will fail with that HWND, try child of this widget
11. w = this->childAt(this->mapFromGlobal(globalPos));
12. if (!w)
13. w = this;
14. }
15. // send the event to the widget or its ancestors
16. {
17. QWidget* popup = QApplication::activePopupWidget();
18. if (popup && w->window() != popup)
19. popup->close();
20. QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
21. Qt::MouseButtons(state & Qt::MouseButtonMask),
22. Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
23. if (QApplication::sendSpontaneousEvent(w, &e))
24. return true;
25. }
26. // send the event to the widget that has the focus or its ancestors, if different
27. if (w != QApplication::focusWidget() && (w = QApplication::focusWidget())) {
28. QWidget* popup = QApplication::activePopupWidget();
29. if (popup && w->window() != popup)
30. popup->close();
31. QWheelEvent e(w->mapFromGlobal(globalPos), globalPos, delta,
32. Qt::MouseButtons(state & Qt::MouseButtonMask),
33. Qt::KeyboardModifier(state & Qt::KeyboardModifierMask), orient);
34. if (QApplication::sendSpontaneousEvent(w, &e))
35. return true;
36. }
37. return false;
通过这个,我们看到:
- 消息函数接受到的消息被封装成相应的QEvent,然后发送到Qt自身的事件循环中。
- 我们可以看到接收事件的对象是如何一步一步被确定的。对于wheel事件:
- 首先是光标下的widget(注意popup widget的处理)
- 如果该widget不接受,则发送到有焦点的widget。
- 通过这儿,我们应该容易理解QWheel中这句话的真实含义了:
在 QDialog 模态对话框与事件循环 与 QEventLoop 的使用两例 等blog中,已经对此做过介绍:QApplication::exec()最终将(在一个while循环内)不断调用 qeventdispatcher_win.cpp 文件中的processEvents函数:
[cpp]
1. bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
2. {
3. ...
4. haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
5. if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
6. && ((msg.message >= WM_KEYFIRST
7. && msg.message <= WM_KEYLAST)
8. || (msg.message >= WM_MOUSEFIRST
9. ...
10. || msg.message == WM_CLOSE)) {
11. // queue user input events for later processing
12. haveMessage = false;
13. d->queuedUserInputEvents.append(msg);
14. }
15. if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
16. && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
17. // queue socket events for later processing
18. haveMessage = false;
19. d->queuedSocketEvents.append(msg);
20. }
21. ...
22. if (!filterEvent(&msg)) {
23. TranslateMessage(&msg);
24. DispatchMessage(&msg);
25. }
26. ...
27. }
这个文件很复杂,此处只摘取了个人感兴趣的片段(其实是因为其他的大段我看不太懂):
· 可以看到win32中熟悉的 PeekMessage、TranslateMessage、DispatchMessage
- 注意用户输入事件和socket通知事件的处理(放入queued队列)
- 注意此处也有一个 filterEvent,和QApplication提供的过滤器比较一下。发现谁更厉害没?
写到到这个地方,似乎从win32到Qt的对比分析已经做完了。恩,我也觉得差不多,只是...
对与 QEventDispatcherWin32 这个东西,我们还有很多话没说。它是事件循环的关键,而且它不止在主线程使用(我们肯定都知道QThread::exec())。
在QTimer源码分析(以Windows下实现为例) 我们提到了它和定时器Timer的密切关系,刚刚又提到它是事件循环的关键,还有一点似乎还需要提一下:
- QApplication 初始化时创建该对象
[cpp]
1. void QApplicationPrivate::createEventDispatcher()
2. {
3. Q_Q(QApplication);
4. if (q->type() != QApplication::Tty)
5. eventDispatcher = new QGuiEventDispatcherWin32(q);
6. else
7. eventDispatcher = new QEventDispatcherWin32(q);
8. }
- 在构造函数中,它创建并注册了一个内部用的窗口类别,而后创建一个窗口。
[cpp]
1. static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
2. {
3. // make sure that multiple Qt's can coexist in the same process
4. QString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc));
5. WNDCLASS wc;
6. wc.style = 0;
7. wc.lpfnWndProc = qt_internal_proc;
8. wc.cbClsExtra = 0;
9. wc.cbWndExtra = 0;
10. wc.hInstance = qWinAppInst();
11. wc.hIcon = 0;
12. wc.hCursor = 0;
13. wc.hbrBackground = 0;
14. wc.lpszMenuName = NULL;
15. wc.lpszClassName = reinterpret_cast<const wchar_t *> (className.utf16());
16. RegisterClass(&wc);
17. HWND wnd = CreateWindow(wc.lpszClassName, // classname
18. wc.lpszClassName, // window name
19. 0, // style
20. 0, 0, 0, 0, // geometry
21. 0, // parent
22. 0, // menu handle
23. qWinAppInst(), // application
24. 0); // windows creation data.
25. ...
其回调函数 qt_internal_proc 也在该文件内(略过)
- 在创建内部窗口的同时,它还安装了一个钩子(Hook)
[cpp]
1. d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
2. if (!d->getMessageHook) {
3. qFatal("Qt: INTERNALL ERROR: failed to install GetMessage hook");
4. }
· 在钩子的回调函数中,一些消息被PostMessage到上面提到的内部窗口中
[cpp]
1. LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
2. {
3. ...
4. MSG *msg = (MSG *) lp;
5. if (localSerialNumber != d->lastSerialNumber
6. // if this message IS the one that triggers sendPostedEvents(), no need to post it again
7. && (msg->hwnd != d->internalHwnd
8. || msg->message != WM_QT_SENDPOSTEDEVENTS)) {
9. PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
总算大体上理了一遍,尽管很多东西还是不懂。有错误欢迎大家指出哈 dbzhang800 2011.04.28
写的途中发现一个技术大牛 tingsking18 写过这方面的东西了(链接附文后),不过我还是认为自己的更详细一点。
- Programming Windows
· http://blog.csdn.net/tingsking18/archive/2009/10/28/4737925.aspx
若要完成本演练,你必须了解 C++ 语言的基础知识。
有关视频演示,请参见 Visual Studio 2008 文档中的 Video How to: Creating Win32 Applications (C++)(视频帮助:创建 Win32 应用程序 (C++))。
创建基于 Win32 的项目
-
在“文件”菜单上,单击“新建”,然后单击“项目”。
-
在“新建项目”对话框的左侧窗格中,单击“已安装的模板”,单击“Visual C++”,然后选择“Win32”。 在中间窗格中,选择“Win32 项目”。
在“名称”框中,键入项目名称,例如 win32app。 单击“确定”。
-
在“Win32 应用程序向导”的“欢迎”页上,单击“下一步”。
-
在“应用程序设置”页上的在“应用程序类型”下,选择“Windows 应用程序”。 在“附加选项”下,选择“空项目”。 单击“完成”创建项目。
-
在“解决方案资源管理器”中,右击 Win32app 项目,单击“添加”,然后单击“新建项”。 在“添加新项”对话框中选择“C++ 文件(.cpp)”。 在“名称”框中,键入文件名称,例如 GT_HelloWorldWin32.cpp。 单击“添加”。
启动基于 Win32 的应用程序
-
就像每个 C 应用程序和 C++ 应用程序都以 main 函数作为起点那样,每个基于 Win32 的应用程序同样必须要有 WinMain 函数。 WinMain 具有以下语法。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
有关此函数的参数和返回值的信息,请参见 WinMain 函数。
-
由于应用程序代码必须使用现有定义,因此应将 include 语句添加到文件中。
#include <windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h>
-
除 WinMain 函数外,每个基于 Win32 的应用程序还必须具有一个窗口过程函数。 此函数通常名为 WndProc。 WndProc 具有以下语法。
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
此函数处理应用程序从操作系统接收的许多消息。 例如,在具有对话框(该对话框中有一个“确定”按钮)的应用程序中,如果用户单击该按钮,操作系统就会向该应用程序发送一条消息,告知已单击该按钮。 WndProc 负责响应该事件。 在此示例中,相应的响应可能是关闭该对话框。
有关更多信息,请参见窗口过程。
向 WinMain 函数添加功能
-
在 WinMain 函数中,创建 WNDCLASSEX 类型的窗口类结构。 此结构包含有关该窗口的信息,例如,应用程序图标、窗口的背景色、要在标题栏中显示的名称、窗口过程函数的名称等等。 下面的示例演示一个典型 WNDCLASSEX 结构。
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
有关此结构的各字段的信息,请参见 WNDCLASSEX。
-
现在您已经创建一个窗口类,接下来必须将其注册。 使用 RegisterClassEx 函数,并将窗口类结构作为参数进行传递。
if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL); return 1; }
-
现在可以创建一个窗口。 使用 CreateWindow 函数。
static TCHAR szWindowClass[] = _T("win32app"); static TCHAR szTitle[] = _T("Win32 Guided Tour Application"); // The parameters to CreateWindow explained: // szWindowClass: the name of the application // szTitle: the text that appears in the title bar // WS_OVERLAPPEDWINDOW: the type of window to create // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y) // 500, 100: initial size (width, length) // NULL: the parent of this window // NULL: this application does not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL); return 1; }
此函数返回 HWND,它是某个窗口的句柄。 有关更多信息,请参见 Windows 数据类型。
-
现在,使用下列代码来显示窗口。
// The parameters to ShowWindow explained: // hWnd: the value returned from CreateWindow // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
此时,所显示的窗口不会有太多内容,因为您尚未实现 WndProc 函数。
-
现在添加一个消息循环以侦听操作系统发送的消息。 如果应用程序收到一条消息,则此循环会将该消息调度至 WndProc 函数以接受处理。 消息循环类似于下列代码。
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam;
有关消息循环中各结构和函数的更多信息,请参见 MSG、GetMessage、TranslateMessage 和 DispatchMessage。
此时,WinMain 函数应与下列代码类似。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL); return 1; } hInst = hInstance; // Store instance handle in our global variable // The parameters to CreateWindow explained: // szWindowClass: the name of the application // szTitle: the text that appears in the title bar // WS_OVERLAPPEDWINDOW: the type of window to create // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y) // 500, 100: initial size (width, length) // NULL: the parent of this window // NULL: this application dows not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL); return 1; } // The parameters to ShowWindow explained: // hWnd: the value returned from CreateWindow // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // Main message loop: MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; }
向 WndProc 函数添加功能
-
若要启用 WndProc 函数来处理应用程序所收到的消息,请实现 switch 语句。
要处理的第一条消息是 WM_PAINT 消息。 如果必须更新所显示的应用程序窗口的一部分,该应用程序就会收到此消息。(首次显示该窗口时,必须将其全部更新。)
若要处理 WM_PAINT 消息,请首先调用 BeginPaint,然后处理用于布局该窗口中的文本、按钮和其他控件的所有逻辑,再调用 EndPaint。 对于此应用程序,开始调用和结束调用之间的逻辑会在窗口中显示字符串“Hello, World!”。 在下列代码中,请注意 TextOut 函数用于显示该字符串。
PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, World!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, World!" // in the top left corner. TextOut(hdc, 5, 5, greeting, _tcslen(greeting)); // End application-specific layout section. EndPaint(hWnd, &ps); break; }
-
应用程序通常会处理许多其他消息,例如 WM_CREATE 和 WM_DESTROY。 下列代码展示了一个基本但完整的 WndProc 函数。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, World!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, World!" // in the top left corner. TextOut(hdc, 5, 5, greeting, _tcslen(greeting)); // End application specific layout section. EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; }
生成此示例
-
创建本演练中之前“创建基于 Win32 的项目”中的基于 Win32 的项目。
-
复制这些步骤之后的代码,然后将其粘贴到 GT_HelloWorldWin32.cpp 源文件中。
-
在“生成”菜单上,单击“生成解决方案”。
-
若要运行该应用程序,请按 F5。 在显示屏的左上角应出现一个窗口,窗口中含有文本“Hello World!”。
代码
// GT_HelloWorldWin32.cpp // compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c #include <windows.h> #include <stdlib.h> #include <string.h> #include <tchar.h> // Global variables // The main window class name. static TCHAR szWindowClass[] = _T("win32app"); // The string that appears in the application's title bar. static TCHAR szTitle[] = _T("Win32 Guided Tour Application"); HINSTANCE hInst; // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL); return 1; } hInst = hInstance; // Store instance handle in our global variable // The parameters to CreateWindow explained: // szWindowClass: the name of the application // szTitle: the text that appears in the title bar // WS_OVERLAPPEDWINDOW: the type of window to create // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y) // 500, 100: initial size (width, length) // NULL: the parent of this window // NULL: this application does not have a menu bar // hInstance: the first parameter from WinMain // NULL: not used in this application HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL ); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL); return 1; } // The parameters to ShowWindow explained: // hWnd: the value returned from CreateWindow // nCmdShow: the fourth parameter from WinMain ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // Main message loop: MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; TCHAR greeting[] = _T("Hello, World!"); switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Here your application is laid out. // For this introduction, we just print out "Hello, World!" // in the top left corner. TextOut(hdc, 5, 5, greeting, _tcslen(greeting)); // End application-specific layout section. EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; }
相关推荐
QT是一种跨平台的应用程序...通过这个项目,开发者可以学习到如何使用QT来创建具有交互性的GUI应用,理解信号与槽机制,以及如何处理用户输入和执行命令。这不仅是一个基础的GUI编程练习,也是深入理解QT框架的好起点。
本文将详细介绍如何在Qt框架下利用qbreakpad库,实现Windows(Win32)、MacOS和Linux平台的跨平台异常捕获与分析。 首先,让我们了解核心组件——qbreakpad。qbreakpad是基于Google Breakpad的Qt接口,它允许开发者...
首先,`VLC-Qt_1.1.0_win32_mingw.7z`可能是VLC-Qt库的一个预编译版本,适用于Windows 32位系统,并且基于MinGW编译环境。VLC-Qt是VLC和Qt之间的桥梁,它提供了一系列的C++类和函数,使得在Qt应用中轻松集成VLC...
"Win32"可能是指这个程序也支持32位Windows系统,或者包含了特定于32位Windows的代码或配置。 综上所述,这个项目是一个使用C++和Qt框架编写的远程桌面监控程序,具有实时监控、鼠标锁定和局域网自动发现等功能。其...
学习这个源码,你可以了解Qt的GUI设计、事件处理、多线程(如果涉及到AI的异步计算)、文件I/O(保存和加载游戏状态)以及简单的游戏算法。同时,对于想要提升C++和Qt编程能力的人来说,这是一个很好的实践项目。...
在开发过程中,Qt Creator提供了强大的调试工具,如源码级调试、内存检查和性能分析器。这些工具可以帮助你定位和解决问题,优化代码。同时,Qt的API文档非常详尽,是学习和解决问题的重要资源。 总之,安装和配置...
Qt+Cutelyst学习笔记(二十七)win10+Qt5.15.2+qmake实现一个简单的RESTful服务器 示例源码 https://blog.csdn.net/aggs1990/article/details/124115368 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下
Qt+Cutelyst学习笔记(十二)win10 + Qt5.15.2 使用视图和模板 示例源码 https://blog.csdn.net/aggs1990/article/details/123903752 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下
QT5Checker4Win.zip是一个包含Windows平台上用于获取硬件信息的源码项目的压缩包。这个项目主要基于QT5框架,适用于Windows 10 LTSB(长期服务分支)操作系统,并且已经过与QT5.5.9版本及Visual Studio 2017的兼容性...
2. **获取源代码**:从Qt官方仓库或者第三方资源获取Qt的源代码,特别是sqldrivers模块,它包含了各种数据库驱动的源码。 3. **编译驱动插件**:使用qmake和mingw32-make命令来编译MySQL驱动插件。你需要指定正确的...
此外,不要忘记选择你想开发的应用程序平台,比如Win32或x64。 3. **Qt版本管理**:如果你计划同时开发多个Qt项目,可能需要安装多个Qt版本。Qt安装器允许你管理这些版本,以便在不同项目间切换。 4. **模块选择**...
1. **下载并解压OpenCV**:从官方网站下载对应版本的OpenCV源码或预编译包,然后解压至指定路径(例如:`D:\Program\Opencv2413-minGW53-32`)。 - 如果使用的是MinGW编译器,需要通过`cmake`工具对下载的OpenCV...
Qt+Cutelyst学习笔记(二十三)win10+Qt5.15.2 添加认证 示例源码 https://blog.csdn.net/aggs1990/article/details/123997789 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下
通过学习和研究这个项目的源码,开发者可以深入了解如何在Qt环境中使用Windows API,以及如何构建一个数据获取工具。这有助于提升对Windows系统底层运作的理解,同时增强在Qt环境中进行复杂应用开发的能力。对于想要...
QT+Basler相机SDK开发源码与详细说明文档是一份非常宝贵的资源,它结合了QT图形用户界面框架和Basler...通过学习和理解这份源码,开发者可以提升在QT和工业相机集成方面的技能,进一步优化图像处理流程,提高生产效率。
2. **解压源代码**:将"qt-win-opensource-src-4.5.2"解压到本地目录。 3. **配置编译选项**:根据需求配置QT的编译选项,如是否编译示例程序、文档等。 4. **编译源码**:使用编译器编译QT源代码,生成库文件。 5. ...
- 为了在win10环境下编译通过,确保你的项目配置正确地指定了mingw32编译器,并且包含了必要的Qt库和模块。 总的来说,理解和掌握Qt中的鼠标和键盘事件处理是构建交互式图形界面的关键步骤。通过实践和研究...
Qt+Cutelyst学习笔记(二十八)win10+Qt5.15.2+qmake接收表单提交的文件 示例源码 https://blog.csdn.net/aggs1990/article/details/124128783 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下
总结来说,QT6源码编译工具集合为开发者提供了一套完整的环境,涵盖了从源码配置到编译执行的各个环节,确保用户能够在Windows平台上顺利构建和使用QT6库。通过VS2019、Perl、Python和ninja的组合,开发者可以高效地...
Qt+Cutelyst学习笔记(十六)win10+Qt5.15.2 添加基本样式表 示例源码 https://blog.csdn.net/aggs1990/article/details/123942997 CSDN审核可能较慢,如无法下载,可以过段时间再回来看下