基于hotfox的客户端的结构

HotFox是一个程序框架,通过插件提供应用入口、登录实现及业务主窗口。文章介绍了HotFox主程序逻辑及其与Frame1、logina、AppEntryStyle1等插件的组合使用,并以GDSN_Supplier插件为例展示了具体的客户端应用构造。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简述

hotfox是程序框架,由插件提供具体的应用入口,登录实现和业务主窗口.

具体的应用业务逻辑由相应的插件实现。

除hotfox外,下文所有模块的代码在SVN上的nxclient目录下.以后除供应宝(合适的时候再迁移)以外的新的客户端程序全部采用本文所说的方法.


Frame1是一个常见的应用模式的实现插件.
logina是一个风格简单的登录模块.
AppEntryStyle1是一个动态构造菜单系统的模块,可以作为应用的主界面插件提供者.
GDSN_Supplier是一个具体应用的插件,该插件提供主窗口.

以下用示例描述这些组件如何组合构造不同的应用(未包含实际应用需要的其它模块)
GGOM应用的构造:
.hotfox
.Frame1
.logina
.GDSN_Supplier:GGOM客户端插件

单据监控应用构造:
.hotfox
.Frame1
.logina
.AppEntryStyle1
.dxm_c: 单据监控客户端插件


由于AppEntryStyle1是自绘风格的,通过宏USE_OWNERDRAW的控制保证应用模块风格的一致(如复用本地管理的模块).
如对TFrmOwnerDrawCommon的定义如下:

#ifdef USE_OWNERDRAW
#include "TFrmOwnerDrawBase.h"
class CSDK_FORMPUBLIC_API TFrmOwnerDrawCommon:public TFrmOwnerDrawBase{
public:
    __fastcall TFrmOwnerDrawCommon(TComponent* Owner, bool load_res = true);
    void __fastcall LoadFormRes();
protected:
    void __fastcall WndProc(TMessage& Message);

};
#else
#include "BaseForm1.h"
typedef TfrmBase1 TFrmOwnerDrawCommon;
#endif

2.实现逻辑

2.1 hotfox


hotfox主程序代码如下:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    try
    {
         Application->Initialize();

         g_cfg_info_.Load();
         Application->HelpFile  = ExtractFilePath(Application->ExeName) + g_cfg_info_.help_file_;
         
        ///< 设置为非NT服务方式
         HOTFOX::instance()->set_service_mode(false);
         HTX_LOGGER::instance()->sync_flag(true);
         HTX_LOGGER::instance()->hook_verbose(SEVERITY_DEBUG);
         HTX_LOGGER::instance()->set_callback(LoggerCallback,g_cfg_info_.show_startup_ ? (void*)1:(void*)2);
         if (g_cfg_info_.show_startup_) {
             frmStartup = new TfrmStartup(NULL);
             frmStartup->Show();
         }
         else {
            wait_form = TfrmWaitForm::NewWaitForm(0,"启动程序");
         }
         ///< 启动hotfox
         if (HOTFOX::instance()->svc()) {
            if (g_cfg_info_.show_startup_)
                delete frmStartup;
            if (last_error.IsEmpty()) {
                last_error = "程序启动失败,未描述的错误." ;
            }
            ShowMessage(last_error);
            return -1;
         }

         Application->Title = HOTFOX::instance()->server_info_.app_name_.c_str();
         if (g_cfg_info_.show_startup_) {
             ::PostMessage(frmStartup->Handle,WM_CLOSE,0,0);
             Application->ProcessMessages();
         }
         else {
            delete wait_form;
         }
         HTX_LOGGER::instance()->sync_flag(HOTFOX::instance()->server_info_.sync_log_);
         HTX_LOGGER::instance()->reset_callback(); ///< @note 本程序不支持插件日志挂钩

         AppEntryFunc app_entry = HOTFOX::instance()->GetAppEntry();
         short ok_flag = 1;
         CleanupFunc fp = 0;
         if (app_entry) {
            HTX_LOGGER::instance()->log(LO_STDOUT|LO_FILE,SEVERITY_INFO,"调用应用入口...\n");
            if ((*app_entry)(0,&fp)) {
                ///< 应用入口执行失败或者不需要继续执行(如需要升级), 则程序不进程循环,直接结束退出
                ok_flag = 0;
            }
            HTX_LOGGER::instance()->log(LO_STDOUT|LO_FILE,SEVERITY_INFO,"调用应用入口%s.\n",ok_flag ? "成功":"失败");
         }

         if (ok_flag)
            Application->Run();
         if (fp)
            (*fp)();
         HOTFOX::instance()->stop();
         HOTFOX::instance()->wait();
    }
    catch (Exception &exception)
    {
         Application->ShowException(&exception);
    }
    catch (...)
    {
         try
         {
             throw Exception("");
         }
         catch (Exception &exception)
         {
             Application->ShowException(&exception);
         }
    }


    return 0;
}  


