
本文共 7592 字,大约阅读时间需要 25 分钟。
APP启动过程
本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手。
wxApp入口定义
wxApp通过IMPLEMENT_APP
宏注册App类,这个宏同时定义了入口,实现在wx/app.h
文件中。
// wx/app.h 文件中定义#define IMPLEMENT_APP(app) wxIMPLEMENT_APP(app);// 可以忽略 wxIMPLEMENT_WX_THEME_SUPPORT,// 重点关注wxIMPLEMENT_APP_NO_THEMES#define wxIMPLEMENT_APP(appname) \ wxIMPLEMENT_WX_THEME_SUPPORT \ wxIMPLEMENT_APP_NO_THEMES(appname)// 继续展开宏#define wxIMPLEMENT_APP_NO_THEMES(appname) \ wxIMPLEMENT_WXWIN_MAIN \ wxIMPLEMENT_APP_NO_MAIN(appname)
下面的 wxIMPLEMENT_WXWIN_MAIN 是重点之一,它是我们整个应用程序的入口,实现了WinMain
函数,我们继续分析,wx/app.h
文件包含了wx/init.h
文件,追踪进去我们就可以找到WinMain
函数的实现:
// 在windows版本中,wx/init.h包含了wx/msw/init.h文件,用于做具体的实现#if wxUSE_GUI && defined(__WINDOWS__) #include "wx/msw/init.h"#endif// wx/init.h 文件中并没有具体 wxIMPLEMENT_WXWIN_MAIN 的定义,所以要继续看 wx/msw/init.h// wx/msw/init.h 中实现如下:// 由于wxIMPLEMENT_WXWIN_MAIN是宏定义所以在宏展开的时候就相当于定义了这个WinMain函数,// 可以看到最后是调用wxEntry(hInstance, hPrevInstance, NULL, nCmdShow)继续进行初始化#define wxIMPLEMENT_WXWIN_MAIN \ extern "C" int WINAPI WinMain(HINSTANCE hInstance, \ HINSTANCE hPrevInstance, \ wxCmdLineArgType WXUNUSED(lpCmdLine), \ int nCmdShow) \ { \ wxDISABLE_DEBUG_SUPPORT(); \ \ /* NB: We pass NULL in place of lpCmdLine to behave the same as */ \ /* Borland-specific wWinMain() above. If it becomes needed */ \ /* to pass lpCmdLine to wxEntry() here, you'll have to fix */ \ /* wWinMain() above too. */ \ return wxEntry(hInstance, hPrevInstance, NULL, nCmdShow); \ } \ wxIMPLEMENT_WXWIN_MAIN_BORLAND_NONSTANDARD
wxApp实例化准备
接着上文我们看看 wxIMPLEMENT_APP_NO_MAIN 宏的实现:
- 实现了 wxGetApp() 函数,返回当前App类型的引用;
- 实现了 wxCreateApp() 函数,这个会在 wxEntry 中调用,用于创建wxApp对象;
- 实例化 wxAppInitializer 对象 wxTheAppInitializer,入参就是 wxCreateApp;
- wxCreateApp做一些必要的检查,然后new出一个App对象返回。
#define wxIMPLEMENT_APP_NO_MAIN(appname) \ appname& wxGetApp() { return *static_cast(wxApp::GetInstance()); } \ wxAppConsole *wxCreateApp() \ { \ wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, \ "your program"); \ return new appname; \ } \ wxAppInitializer \ wxTheAppInitializer((wxAppInitializerFunction) wxCreateApp)
wxAppInitializer对象实例化的时机是在main函数执行之前,所以我们先要看看这个对象的创建过程,此类型定义在 wx/app.h
文件中,此函数通过调用静态函数wxApp::SetInitializerFunction
参数就是递进来的函数指针wxCreateApp
:
class WXDLLIMPEXP_BASE wxAppInitializer{public: wxAppInitializer(wxAppInitializerFunction fn) { wxApp::SetInitializerFunction(fn); }};
继续看wxApp::SetInitializerFunction
,我们先找下wxApp
的定义,可以看到不同的体系结构定义的wxApp不同,位于wx/ARCH/app.h
文件中,ARCH就是体系结构,对于windows系统来说就是msw,我们可以看下wx/msw/app.h
中的定义,梳理wxApp的继承关系:
wxAppw -> wxAppBase -> wxAppConsole -> xAppConsoleBase
实际上 SetInitializerFunction 函数是在 wxAppConsoleBase 类中实现的,最终将这个函数指针保存到了静态变量 ms_appInitFn
中,后续在App创建时会调用GetInitializerFunction
函数执行真正的创建:
// wx/app.h 文件中有定义 class wxAppConsoleBase { static void SetInitializerFunction(wxAppInitializerFunction fn) { ms_appInitFn = fn; } static wxAppInitializerFunction GetInitializerFunction() { return ms_appInitFn; } static wxAppInitializerFunction ms_appInitFn;}
wxApp的实例化
启动前的准备工作都已经妥当,我们看看启动主体 wxEntry
怎么运行的,windows版本中实现在wx/msw/mian.cpp
文件中:
// wx/msw/mian.cpp实现如下,不重要的部分先忽略// 1. 调用 wxMSWEntryCommon 进行通用参数处理,重点是处理传递进来的入参,保存到wxArgs中// 2. 随后调用 wxEntry 执行处理,传递刚才解析好的参数信息WXDLLEXPORT int wxEntry(HINSTANCE hInstance, HINSTANCE WXUNUSED(hPrevInstance), wxCmdLineArgType WXUNUSED(pCmdLine), int nCmdShow){ ... if ( !wxMSWEntryCommon(hInstance, nCmdShow) ) return -1; wxON_BLOCK_EXIT_OBJ0(wxArgs, wxMSWCommandLineArguments::Free); return wxEntry(wxArgs.argc, wxArgs.argv);}
接着看看参数的解析函数wxMSWEntryCommon
的实现:
// wx/msw/mian.cpp// 定义全局的参数变量,然后调用Win32API函数 ::GetCommandLine 获取命令行// 进而解析该命令行,存放到wxArgs变量中static wxMSWCommandLineArguments wxArgs;static boolwxMSWEntryCommon(HINSTANCE hInstance, int nCmdShow){ wxArrayString args; const wxChar *cmdLine = ::GetCommandLine(); if ( cmdLine ) { args = wxCmdLineParser::ConvertStringToArgs(cmdLine); } wxArgs.Init(args); return true;}
接着再回过头,看看wxEntry
的执行过程,此函数同样实现在wx/msw/mian.cpp
文件中,实现过程主要如下:
- 创建 wxInitializer 对象,主要目的是实例化App对象;
- 调用 wxTheApp->CallOnInit() 函数执行 App 的初始化;
- 调用 wxTheApp->OnRun() 函数,进入App的消息循环;
int wxEntry(int& argc, wxChar **argv){ return wxEntryReal(argc, argv);}int wxEntryReal(int& argc, wxChar **argv){ // library initialization wxInitializer initializer(argc, argv); if ( !initializer.IsOk() ) { return -1; } wxTRY { // app initialization if ( !wxTheApp->CallOnInit() ) { // don't call OnExit() if OnInit() failed return -1; } // ensure that OnExit() is called if OnInit() had succeeded class CallOnExit { public: ~CallOnExit() { wxTheApp->OnExit(); } } callOnExit; WX_SUPPRESS_UNUSED_WARN(callOnExit); // app execution return wxTheApp->OnRun(); } wxCATCH_ALL( wxTheApp->OnUnhandledException(); return -1; )}
我们先看下 wxInitializer
对象的实现,注意此时还没有App对象,这里主要用于创建当前的App对象,我们看下详细过程:
// 位于 wx/init.h wx/init.cpp 文件中,这里只保留重点// 构造函数执行wxInitialize函数执行初始化class WXDLLIMPEXP_BASE wxInitializer{ wxInitializer(int argc, wxChar **argv) { m_ok = wxInitialize(argc, argv); } bool IsOk() const { return m_ok; }}// wxInitialize 继续调用 wxEntryStartbool wxInitialize(int argc, wxChar **argv){ wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit); return wxEntryStart(argc, argv);}
wxEntryStart
函数的执行过程:
- 调用
wxApp::GetInitializerFunction()
获取到函数指针wxCreateApp
,这个函数是通过IMPLEMENT_APP
宏实现的; - 调用完成后调用该App的
Initialize
方法执行初始化,同时还将入参传递给App类,方便用户程序使用; - 执行必要的清理;
// wx/init.h wx/init.cpp,下面的代码中只保留的关键代码,如果需要全版本代码请参考源代码// 这里执行App对象的创建bool wxEntryStart(int& argc, wxChar **argv){ wxAppPtr app(wxTheApp); if ( !app.get() ) { wxAppInitializerFunction fnCreate = wxApp::GetInitializerFunction(); if ( fnCreate ) { // 实例化App对象出来 app.Set((*fnCreate)()); } } if ( !app->Initialize(argc, argv) ) return false; app->argc = argc; app->argv = argv; return true;}
wxApp运行
接着我们看下wxApp::CallOnInit
函数的实现,这个函数调用了App类的虚方法OnInit
,控制权交给了用户程序了。
virtual bool CallOnInit() { return OnInit(); }
最后我们看下wxApp::OnRun
函数的作用,调用关系参考下面的代码:
- 实例化一个
wxEventLoop
对象,通过 CreateMainLoop 创建; - 调用wxApp的OnLaunched方法,这个方法也是个虚函数,用户按需实现就可以了
- 进入到消息循环;
int wxAppBase::OnRun() { return wxAppConsole::OnRun(); }int wxAppConsoleBase::OnRun(){ return MainLoop();}
总结
通过上面的过程,我们分析了从wxApp定义到wxApp的创建和运行的整个过程,wxWidgets考虑了多种架构的兼容性,所以很多代码都是通过不同的编译宏隔开的,所以跟踪代码时务必要找对点。
另外一点,wxWidgets库中大量使用了宏定义,导致查找代码时会比较麻烦,这个也是难点之一。
发表评论
最新留言
关于作者
