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 宏的实现:

  1. 实现了 wxGetApp() 函数,返回当前App类型的引用;
  2. 实现了 wxCreateApp() 函数,这个会在 wxEntry 中调用,用于创建wxApp对象;
  3. 实例化 wxAppInitializer 对象 wxTheAppInitializer,入参就是 wxCreateApp;
  4. wxCreateApp做一些必要的检查,然后new出一个App对象返回。
#define wxIMPLEMENT_APP_NO_MAIN(appname)                                    \
appname& wxGetApp() { return *static_cast<appname*>(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 bool
wxMSWEntryCommon(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文件中,实现过程主要如下:

  1. 创建 wxInitializer 对象,主要目的是实例化App对象;
  2. 调用 wxTheApp->CallOnInit() 函数执行 App 的初始化;
  3. 调用 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 继续调用 wxEntryStart
bool wxInitialize(int argc, wxChar **argv)
{
wxCRIT_SECT_LOCKER(lockInit, gs_initData.csInit);
return wxEntryStart(argc, argv);
}

wxEntryStart函数的执行过程:

  1. 调用 wxApp::GetInitializerFunction() 获取到函数指针wxCreateApp,这个函数是通过 IMPLEMENT_APP 宏实现的;
  2. 调用完成后调用该App的Initialize方法执行初始化,同时还将入参传递给App类,方便用户程序使用;
  3. 执行必要的清理;
// 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函数的作用,调用关系参考下面的代码:

  1. 实例化一个 wxEventLoop 对象,通过 CreateMainLoop 创建;
  2. 调用wxApp的OnLaunched方法,这个方法也是个虚函数,用户按需实现就可以了
  3. 进入到消息循环;
int wxAppBase::OnRun() { return wxAppConsole::OnRun(); }

int wxAppConsoleBase::OnRun()
{
return MainLoop();
}

总结

通过上面的过程,我们分析了从wxApp定义到wxApp的创建和运行的整个过程,wxWidgets考虑了多种架构的兼容性,所以很多代码都是通过不同的编译宏隔开的,所以跟踪代码时务必要找对点。

另外一点,wxWidgets库中大量使用了宏定义,导致查找代码时会比较麻烦,这个也是难点之一。

wxWidgets源码分析(1) - App启动过程的更多相关文章

  1. tomcat8 源码分析 | 组件及启动过程

    tomcat 8 源码分析 ,本文主要讲解tomcat拥有哪些组件,容器,又是如何启动的 推荐访问我的个人网站,排版更好看呦: https://chenmingyu.top/tomcat-source ...

  2. Netty源码分析之客户端启动过程

    一.先来看一下客户端示例代码. public class NettyClientTest { public void connect(int port, String host) throws Exc ...

  3. wxWidgets源码分析(6) - 窗口关闭过程

    目录 窗口关闭过程 调用流程 关闭文档 删除视图 删除文档对象 关闭Frame App清理 多文档窗口的关闭 多文档父窗口关闭 多文档子窗口关闭 窗口的正式删除 窗口关闭过程总结 如何手工删除view ...

  4. Hive源码分析(1)——HiveServer2启动过程

    1.想了解HiveServer2的启动过程,则需要找到启动HiveServer2的入口,hive服务的启动命令为hive --service HiveServer2,通过分析$HIVE_HOME/bi ...

  5. wxWidgets源码分析(2) - App主循环

    目录 APP主循环 MainLoop 消息循环对象的创建 消息循环 消息派发 总结 APP主循环 MainLoop 前面的wxApp的启动代码可以看到,执行完成wxApp::OnInit()函数后,接 ...

  6. JVM源码分析之JVM启动流程

      原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十四篇. 今天呢!灯塔君跟大家讲: JVM源码分析之JVM启动流程 前言: 执行Java类的main方法,程序就能运 ...

  7. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  8. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  9. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

随机推荐

  1. STM32通过rosserial接入ROS通讯开发

    作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:羽林君 前言 主题:串口是一种设备间常用的通讯接口,rosserial将串口字符数据转发到标准ROS网络,并输出到rosout和其日志文件.本文将 ...

  2. 2019CCPC厦门站总结

    这是一篇打铁游记~ $day1$ 坐动车去厦门,三个人买了一堆零食,吃了一路,除了睡觉嘴巴基本就没停过.当然,我们到酒店后也去吃了烧烤,我们虽然是在岛外的厦门北站的下的,还是很幸运的找到一家好吃了,乌 ...

  3. Codeforces Global Round 7 B. Maximums(逻辑)

    题意: 有数组 an,定义 xi 为 a 的前 i - 1 位的最大值(x0 = 0),定义 bi 为 ai - xi,给出数组 bn,还原数组 an. 思路: 因为 x0 = 0,所以 b0 = a ...

  4. AcWing 241.楼兰图腾 (树状数组,逆序对)

    题意:在二维坐标轴上给你一些点,求出所有由三个点构成的v和∧图案的个数. 题解:因为给出的点是按横坐标的顺序给出的,所以我们可以先遍历然后求出某个点左边比它高和低的点的个数(这个过程简直和用树状数组求 ...

  5. Codeforces Round #669 (Div. 2) A. Ahahahahahahahaha (构造)

    题意:有一个长度为偶数只含\(0\)和\(1\)的序列,你可以移除最多\(\frac{n}{2}\)个位置的元素,使得操作后奇数位置的元素和等于偶数位置的元素和,求新序列. 题解:统计\(0\)和\( ...

  6. Matching Engine For Laravel(基于redis的撮合引擎),PHP高性能撮合引擎

    Laravel Package for Matching Engine 快速开始 github地址 安装: composer require sting_bo/mengine 复制配置文件: php ...

  7. codeforces 1076E Vasya and a Tree 【dfs+树状数组】

    题目:戳这里 题意:给定有n个点的一棵树,顶点1为根.m次操作,每次都把以v为根,深度dep以内的子树中所有的顶点(包括v本身)加x.求出最后每个点的值为多少. 解题思路:考虑到每次都只对点及其子树操 ...

  8. C++ 变量声明数组

    int len; cin>>len; int *p=new int[len]; delete[] p; 不能写作 int p[]=new int[len]; 因为new是开辟了内存空间后返 ...

  9. 2018大都会赛 A Fruit Ninja【随机数】

    题目链接:戳这里 题意:一个平面里有n个点,问存不存在一条直线上有m个点,满足m >= n*x. 解题思路:0<x<1,且x小数点后只有1位,也就是说10*m > n.假设存在 ...

  10. SSL/TLS All In One

    SSL/TLS All In One HTTPS SSL/TLS 的工作原理 https://www.websecurity.digicert.com/zh/cn/security-topics/ho ...