wxWidgets学习笔记(3)wxWidgets程序生死因果

     本文将分析wxWidgets应用程序如何开始执行、如何结束,如何创建主窗口,程序如何推动等问题。

    1 三个不同版本的 Hello World

    1-1  Hello World (Win32 SDK版)
    先看一下Win32 SDK简单程序,著名的Hello World。本程序由Code::Block在XP下通过向导生成,程序运行结果如图所示。
    wxWidgets学习笔记(3)wxWidgets程序生死因果_第1张图片
  1. #include 
  2. /*  Declare Windows procedure  */
  3. LRESULT CALLBACK WindowProcedure (HWNDUINTWPARAMLPARAM);
  4. /*  Make the class name into a global variable  */
  5. char szClassName[ ] = "CodeBlocksWindowsApp";
  6. int WINAPI WinMain (HINSTANCE hThisInstance,                 // 1 程序入口 WinMain(...)
  7.                      HINSTANCE hPrevInstance,
  8.                      LPSTR lpszArgument,
  9.                      int nCmdShow)
  10. {
  11.     HWND hwnd;               /* This is the handle for our window */
  12.     MSG messages;            /* Here messages to the application are saved */
  13.     WNDCLASSEX wincl;        /* Data structure for the windowclass */
  14.     /* The Window structure */
  15.     wincl.hInstance = hThisInstance;
  16.     wincl.lpszClassName = szClassName;
  17.     wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
  18.     wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
  19.     wincl.cbSize = sizeof (WNDCLASSEX);
  20.     /* Use default icon and mouse-pointer */
  21.     wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
  22.     wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  23.     wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
  24.     wincl.lpszMenuName = NULL;                 /* No menu */
  25.     wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
  26.     wincl.cbWndExtra = 0;                      /* structure or the window instance */
  27.     /* Use Windows's default colour as the background of the window */
  28.     wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  29.     /* Register the window class, and if it fails quit the program */
  30.     if (!RegisterClassEx (&wincl))                                   // 2 注册窗口
  31.         return 0;
  32.     /* The class is registered, let's create the program*/
  33.     hwnd = CreateWindowEx (
  34.            0,                   /* Extended possibilites for variation */
  35.            szClassName,         /* Classname */
  36.            "Hello World!",       /* Title Text */
  37.            WS_OVERLAPPEDWINDOW, /* default window */
  38.            CW_USEDEFAULT,       /* Windows decides the position */
  39.            CW_USEDEFAULT,       /* where the window ends up on the screen */
  40.            544,                 /* The programs width */
  41.            375,                 /* and height in pixels */
  42.            HWND_DESKTOP,        /* The window is a child-window to desktop */
  43.            NULL,                /* No menu */
  44.            hThisInstance,       /* Program Instance handler */
  45.            NULL                 /* No Window Creation data */
  46.            );
  47.     /* Make the window visible on the screen */
  48.     ShowWindow (hwnd, nCmdShow);                  // 3 生成窗口并显示窗口
  49.     /* Run the message loop. It will run until GetMessage() returns 0 */
  50.     while (GetMessage (&messages, NULL, 0, 0))    // 4 消息循环,响应用户命令
  51.     {
  52.         /* Translate virtual-key messages into character messages */
  53.         TranslateMessage(&messages);
  54.         /* Send message to WindowProcedure */
  55.         DispatchMessage(&messages);
  56.     }
  57.     /* The program return-value is 0 - The value that PostQuitMessage() gave */
  58.     return messages.wParam;
  59. }
  60.                                                                     // 5 窗口处理过程
  61. /*  This function is called by the Windows function DispatchMessage()  */
  62. LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  63. {
  64.     switch (message)                  /* handle the messages */
  65.     {
  66.         case WM_DESTROY:
  67.             PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
  68.             break;
  69.         default:                      /* for messages that we don't deal with */
  70.             return DefWindowProc (hwnd, message, wParam, lParam);
  71.     }
  72.     return 0;
  73. }
    可以看出,Win32 SDK程序
    1) 从WinMain(...)开始执行;
    2) 通过RegisterClassEx(...)注册窗口类;
    3)通过CreateWindowEx(...)创建主窗口,并通过ShowWindow(...)显示窗口;
    4)通过 while (GetMessage (&messages, NULL, 0, 0))     消息循环,等待响应用户命令;
    5) 窗口处理过程为WindowProcedure(...),在注册窗口的时候绑定。

    1-2 Hello World (GTK+ 版)
    再看一下GTK+ 简单程序,也是著名的Hello World。本程序由Code::Block在Ubuntu下通过向导生成,程序运行结果如图所示。
