基于hotfox的客户端的结构

来源:互联网 发布:淘宝教程 百度网盘 编辑:程序博客网 时间:2024/05/19 20:39

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;}


原创粉丝点击