2.2 Frame1

Frame1是一种应用入口,是应用客户端的常见形式:
---用户执行登录操作
---升级检测和启动升级
---登录成功后打开应用主窗口

EZEntry函数是Frame1向hotfox登记的入口函数.
在插件Initialize时登记:
    mgr_->SetAppEntry(EZEntry);

Frame1调用运行时初始化,提供释放操作由hotfox在结束前调用.

int EZEntry(void *arg,CleanupFunc *fp) {
    ///< 在hotfox中设置Application->HelpFile为什么不起作用? ()
//    AnsiString FileName = ExtractFilePath(Application->ExeName) + "ggom.chm";
//    Application->HelpFile = FileName;

    ///< 设置应用窗口Icon.
    if (!CEZMainPlugin::instance()->app_icon_file_.empty()) {
        HICON old_icon = NULL;
        TIcon *Icon = new TIcon();
        Icon->LoadFromFile(CEZMainPlugin::instance()->app_icon_file_.c_str());
        old_icon = CPluginHelper::SetIcon(2,Icon->Handle); ///< id=2是窗口基类用来指定窗口Icon的id
        if (old_icon)
            FreeResource(old_icon);
        Icon->ReleaseHandle();

        delete Icon;

        ///< 如果不执行下行代码,则frmLogin的窗体的ICON为默认的.(为什么会这样?)
        Application->Icon->LoadFromFile(CEZMainPlugin::instance()->app_icon_file_.c_str());
    }
    CPluginHelper::Init();
    CBasePlugInModule::sc_->set_callback(SyncCallCallback);

    LoginFunc login_fp = CEZMainPlugin::instance()->mgr_->GetLoginFunc();
    if (login_fp) {
        int ret = (*login_fp)();
        if (ret==1) { ///< 检查是否需要升级
            CBasePlugInModule::nlogger_->log(LO_FILE|LO_STDOUT,SEVERITY_DEBUG,"开始启动客户端升级程序\r\n");
            LJShellExecute("CltLiveUpdate.exe", "1", "open", ExtractFilePath(Application->ExeName).c_str());
            CBasePlugInModule::nlogger_->log(LO_FILE|LO_STDOUT,SEVERITY_DEBUG,"启动客户端升级程序结束\r\n");
            CPluginHelper::Cleanup();
            return 1; ///< 需要升级
        }
        if (ret==-1) {
            CPluginHelper::Cleanup(); ///< @note 如果不执行,则会导致程序退出时异常. CBaseClientModule::g_async_fc_form
            return -1;
        }
    }

    INative_Logger_Base *log = CEZMainPlugin::instance()->nlogger_;
    bool sync_flag = log->sync_flag();
    log->sync_flag(true);
    log->hook_verbose(SEVERITY_DEBUG);
    log->set_callback(LoggerCallback,CEZMainPlugin::instance()->show_startup_ ? (void*)1:(void*)2);
    if (CEZMainPlugin::instance()->show_startup_) {
        frmStartup = new TfrmStartup(NULL);
        frmStartup->Show();
    }

    CPluginHelper::ShareResource(CachedData::instance(), CACHEDDATA_INFO, NULL);
    ///<同步调用获取过时的数据
    int ret = CachedData::instance()->GetExpiredDataVersion();
    if( ret ){
        AnsiString err_str = AnsiString("获取数据版本失败,错误码")+ret;
        MessageShow(NULL,err_str.c_str(),"系统提示",MSGERROR);
        if (CEZMainPlugin::instance()->show_startup_)
            ::PostMessage(frmStartup->Handle,WM_CLOSE,0,0);
        CPluginHelper::Cleanup();
        return -1;
    }


    ///< 登录成功,执行各插件的登录后操作(如从服务器下载数据)
    if (CBasePluginModule::plugin_mgr_->OnLogin()) {
        MessageShow(NULL,"登录处理失败","系统提示",MSGERROR);
        if (CEZMainPlugin::instance()->show_startup_)
            ::PostMessage(frmStartup->Handle,WM_CLOSE,0,0);
        CPluginHelper::Cleanup();
        return -1;
    }
    if (CEZMainPlugin::instance()->show_startup_) {
        ::PostMessage(frmStartup->Handle,WM_CLOSE,0,0);
        Application->ProcessMessages();
    }

    log->reset_callback();
    log->sync_flag(sync_flag); ///< 恢复日志的同步标志

    CEZMainPlugin::instance()->NotifyUserReady(); ///< 通知服务器客户端已就绪(可以接收服务器主动推送的消息)

    AppMainWindowFunc main_wnd_entry = CEZMainPlugin::instance()->mgr_->GetAppMainWindowEntry();
    if (main_wnd_entry==0) { ///< 未指定启动的主窗口
        CPluginHelper::Cleanup();
        return -1;
    }

    (*main_wnd_entry)(0);

    *fp = &Cleanup;

    return 0;
}

