`
美丽的小岛
  • 浏览: 297695 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Qt源码学习(从Win32到Qt)

 
阅读更多

本文以一个Win32helloworld程序开篇,

·         程序入口WinMain

  • 注册窗口类别
  • 建立窗口,在屏幕上显示
  • 进入事件循环,不断从事件队列中取出消息来处理

而后尝试解释前述各部分分别隐藏在Qt何处:

main()

程序入口

Qt提供一个WinMain来调用main

QWidget::show()

注册窗口类别

第一次使用时会注册类别

显示窗体

hidesetHidden都是setVisble的马甲

QApplication::exec()

进入事件循环

核心是 QEventDispatcherWin32

声明,我对Win32编程不了解,本文只是Qt学习过程中的学习笔记,本文提到内容以及用词描述可能会不太准确。

一个简单的Win32程序

 

这是Win32编程的一个hello world程序:

·         包含头文件,定义入口函数WinMain

[cpp] 

1.   #include <windows.h>  

2.   LRESULT CALLBACK WndProc(HWNDUINTWPARAMLPARAM);  

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

 

clgcc编译该程序:

[cpp] 

1.   gcc hello.c -o hello -lgdi32 -Wl,-subsystem,windows  

2.   cl hello.c user32.lib gdi32.lib  

 

考虑Qt

 

既然是 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.liblibqtmain.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中熟悉的 PeekMessageTranslateMessageDispatchMessage

  • 注意用户输入事件和socket通知事件的处理(放入queued队列)
  • 注意此处也有一个 filterEvent,和QApplication提供的过滤器比较一下。发现谁更厉害没?

写到到这个地方,似乎从win32Qt的对比分析已经做完了。恩,我也觉得差不多,只是...

对与 QEventDispatcherWin32 这个东西,我们还有很多话没说。它是事件循环的关键,而且它不止在主线程使用(我们肯定都知道QThread::exec())

QEventDispatcherWin32补遗

 

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 的项目

  1. “文件”菜单上,单击“新建”,然后单击“项目”

  2. “新建项目”对话框的左侧窗格中,单击“已安装的模板”,单击“Visual C++”,然后选择“Win32”。 在中间窗格中,选择“Win32 项目”

    “名称”框中,键入项目名称,例如 win32app。 单击“确定”

  3. “Win32 应用程序向导”的“欢迎”页上,单击“下一步”

  4. 在“应用程序设置”页上的在“应用程序类型”下,选择“Windows 应用程序”。 “附加选项”下,选择“空项目” 单击“完成”创建项目。

  5. “解决方案资源管理器”中,右击 Win32app 项目,单击“添加”,然后单击“新建项”。 “添加新项”对话框中选择“C++ 文件(.cpp)” “名称”框中,键入文件名称,例如 GT_HelloWorldWin32.cpp 单击“添加”

启动基于 Win32 的应用程序

  1. 就像每个 C 应用程序和 C++ 应用程序都以 main 函数作为起点那样,每个基于 Win32 的应用程序同样必须要有 WinMain 函数。 WinMain 具有以下语法。

     
     
    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow);
    

    有关此函数的参数和返回值的信息,请参见 WinMain 函数

  2. 由于应用程序代码必须使用现有定义,因此应将 include 语句添加到文件中。

     
     
    #include <windows.h>
    #include <stdlib.h>
    #include <string.h>
    #include <tchar.h>
    
  3. 除 WinMain 函数外,每个基于 Win32 的应用程序还必须具有一个窗口过程函数。 此函数通常名为 WndProc WndProc 具有以下语法。

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

    此函数处理应用程序从操作系统接收的许多消息。 例如,在具有对话框(该对话框中有一个“确定”按钮)的应用程序中,如果用户单击该按钮,操作系统就会向该应用程序发送一条消息,告知已单击该按钮。 WndProc 负责响应该事件。 在此示例中,相应的响应可能是关闭该对话框。

    有关更多信息,请参见窗口过程

向 WinMain 函数添加功能

  1. 在 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

  2. 现在您已经创建一个窗口类,接下来必须将其注册。 使用 RegisterClassEx 函数,并将窗口类结构作为参数进行传递。

     
     
        if (!RegisterClassEx(&wcex))
        {
            MessageBox(NULL,
                _T("Call to RegisterClassEx failed!"),
                _T("Win32 Guided Tour"),
                NULL);
    
            return 1;
        }
    
  3. 现在可以创建一个窗口。 使用 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 数据类型

  4. 现在,使用下列代码来显示窗口。

     
     
    // The parameters to ShowWindow explained:
    // hWnd: the value returned from CreateWindow
    // nCmdShow: the fourth parameter from WinMain
    ShowWindow(hWnd,
        nCmdShow);
    UpdateWindow(hWnd);
    

    此时,所显示的窗口不会有太多内容,因为您尚未实现 WndProc 函数。

  5. 现在添加一个消息循环以侦听操作系统发送的消息。 如果应用程序收到一条消息,则此循环会将该消息调度至 WndProc 函数以接受处理。 消息循环类似于下列代码。

     
     
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return (int) msg.wParam;
    

    有关消息循环中各结构和函数的更多信息,请参见 MSGGetMessageTranslateMessage 和 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 函数添加功能

  1. 若要启用 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;
    }
    
  2. 应用程序通常会处理许多其他消息,例如 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;
    }
    
示例
 

生成此示例

  1. 创建本演练中之前“创建基于 Win32 的项目”中的基于 Win32 的项目。

  2. 复制这些步骤之后的代码,然后将其粘贴到 GT_HelloWorldWin32.cpp 源文件中。

  3. “生成”菜单上,单击“生成解决方案”

  4. 若要运行该应用程序,请按 F5。 在显示屏的左上角应出现一个窗口,窗口中含有文本“Hello World!”。

Bb384843.collapse_all(zh-cn,VS.120).gif代码

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

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics