wxWidgets源码分析(1) - App启动过程
阅读原文时间:2023年07月09日阅读:5

目录

本文主要介绍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) <br /> appname& wxGetApp() { return static_cast>(wxApp::GetInstance()); } <br /> wxAppConsole *wxCreateApp() <br /> { <br /> wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, <br /> "your program"); <br /> return new appname; <br /> } <br /> wxAppInitializer <br /> 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-&gt;OnExit(); }
    } callOnExit;
    
    WX_SUPPRESS_UNUSED_WARN(callOnExit);
    
    // app execution
    return wxTheApp-&gt;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库中大量使用了宏定义,导致查找代码时会比较麻烦,这个也是难点之一。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章