2.3 logina


logina插件实现了一种常用的登录模式.
风格简单.支持本地服务器和服务器查找,支持网络设置.

///< 登录函数
int Login() {
    TfrmLogin *frmLogin = new TfrmLogin(NULL);
    int result = frmLogin->ShowModal();
    ///< 登录过程包含一个版本比较操作,决定是否需要升级
    if (frmLogin->GetUpdateFlag()) { ///< 检查是否需要升级
        delete frmLogin;
        return 1; ///< 需要升级
    }
    if (result!=mrOk) {
        delete frmLogin;
        return -1;
    }

    CLoginA::instance()->first_logon_ = false; ///< 已经登录过,以后连接断开只需要重新登录(597协议)
    CLoginA::instance()->connector_ = frmLogin->GetConnector();

    delete frmLogin;

    return 0;
}

登录模块的登记是在Prepare中完成的.
int CLoginA::Prepare() {
    parent::Prepare();

  mgr_->SetLoginFunc(Login); ///< 由本插件提供登录入口

    return 0;
}

2.4 AppEntryStyle1

AppEntryStyle1插件从供应宝业务主窗口改造而来,具有以下特性:
.从服务器获取菜单信息,动态构造菜单
.具有权限控制
.响应菜单操作启动相应功能

int Main_Wnd(void *arg) {
    ///< 启动业务窗口
    TBusinessMainForm  *frmBiz;
    Application->CreateForm(__classid(TBusinessMainForm), &frmBiz);

    return 0;
}

int CAppEntryStyle1::Initialize() {
    parent::Initialize();
    mgr_->SetAppMainWindowEntry(Main_Wnd);

    return 0;
}

2.5 一个应用:GDSN_Supplier

GDSN_Supplier插件是实现GGOM功能的客户端插件.
在GGOM应用中作为主窗口的提供者.

///< 应用主窗口函数
int Main_Wnd(void *arg) {
    AnsiString FileName = ExtractFilePath(Application->ExeName) + "ggom.chm";
    Application->HelpFile = FileName;

    if (UserIsAdmin()) { ///< 管理员模式
        TfrmSystemManage *frmMain;
        Application->CreateForm(__classid(TfrmSystemManage), &frmMain);
    }
    else { ///< 非管理员模式
        TFrmMainGDSNSup  *frmMain;
        Application->CreateForm(__classid(TFrmMainGDSNSup), &frmMain);
    }
    return 0;
}

主窗口函数在Initialize时登记.
int CAppEntryStyle1::Initialize() {
    parent::Initialize();
    mgr_->SetAppMainWindowEntry(Main_Wnd);

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值