wxWidgets学习笔记(3)wxWidgets程序生死因果_第2张图片

  1. #include 
  2. #include 
  3.                                                    //5 消息处理函数,通过g_signal_connect与窗口、控件绑定
  4. static void helloWorld (GtkWidget *wid, GtkWidget *win)
  5. {
  6.   GtkWidget *dialog = NULL;
  7.   dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "Hello World!");
  8.   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
  9.   gtk_dialog_run (GTK_DIALOG (dialog));
  10.   gtk_widget_destroy (dialog);
  11. }
  12. int main (int argc, char *argv[])     // 1 程序入口 main(...)
  13. {
  14.   GtkWidget *button = NULL;
  15.   GtkWidget *win = NULL;
  16.   GtkWidget *vbox = NULL;
  17.   /* Initialize GTK+ */                         // 2 初始化GTK+
  18.   g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
  19.   gtk_init (&argc, &argv);
  20.   g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);
  21.   /* Create the main window */        // 3 生成主窗口以及窗口内的控件,并通过 g_signal_connect绑定消息处理函数。
  22.   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  23.   gtk_container_set_border_width (GTK_CONTAINER (win), 8);
  24.   gtk_window_set_title (GTK_WINDOW (win), "Hello World");
  25.   gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
  26.   gtk_widget_realize (win);
  27.   g_signal_connect (win, "destroy", gtk_main_quit, NULL);
  28.   /* Create a vertical box with buttons */
  29.   vbox = gtk_vbox_new (TRUE, 6);
  30.   gtk_container_add (GTK_CONTAINER (win), vbox);
  31.   button = gtk_button_new_from_stock (GTK_STOCK_DIALOG_INFO);
  32.   g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (helloWorld), (gpointer) win);
  33.   gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  34.   button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
  35.   g_signal_connect (button, "clicked", gtk_main_quit, NULL);
  36.   gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  37.   /* Enter the main loop */        //4 进入消息循环,响应用户命令。
  38.   gtk_widget_show_all (win);
  39.   gtk_main ();
  40.   return 0;
  41. }

    与Win32 SDK程序类似,
    1)从main(...)作为程序入口,开始执行;
    2)gtk_init(...)初始化GTK+;
    3)gtk_window_new(...)生成主窗口;
    4)   gtk_main ()  进入消息循环,响应用户命令。

    1-3 Hello World(wxWidgets版)
    最后看一下wxWidgets应用程序,源代码同样通过Code::Block向导生成,在XP和Ubuntu下生成的源代码没有什么区别。在Ubuntu下编译运行的结果如图。
wxWidgets学习笔记(3)wxWidgets程序生死因果_第3张图片
    程序包含4个文件:
 
  1. HelloWorld.h
  2. #ifndef HELLOWORLDAPP_H
  3. #define HELLOWORLDAPP_H
  4. #include 
  5. class HelloWorldApp : public wxApp
  6. {
  7.     public:
  8.         virtual bool OnInit();
  9. };
  10. #endif // HELLOWORLDAPP_H
  11. HelloWorld.cpp
  12. #ifdef WX_PRECOMP
  13. #include "wx_pch.h"
  14. #endif
  15. #ifdef __BORLANDC__
  16. #pragma hdrstop
  17. #endif //__BORLANDC__
  18. #include "HelloWorldApp.h"
  19. #include "HelloWorldMain.h"
  20. IMPLEMENT_APP(HelloWorldApp);
  21. bool HelloWorldApp::OnInit()
  22. {
  23.     HelloWorldFrame* frame = new HelloWorldFrame(0L, _("Hello World!"));
  24.     frame->Show();
  25.     return true;
  26. }
  27. HelloWorldMain.h
  28. #ifndef HELLOWORLDMAIN_H
  29. #define HELLOWORLDMAIN_H
  30. #ifndef WX_PRECOMP
  31.     #include 
  32. #endif
  33. #include "HelloWorldApp.h"
  34. class HelloWorldFrame: public wxFrame
  35. {
  36.     public:
  37.         HelloWorldFrame(wxFrame *frame, const wxString& title);
  38.         ~HelloWorldFrame();
  39.     private:
  40.         enum
  41.         {
  42.             idMenuQuit = 1000,
  43.             idMenuAbout
  44.         };
  45.         void OnClose(wxCloseEvent& event);
  46.         void OnQuit(wxCommandEvent& event);
  47.         void OnAbout(wxCommandEvent& event);
  48.         DECLARE_EVENT_TABLE()
  49. };
  50. #endif // HELLOWORLDMAIN_H
  51. HelloWorldMain.cpp
  52. #ifdef WX_PRECOMP
  53. #include "wx_pch.h"
  54. #endif
  55. #ifdef __BORLANDC__
  56. #pragma hdrstop
  57. #endif //__BORLANDC__
  58. #include "HelloWorldMain.h"
  59. //helper functions
  60. enum wxbuildinfoformat {
  61.     short_f, long_f };
  62. wxString wxbuildinfo(wxbuildinfoformat format)
  63. {
  64.     wxString wxbuild(wxVERSION_STRING);
  65.     if (format == long_f )
  66.     {
  67. #if defined(__WXMSW__)
  68.         wxbuild << _T("-Windows");
  69. #elif defined(__WXMAC__)
  70.         wxbuild << _T("-Mac");
  71. #elif defined(__UNIX__)
  72.         wxbuild << _T("-Linux");
  73. #endif
  74. #if wxUSE_UNICODE
  75.         wxbuild << _T("-Unicode build");
  76. #else
  77.         wxbuild << _T("-ANSI build");
  78. #endif // wxUSE_UNICODE
  79.     }
  80.     return wxbuild;
  81. }
  82. BEGIN_EVENT_TABLE(HelloWorldFrame, wxFrame)
  83.     EVT_CLOSE(HelloWorldFrame::OnClose)
  84.     EVT_MENU(idMenuQuit, HelloWorldFrame::OnQuit)
  85.     EVT_MENU(idMenuAbout, HelloWorldFrame::OnAbout)
  86. END_EVENT_TABLE()
  87. HelloWorldFrame::HelloWorldFrame(wxFrame *frame, const wxString& title)
  88.     : wxFrame(frame, -1, title)
  89. {
  90. #if wxUSE_MENUS
  91.     // create a menu bar
  92.     wxMenuBar* mbar = new wxMenuBar();
  93.     wxMenu* fileMenu = new wxMenu(_T(""));
  94.     fileMenu->Append(idMenuQuit, _("&Quit/tAlt-F4"), _("Quit the application"));
  95.     mbar->Append(fileMenu, _("&File"));
  96.     wxMenu* helpMenu = new wxMenu(_T(""));
  97.     helpMenu->Append(idMenuAbout, _("&About/tF1"), _("Show info about this application"));
  98.     mbar->Append(helpMenu, _("&Help"));
  99.     SetMenuBar(mbar);
  100. #endif // wxUSE_MENUS
  101. #if wxUSE_STATUSBAR
  102.     // create a status bar with some information about the used wxWidgets version
  103.     CreateStatusBar(2);
  104.     SetStatusText(_("Hello World!"),0);
  105.     SetStatusText(wxbuildinfo(short_f), 1);
  106. #endif // wxUSE_STATUSBAR
  107. }
  108. HelloWorldFrame::~HelloWorldFrame()
  109. {
  110. }
  111. void HelloWorldFrame::OnClose(wxCloseEvent &event)
  112. {
  113.     Destroy();
  114. }
  115. void HelloWorldFrame::OnQuit(wxCommandEvent &event)
  116. {
  117.     Destroy();
  118. }
  119. void HelloWorldFrame::OnAbout(wxCommandEvent &event)
  120. {
  121.     wxString msg = wxbuildinfo(long_f);
  122.     wxMessageBox(msg, _("Welcome to..."));
  123. }
    从程序代码中,我们
    1)看不到WinMain或者是main,那么程序是从那里开始执行的呢?
    2)看不到CreateWindowEx或者是gtk_window_new,主窗口是如何产生的呢?
    3)看不到消息循环 while (GetMessage (&messages, NULL, 0, 0))  或者是gtk_main (),程序如何推动的呢?
    4)在XP下使用SDK API,在Ubuntu下使用GTK,但是wxWidgets应用程序在Window XP下的源代码和Ubuntu下的源代码几乎完全一致,wxWidgets如何实现的呢?
    显然,wxWidgets在背后为我们做了大量的工作。

    2 wxWidgets 程序生因死果
   从1-3的程序里可以看出,这个简单的程序里有两个主要的类:HelloWorldMain和HelloWorldFrame。分别继承于wxApp和wxFrame。其中wxApp代表了程序主体,wxFrame代表一个主框窗口。从这两个类里看不出程序的入口,那么程序是从哪里开始执行的呢?

    2-1 程序入口
    仔细分析程序,在HelloWorld.cpp中,我们发现了一个宏IMPLEMENT_APP(HelloWorldApp),这究竟代表了什么呢?
    查看wxWidgets源代码,我们可以在wx/app.h中发现IMPLEMENT_APP的定义:
  1.  wx/app.h
  2. #define wxTheApp wx_static_cast(wxApp*, wxApp::GetInstance())
  3. // Use this macro exactly once, the argument is the name of the wxApp-derived
  4. // class which is the class of your application.
  5. #define IMPLEMENT_APP(appname)              /
  6.     IMPLEMENT_APP_NO_THEMES(appname)        /
  7.     IMPLEMENT_WX_THEME_SUPPORT
  8. // Same as IMPLEMENT_APP() normally but doesn't include themes support in
  9. // wxUniversal builds
  10. #define IMPLEMENT_APP_NO_THEMES(appname)    /
  11.     IMPLEMENT_APP_NO_MAIN(appname)          /
  12.     IMPLEMENT_WXWIN_MAIN
  13. // Use this macro if you want to define your own main() or WinMain() function
  14. // and call wxEntry() from there.
  15. #define IMPLEMENT_APP_NO_MAIN(appname)                                      /
  16.     wxAppConsole *wxCreateApp()                                             /
  17.     {                                                                       /
  18.         wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE,         /
  19.                                         "your program");                    /
  20.         return new appname;                                                 /
  21.     }                                                                       /
  22.     wxAppInitializer                                                        /
  23.         wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp);        /
  24.     DECLARE_APP(appname)                                                    /
  25.     appname& wxGetApp() { return *wx_static_cast(appname*, wxApp::GetInstance()); }
  26. // this macro can be used multiple times and just allows you to use wxGetApp()
  27. // function
  28. #define DECLARE_APP(appname) extern appname& wxGetApp();
     其中 IMPLEMENT_WXWIN_MAIN针对不同平台有不同的定义,wxWidgets通过定义不同的宏来区分:
  1. #if defined(__WXPALMOS__)
  2.         #include "wx/palmos/app.h"
  3.     #elif defined(__WXMSW__)
  4.         #include "wx/msw/app.h"
  5.     #elif defined(__WXMOTIF__)
  6.         #include "wx/motif/app.h"
  7.     #elif defined(__WXMGL__)
  8.         #include "wx/mgl/app.h"
  9.     #elif defined(__WXDFB__)
  10.         #include "wx/dfb/app.h"
  11.     #elif defined(__WXGTK20__)
  12.         #include "wx/gtk/app.h"
  13.     #elif defined(__WXGTK__)
  14.         #include "wx/gtk1/app.h"
  15.     #elif defined(__WXX11__)
  16.         #include "wx/x11/app.h"
  17.     #elif defined(__WXMAC__)
  18.         #include "wx/mac/app.h"
  19.     #elif defined(__WXCOCOA__)
  20.         #include "wx/cocoa/app.h"
  21.     #elif defined(__WXPM__)
  22.         #include "wx/os2/app.h"
  23.     #endif
    这样,不同的平台引用相应的文件,实现不同平台下的功能。
    在GTK下,IMPLEMENT_WXWIN_MAIN的定义如下:
  1.  #define IMPLEMENT_WXWIN_MAIN_CONSOLE /
  2.         int main(int argc, char **argv) { return wxEntry(argc, argv); }
  3.     // port-specific header could have defined it already in some special way
  4.     #ifndef IMPLEMENT_WXWIN_MAIN
  5.         #define IMPLEMENT_WXWIN_MAIN IMPLEMENT_WXWIN_MAIN_CONSOLE
  6.     #endif 
    而Window下,定义于wx/msw/app.h中:
  1. #define IMPLEMENT_WXWIN_MAIN /
  2.     extern "C" int WINAPI WinMain(HINSTANCE hInstance,                    /
  3.                                   HINSTANCE hPrevInstance,                /
  4.                                   wxCmdLineArgType lpCmdLine,             /
  5.                                   int nCmdShow)                           /
  6.     {                                                                     /
  7.         return wxEntry(hInstance, hPrevInstance, lpCmdLine, nCmdShow);    /
  8.     }                                                                     /
  9.     IMPLEMENT_WXWIN_MAIN_BORLAND_NONSTANDARD
    我们把IMPLEMENT_APP(HelloWorldApp)展开,在GTK下为:
  1.    wxAppConsole *wxCreateApp()                                             
  2.     {                                                                       
  3.         wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE,         
  4.                                         "your program");                  
  5.         return new HelloWorldApp;                                                 
  6.     }                                                                      
  7.     wxAppInitializer                                                       
  8.         wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp);       
  9.    extern HelloWorldApp& wxGetApp();                                              
  10.     HelloWorldApp& wxGetApp() { return *wx_static_cast(HelloWorldApp*, wxApp::GetInstance()); }
  11.   
  12.    int main(int argc, char **argv) { return wxEntry(argc, argv); }
    而在Window下,为:
  1.      wxAppConsole *wxCreateApp()                                             
  2.     {                                                                       
  3.         wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE,         
  4.                                         "your program");                  
  5.         return new HelloWorldApp;                                                 
  6.     }                                                                      
  7.     wxAppInitializer                                                       
  8.         wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp);       
  9.    extern HelloWorldApp& wxGetApp();                                              
  10.     HelloWorldApp& wxGetApp() { return *wx_static_cast(HelloWorldApp*, wxApp::GetInstance()); }
  11.   
  12.    int main(int argc, char **argv) { return wxEntry(argc, argv); }
  13.    extern "C" int WINAPI WinMain(HINSTANCE hInstance,                    
  14.                                   HINSTANCE hPrevInstance,               
  15.                                   wxCmdLineArgType lpCmdLine,             
  16.                                   int nCmdShow)                         
  17.     {                                                                    
  18.         return wxEntry(hInstance, hPrevInstance, lpCmdLine, nCmdShow);    
  19.     }            
    第一步,程序的入口 main(...)或 WinMain(...)我们找到了。

     2-2 程序初始化
     以MSW(Window下为例子)说明。
     WinMain(...)很简单,只是调用了函数
  1. int wxEntry(HINSTANCE hInstance,
  2.                         HINSTANCE WXUNUSED(hPrevInstance),
  3.                         wxCmdLineArgType WXUNUSED(pCmdLine),
  4.                         int nCmdShow),
     wxEntry定义在common/init.cpp中,wxEntry又调用了函数int wxEntry(int& argc, wxChar **argv),进而又调用了函数int wxEntryReal(int& argc, wxChar **argv)。我们来看一下函数
  1. int wxEntryReal(int& argc, wxChar **argv)
  2. {
  3.     // library initialization
  4.     if ( !wxEntryStart(argc, argv) )
  5.     {
  6. #if wxUSE_LOG
  7.         // flush any log messages explaining why we failed
  8.         delete wxLog::SetActiveTarget(NULL);
  9. #endif
  10.         return -1;
  11.     }
  12.     // if wxEntryStart succeeded, we must call wxEntryCleanup even if the code
  13.     // below returns or throws
  14.     wxCleanupOnExit cleanupOnExit;
  15.     WX_SUPPRESS_UNUSED_WARN(cleanupOnExit);
  16.     wxTRY
  17.     {
  18.         // app initialization
  19.         if ( !wxTheApp->CallOnInit() )
  20.         {
  21.             // don't call OnExit() if OnInit() failed
  22.             return -1;
  23.         }
  24.         // ensure that OnExit() is called if OnInit() had succeeded
  25.         class CallOnExit
  26.         {
  27.         public:
  28.             ~CallOnExit() { wxTheApp->OnExit(); }
  29.         } callOnExit;
  30.         WX_SUPPRESS_UNUSED_WARN(callOnExit);
  31.         // app execution
  32.         return wxTheApp->OnRun();
  33.     }
  34.     wxCATCH_ALL( wxTheApp->OnUnhandledException(); return -1; )
  35. }
    整个程序的初始化,推动过程都在此函数中。其中初始化过程调用wxEntryStart,本节主要讨论wxEntryStart函数为我们做了些什么。
  1. bool wxEntryStart(int& argc, wxChar **argv)
  2. {
  3.     // do minimal, always necessary, initialization
  4.     // --------------------------------------------
  5.     // initialize wxRTTI
  6.     if ( !DoCommonPreInit() )
  7.     {
  8.         return false;
  9.     }
  10.     // first of all, we need an application object
  11.     // -------------------------------------------
  12.     // the user might have already created it himself somehow
  13.     wxAppPtr app(wxTheApp);
  14.     if ( !app.get() )
  15.     {
  16.         // if not, he might have used IMPLEMENT_APP() to give us a function to
  17.         // create it
  18.         wxAppInitializerFunction fnCreate = wxApp::GetInitializerFunction();
  19.         if ( fnCreate )
  20.         {
  21.             // he did, try to create the custom wxApp object
  22.             app.Set((*fnCreate)());
  23.         }
  24.     }
  25.     if ( !app.get() )
  26.     {
  27.         // either IMPLEMENT_APP() was not used at all or it failed -- in any
  28.         // case we still need something
  29.         app.Set(new wxDummyConsoleApp);
  30.     }
  31.     // wxApp initialization: this can be customized
  32.     // --------------------------------------------
  33.     if ( !app->Initialize(argc, argv) )
  34.     {
  35.         return false;
  36.     }
  37.     wxCallAppCleanup callAppCleanup(app.get());
  38.     // for compatibility call the old initialization function too
  39.     if ( !app->OnInitGui() )
  40.         return false;
  41.     // common initialization after wxTheApp creation
  42.     // ---------------------------------------------
  43.     if ( !DoCommonPostInit() )
  44.         return false;
  45.     // prevent the smart pointer from destroying its contents
  46.     app.release();
  47.     // and the cleanup object from doing cleanup
  48.     callAppCleanup.Dismiss();
  49. #if wxUSE_LOG
  50.     // now that we have a valid wxApp (wxLogGui would have crashed if we used
  51.     // it before now), we can delete the temporary sink we had created for the
  52.     // initialization messages -- the next time logging function is called, the
  53.     // sink will be recreated but this time wxAppTraits will be used
  54.     delete wxLog::SetActiveTarget(NULL);
  55. #endif // wxUSE_LOG
  56.     return true;
  57. }
     wxEntryStart为我们生成了一个application object--wxTheApp。我们可以从app.h中发现wxTheApp的定义:    
     #define wxTheApp wx_static_cast(wxApp*, wxApp::GetInstance())
     而 wxApp::GetInstance()返回的是定义于wxApp基类wxAppConsole中的static变量:
     static wxAppConsole *GetInstance() { return ms_appInstance; }
     但是,wxApp(本例中为HelloWorldApp)是如何被创建的?ms_appInstance又是何时被赋值的呢?答案在 wxEntryStart函数中:

       wxAppInitializerFunction fnCreate = wxApp::GetInitializerFunction();
       app.Set((*fnCreate)());
      
      通过wxApp::GetInitializerFunction返回的函数指针,创建了一个HelloWorldApp实例。wxApp::GetInitializerFunction()返回的是wxApp的静态变量ms_appInitFn(定义在基类wxAppConsole)。接下来的问题是静态变量ms_appInitFn是如何、是什么时候被赋值的呢?这要回到宏IMPLEMENT_APP(HelloWorldApp)中,在展开的结果中,可以看到一个全局变量:
    wxAppInitializer wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp);    
    wxTheAppInitializer在执行WinMain之前首先生成。而wxAppInitializer的定义很简单,就是在构造函数里为 wxApp::ms_appInitFn赋值:
  1.      class WXDLLIMPEXP_BASE wxAppInitializer
  2.     {
  3.     public:
  4.         wxAppInitializer(wxAppInitializerFunction fn)
  5.         { wxApp::SetInitializerFunction(fn); }
  6.     };  
  7.      
    当wxTheAppInitializer生成后,wxApp::ms_appInitFn赋值为wxCreateApp,而wxCreateApp是IMPLEMENT_APP(HelloWorldApp)展开的一部分,创建HelloWorldApp实例。
    ms_appInstance的赋值很简单,在wxAppConsole的构造函数中完成。
    至此,application object--wxTheApp创建的来龙去脉就清楚了。
    application object--wxTheApp(本例中为HelloWorldApp的实例),EntryStart调用app->Initialize(argc, argv)。Initialize为虚函数,HelloWorldApp并没有改写,因此实际调用wxApp的Initialize。wxApp在msw/app.cpp(当然对于GTK平台应该在gtk/app.cpp中,参加前面的说明)实现:
  1. //msw/app.cpp
  2. //// Initialize
  3. bool wxApp::Initialize(int& argc, wxChar **argv)
  4. {
  5.     if ( !wxAppBase::Initialize(argc, argv) )
  6.         return false;
  7.     // ensure that base cleanup is done if we return too early
  8.     wxCallBaseCleanup callBaseCleanup(this);
  9. #ifdef __WXWINCE__
  10.     wxString tmp = GetAppName();
  11.     tmp += wxT("ClassName");
  12.     wxCanvasClassName = wxStrdup( tmp.c_str() );
  13.     tmp += wxT("NR");
  14.     wxCanvasClassNameNR = wxStrdup( tmp.c_str() );
  15.     HWND hWnd = FindWindow( wxCanvasClassNameNR, NULL );
  16.     if (hWnd)
  17.     {
  18.         SetForegroundWindow( (HWND)(((DWORD)hWnd)|0x01) );
  19.         return false;
  20.     }
  21. #endif
  22. #if !defined(__WXMICROWIN__)
  23.     InitCommonControls();
  24. #endif // !defined(__WXMICROWIN__)
  25. #if defined(__SMARTPHONE__) || defined(__POCKETPC__)
  26.     SHInitExtraControls();
  27. #endif
  28. #ifndef __WXWINCE__
  29.     // Don't show a message box if a function such as SHGetFileInfo
  30.     // fails to find a device.
  31.     SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
  32. #endif
  33.     wxOleInitialize();
  34.     RegisterWindowClasses();
  35.     wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
  36. #if !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
  37.     wxSetKeyboardHook(true);
  38. #endif
  39.     callBaseCleanup.Dismiss();
  40.     return true;
  41. }
    在此我们可以发现窗口注册 RegisterWindowClasses()等其他初始化过程。而RegisterWindowClasses()的定义为:
  1. bool wxApp::RegisterWindowClasses()
  2. {
  3.     WNDCLASS wndclass;
  4.     wxZeroMemory(wndclass);
  5.     // for each class we register one with CS_(V|H)REDRAW style and one
  6.     // without for windows created with wxNO_FULL_REDRAW_ON_REPAINT flag
  7.     static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  8.     static const long styleNoRedraw = CS_DBLCLKS;
  9.     // the fields which are common to all classes
  10.     wndclass.lpfnWndProc   = (WNDPROC)wxWndProc;
  11.     wndclass.hInstance     = wxhInstance;
  12.     wndclass.hCursor       = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
  13.     // register the class for all normal windows
  14.     wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  15.     wndclass.lpszClassName = wxCanvasClassName;
  16.     wndclass.style         = styleNormal;
  17.     if ( !RegisterClass(&wndclass) )
  18.     {
  19.         wxLogLastError(wxT("RegisterClass(frame)"));
  20.     }
  21.     // "no redraw" frame
  22.     wndclass.lpszClassName = wxCanvasClassNameNR;
  23.     wndclass.style         = styleNoRedraw;
  24.     if ( !RegisterClass(&wndclass) )
  25.     {
  26.         wxLogLastError(wxT("RegisterClass(no redraw frame)"));
  27.     }
  28.     // Register the MDI frame window class.
  29.     wndclass.hbrBackground = (HBRUSH)NULL; // paint MDI frame ourselves
  30.     wndclass.lpszClassName = wxMDIFrameClassName;
  31.     wndclass.style         = styleNormal;
  32.     if ( !RegisterClass(&wndclass) )
  33.     {
  34.         wxLogLastError(wxT("RegisterClass(MDI parent)"));
  35.     }
  36.     // "no redraw" MDI frame
  37.     wndclass.lpszClassName = wxMDIFrameClassNameNoRedraw;
  38.     wndclass.style         = styleNoRedraw;
  39.     if ( !RegisterClass(&wndclass) )
  40.     {
  41.         wxLogLastError(wxT("RegisterClass(no redraw MDI parent frame)"));
  42.     }
  43.     // Register the MDI child frame window class.
  44.     wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  45.     wndclass.lpszClassName = wxMDIChildFrameClassName;
  46.     wndclass.style         = styleNormal;
  47.     if ( !RegisterClass(&wndclass) )
  48.     {
  49.         wxLogLastError(wxT("RegisterClass(MDI child)"));
  50.     }
  51.     // "no redraw" MDI child frame
  52.     wndclass.lpszClassName = wxMDIChildFrameClassNameNoRedraw;
  53.     wndclass.style         = styleNoRedraw;
  54.     if ( !RegisterClass(&wndclass) )
  55.     {
  56.         wxLogLastError(wxT("RegisterClass(no redraw MDI child)"));
  57.     }
  58.     return true;
  59. }
    呵呵,Win32SDK的东西终于露面了。程序初始化过程也显山露水了。主窗口的创建呢?下一小节接着分析。

    2-3 窗口创建过程
    前面说过,主窗口创建由wxFrame负责,wxFrame(本例中为HelloWorldFrame)如何实现的呢?还是回到wxEntryReal函数中。调用wxEntryStart结束后,程序接着调用了:
    wxTheApp->CallOnInit()
    由上面的分析,我们已经知道wxTheApp是一个application object,相当于一个HelloWorldApp的全局变量。CallOnInit()调用了OnInit。OnInit是一个虚函数,在HelloWorldApp中重写了:
  1.     bool HelloWorldApp::OnInit()
  2.     {
  3.         HelloWorldFrame* frame = new HelloWorldFrame(0L, _("Hello World!"));
  4.         frame->Show();
  5.         return true;
  6.     }
    绕了一圈,终于到了主窗口的创建过程了。
    函数中创建了一个HelloWorldFrame实例,并调用 Show()显示。不用怀疑,wxWidgets肯定做了大量幕后工作。
    HelloWorldFrame的构造函数调用了基类的构造函数:
    HelloWorldFrame::HelloWorldFrame(wxFrame *frame, const wxString& title)
    : wxFrame(frame, -1, title)
   {}
    先看一下wxFrame的类层次关系,如下图: wxWidgets学习笔记(3)wxWidgets程序生死因果_第4张图片

       真正的  wxFrame(frame, -1, title)定义在wxTopLevelWindowMSW中(图中显示的是GTK下,到了Window平台下为wxTopLevelWindowMSW,至于如何实现的已经不是新鲜事,参见前面的说明):
  1.     wxTopLevelWindowMSW(wxWindow *parent,
  2.                         wxWindowID id,
  3.                         const wxString& title,
  4.                         const wxPoint& pos = wxDefaultPosition,
  5.                         const wxSize& size = wxDefaultSize,
  6.                         long style = wxDEFAULT_FRAME_STYLE,
  7.                         const wxString& name = wxFrameNameStr)
  8.     {
  9.         Init();
  10.         (void)Create(parent, id, title, pos, size, style, name);
  11.     }
    Create(parent, id, title, pos, size, style, name)定义于msw/toplevel.cpp中(当然,对于GTK来说定义于jdk/toplevel.cpp中)。在这里我们还可以发现窗口过程的实现。
    Create最终又调用wxWindowMSW中的MSWCreate
  1. bool wxWindowMSW::MSWCreate(const wxChar *wclass,
  2.                             const wxChar *title,
  3.                             const wxPoint& pos,
  4.                             const wxSize& size,
  5.                             WXDWORD style,
  6.                             WXDWORD extendedStyle)
  7. {
  8.     // choose the position/size for the new window
  9.     int x, y, w, h;
  10.     (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h);
  11.     // controlId is menu handle for the top level windows, so set it to 0
  12.     // unless we're creating a child window
  13.     int controlId = style & WS_CHILD ? GetId() : 0;
  14.     // for each class "Foo" we have we also have "FooNR" ("no repaint") class
  15.     // which is the same but without CS_[HV]REDRAW class styles so using it
  16.     // ensures that the window is not fully repainted on each resize
  17.     wxString className(wclass);
  18.     if ( !HasFlag(wxFULL_REPAINT_ON_RESIZE) )
  19.     {
  20.         className += wxT("NR");
  21.     }
  22.     // do create the window
  23.     wxWindowCreationHook hook(this);
  24.     m_hWnd = (WXHWND)::CreateWindowEx
  25.                        (
  26.                         extendedStyle,
  27.                         className,
  28.                         title ? title : m_windowName.c_str(),
  29.                         style,
  30.                         x, y, w, h,
  31.                         (HWND)MSWGetParent(),
  32.                         (HMENU)controlId,
  33.                         wxGetInstance(),
  34.                         NULL                        // no extra data
  35.                        );
  36.     if ( !m_hWnd )
  37.     {
  38.         wxLogSysError(_("Can't create window of class %s"), className.c_str());
  39.         return false;
  40.     }
  41.     SubclassWin(m_hWnd);
  42.     return true;
  43. }
    至此主窗口真正生成。主窗口生成后,在HelloWorldFrame构造函数中为主窗口增加菜单栏、状态栏等。

    2-4 消息循环,程序推动
    主窗口生成后,程序如何推动的呢?我们主要两个地方:
    1) 回到wxEntryReal函数,可以看到:
     // app execution
        return wxTheApp->OnRun();
    程序进入循环等待。
    2)在HelloWorldFrame中,我们可以看到消息映射表
    BEGIN_EVENT_TABLE(HelloWorldFrame, wxFrame)
        EVT_CLOSE(HelloWorldFrame::OnClose)
        EVT_MENU(idMenuQuit, HelloWorldFrame::OnQuit)
        EVT_MENU(idMenuAbout, HelloWorldFrame::OnAbout)
    END_EVENT_TABLE()
    正是这两项推动程序运行。
    具体细节将在以后分析。

    3 结论:wxWidgets程序执行过程
     通过以上分析,可以看出wxWidgets程序一般执行过程为:
     1)根据平台不同,函数main,WinMain或类似的函数运行(这个函数是由wxWidgets提供的,并不是应用程序)。wxWidgets初始化内部数据结构,创建一个wxApp实例(本例中为HelloWorldApp)。
    2)wxWidgets调用HelloWorldApp::OnInit,创建一个wxFrame实例(本例中为HelloWorldFrame)。
    3)HelloWorldFrame构造函数通过wxFrame的构造函数创建主窗口,并且增加菜单栏、状态条等。
    4)HelloWorldApp::OnInit显示frame.
    5)wxWidgets开始消息循环,等待事件发生,并把事件分配到相应的处理函数。
    当主窗口销毁时,程序也随之结束。

你可能感兴趣的:(wxWidgets)