第十一篇:SOUI系统资源管理
SOUI资源管理模块
从前篇已经讲到在SOUI中所有资源文件通过一个uires.idx文件进行索引。
这里将介绍在程序中如何引用这些资源文件。
在SOUI系统中,资源文件通过一个统一的接口对象读取:
namespace SOUI
{
enum BUILTIN_RESTYPE
{
RES_PE=,
RES_FILE,
}; /**
* @struct IResProvider
* @brief ResProvider对象
*
* Describe 实现各种资源的加载
*/
struct IResProvider : public IObjRef
{
/**
* Init
* @brief 资源初始化函数
* @param WPARAM wParam -- param 1
* @param LPARAM lParam -- param 2
* @return BOOL -- true:succeed
*
* Describe every Resprovider must implement this interface.
*/
virtual BOOL Init(WPARAM wParam,LPARAM lParam) =; /**
* HasResource
* @brief 查询一个资源是否存在
* @param LPCTSTR strType -- 资源类型
* @param LPCTSTR pszResName -- 资源名称
* @return BOOL -- true存在,false不存在
* Describe
*/
virtual BOOL HasResource(LPCTSTR strType,LPCTSTR pszResName)=; /**
* LoadIcon
* @brief 从资源中加载ICON
* @param LPCTSTR pszResName -- ICON名称
* @param int cx -- ICON宽度
* @param int cy -- ICON高度
* @return HICON -- 成功返回ICON的句柄,失败返回0
* Describe
*/
virtual HICON LoadIcon(LPCTSTR pszResName,int cx=,int cy=)=; /**
* LoadBitmap
* @brief 从资源中加载HBITMAP
* @param LPCTSTR pszResName -- BITMAP名称
* @return HBITMAP -- 成功返回BITMAP的句柄,失败返回0
* Describe
*/
virtual HBITMAP LoadBitmap(LPCTSTR pszResName)=; /**
* LoadCursor
* @brief 从资源中加载光标
* @param LPCTSTR pszResName -- 光标名
* @return HCURSOR -- 成功返回光标的句柄,失败返回0
* Describe 支持动画光标
*/
virtual HCURSOR LoadCursor(LPCTSTR pszResName)=; /**
* LoadImage
* @brief 从资源加载一个IBitmap对象
* @param LPCTSTR strType -- 图片类型
* @param LPCTSTR pszResName -- 图片名
* @return IBitmap * -- 成功返回一个IBitmap对象,失败返回0
* Describe 如果没有定义strType,则根据name使用FindImageType自动查找匹配的类型
*/
virtual IBitmap * LoadImage(LPCTSTR strType,LPCTSTR pszResName)=; /**
* LoadImgX
* @brief 从资源中创建一个IImgX对象
* @param LPCTSTR strType -- 图片类型
* @param LPCTSTR pszResName -- 图片名
* @return IImgX * -- 成功返回一个IImgX对象,失败返回0
* Describe
*/
virtual IImgX * LoadImgX(LPCTSTR strType,LPCTSTR pszResName)=; /**
* GetRawBufferSize
* @brief 获得资源数据大小
* @param LPCTSTR strType -- 资源类型
* @param LPCTSTR pszResName -- 资源名
* @return size_t -- 资源大小(byte),失败返回0
* Describe
*/
virtual size_t GetRawBufferSize(LPCTSTR strType,LPCTSTR pszResName)=; /**
* GetRawBuffer
* @brief 获得资源内存块
* @param LPCTSTR strType -- 资源类型
* @param LPCTSTR pszResName -- 资源名
* @param LPVOID pBuf -- 输出内存块
* @param size_t size -- 内存大小
* @return BOOL -- true成功
* Describe 应该先用GetRawBufferSize查询资源大小再分配足够空间
*/
virtual BOOL GetRawBuffer(LPCTSTR strType,LPCTSTR pszResName,LPVOID pBuf,size_t size)=; /**
* FindImageType
* @brief 查询与指定名称匹配的资源类型
* @param LPCTSTR pszImgName -- 资源名称
* @return LPCTSTR -- 资源类型,失败返回NULL
* Describe 没有指定图片类型时默认从这些类别中查找
*/
virtual LPCTSTR FindImageType(LPCTSTR pszImgName) =;
}; /**
* Helper_FindImageType
* @brief 查询与指定名称匹配的资源类型
* @param IResProvider * pResProvider -- 当前的ResProvider
* @param LPCTSTR pszImgName -- 资源名称
* @return LPCTSTR -- 资源类型,失败返回NULL
* Describe 提供一个公共的辅助函数
*/ }//namespace SOUI
这个接口的实现类通过实现这些既定接口来完成图标(HICON),光标(HCURSOR),位图(HBITMAP),一般图片(IBitmap)的解码,同时也提供原始数据(RawData)的读取。
在SOUI系统中内置了两种类型的资源加载(ResProvider)模块:SResProviderPE 和 SResProviderFiles,同时也通过外置组件的形式提供了从ZIP文件加载资源的功能。
这三种资源加载方式基本上涵盖了目前常见的资源加载方式。
为了能够从PE的资源数据段中加载资源,我们需要将uires.idx中索引的文件转换成PE资源可以识别的资源类型+资源名(不是资源ID)的形式。
为了达到这个目的,我们只需要在VS的资源文件中(.rc)将SOUI的资源中定义的文件按照uires.idx定义的类型和名称加进去即可。
手工添加资源文件很难保证不写错。为此,我提供了一个工具(tools\uiresbuilder.exe),这个工具接收一组命令参数,用来将uires.idx转换成一个RC编译器可以识别的.rc2文件(命令行参见使用向导生成的工程)。要编译该.rc2文件,需要在.rc的资源包含中加上我们生成的.rc2文件。

如果程序中的资源不从PE资源加载,则不需要编译soui_res.rc2文件,以减少程序体积。
SResProviderPE, SResProviderFiles 和 SResProviderZIP分别从PE资源,文件夹及ZIP文件包中初始化资源:
#if (RES_TYPE == 0)//从文件加载
CreateResProvider(RES_FILE,(IObjRef**)&pResProvider);
if(!pResProvider->Init((LPARAM)_T("uires"),))
{
SASSERT();
return ;
}
#elif (RES_TYPE==1)//从EXE资源加载
CreateResProvider(RES_PE,(IObjRef**)&pResProvider);
pResProvider->Init((WPARAM)hInstance,);
#elif (RES_TYPE==2)//从ZIP包加载
bLoaded=pComMgr->CreateResProvider_ZIP((IObjRef**)&pResProvider);
SASSERT_FMT(bLoaded,_T("load interface [%s] failed!"),_T("resprovider_zip")); ZIPRES_PARAM param;
param.ZipFile(pRenderFactory, _T("uires.zip"),"souizip");
bLoaded = pResProvider->Init((WPARAM)¶m,);
SASSERT(bLoaded);
#endif
资源加载成功后,调用SApplication::AddResProvider(IResProvider *)接口将创建的资源加载器交给SOUI系统管理。
SApplication::AddResProvider可以调用多次,便于加载不同的资源。
资源加载后不再需要了也可以使用SApplication::RemoveResProvider(IResProvider *)来删除。
程序中要使用一个资源时,首先调用SApplication::HasResource来查询一个资源是否存在,然后再根据资源类型选择不同的接口加载资源。
SApplication中管理着一个IResProvider列表,系统采用后进先查的算法处理资源重名,即最后调用AddResProvider加进来的资源加载器优先级最高。
应用程序中资源的组织
一个基于SOUI开发的应用程序通常资源分为两部分:
1、控件默认的系统资源,也可以理解为主题(theme)资源。
2、应用程序自定义的资源。
系统资源又分为3部分:
1、控件默认的皮肤(Skin):
SOUI中很多控件都必须要定义一个皮肤(ISkinObj,称之为绘图对象更好解理),例如绘制checkbox,radiobox,combobox等控件出出现的位图部分。如何在应用程序中使用了这些控件,则必须为它们定义这些皮肤资源。为了简化界面配置,我统一将这些资源进行命名,并打包到一起(参见trunk\soui-sys-resource\theme_sys_res\sys_xml_skin.xml)。
下面是系统中使用定义的命名ISkinObj:
const wchar_t * BUILDIN_SKIN_NAMES[]=
{
L"_skin.sys.checkbox",
L"_skin.sys.radio",
L"_skin.sys.focuscheckbox",
L"_skin.sys.focusradio",
L"_skin.sys.btn.normal",
L"_skin.sys.scrollbar",
L"_skin.sys.border",
L"_skin.sys.dropbtn",
L"_skin.sys.tree.toggle",
L"_skin.sys.tree.checkbox",
L"_skin.sys.tab.page",
L"_skin.sys.header",
L"_skin.sys.split.vert",
L"_skin.sys.split.horz",
L"_skin.sys.prog.bkgnd",
L"_skin.sys.prog.bar",
L"_skin.sys.slider.thumb",
L"_skin.sys.btn.close",
L"_skin.sys.btn.minimize",
L"_skin.sys.btn.maxmize",
L"_skin.sys.btn.restore",
L"_skin.sys.menu.check",
L"_skin.sys.menu.sep",
L"_skin.sys.menu.border",
L"_skin.sys.menu.skin",
L"_skin.sys.icons",
L"_skin.sys.wnd.bkgnd"
};
如果程序中没有使用到特定控件,也可以不在系统资源中提供对应的ISkinObj。
2、系统使用的MsgBox布局模板:
MsgBox是应用程序常用的功能。SOUI通过提供一个MSGBOX的XML布局模板来实现。如果需要修改MsgBox的样式,只需要修改这个XML。
<SOUI title="mesagebox" width="200" height="100" appwin="0" frameSize="40,30,10,80" minSize="300,100" resize="0" translucent="1" trCtx="messagebox">
<style>
<class name="normalbtn" skin="_skin.sys.btn.normal" font="" colorText="#385e8b" colorTextDisable="#91a7c0" textMode="25" cursor="hand" margin-x="0"/>
</style>
<root skin="_skin.sys.wnd.bkgnd">
<caption id="101" pos="0,0,-0,29">
<text pos="11,9" class="cls_txt_red" name="msgtitle" >title</text>
<imgbtn id="2" skin="_skin.sys.btn.close" pos="-45,0" tip="close"/>
</caption> <window pos="5,30,-5,-50">
<icon name="msgicon" pos="0,0,32,32" display="0"/>
<text name="msgtext" pos="[0,0" colorText="#0000FF" multilines="1" maxWidth="300"/>
</window>
<tabctrl name="btnSwitch" pos="0,-50,-0,-0" tabHeight="0">
<page>
<button pos="|-50,10,|50,-10" name="button1st" class="normalbtn">button1</button>
</page>
<page>
<button pos="|-100,10,|-10,-10" name="button1st" class="normalbtn">button1</button>
<button pos="|10,10,|100,-10" name="button2nd" class="normalbtn">button2</button>
</page>
<page>
<button pos="|-140,10,|-50,-10" name="button1st" class="normalbtn">button1</button>
<button pos="|-45,10,|45,-10" name="button2nd" class="normalbtn">button2</button>
<button pos="|50,10,|140,-10" name="button3rd" class="normalbtn">button3</button>
</page>
</tabctrl>
</root>
</SOUI>
上面是SOUI默认提供的模板。
在这个模板中,只提供了必须的命名对象。如果要修改这个模板,这些命名对象不能缺少,不过布局位置可以任意调整。
3、Edit控件使用的右键菜单定义XML:
edit控件的菜单相对固定,因此我们也采用系统资源的形式提供一个预定义的右键菜单定义。
<editmenu trCtx="editmenu" iconSkin="_skin.sys.icons" itemHeight="26" iconMargin="4" textMargin="8" >
<item id="1" icon="3">cut</item>
<item id="2" icon="4">copy</item>
<item id="3" icon="5">paste</item>
<item id="4" >delete</item>
<sep/>
<item id="5">select all</item>
</editmenu>
和前面两个不同,菜单资源每一个item必须包含一个id,取值从1-5,菜单项的位置可以任意。
应用程序自定义资源
很显然,系统资源提供的样式使得应用中每一个同类控件长得都一样。但是很多时候我们会希望两个功能相似的控件有不一样的长相,这就需要使用用户自定义资源。
用户自定义资源和系统资源一样,只不过它可以包含更多类型(任意类型),资源也可以任意命名(只要不和系统资源冲突)。
下面为demo中使用的自定义资源(uires.idx):
<?xml version="1.0" encoding="utf-8"?>
<resource>
<UIDEF>
<file name="xml_init" path="xml\init.xml" />
</UIDEF>
<ICON>
<file name="LOGO" path="image\img_logo.ico" />
</ICON>
<CURSOR>
<file name="ANI_ARROW" path="image\021.ani" />
<file name="CUR_TST" path="image\camera_capture.cur"/>
</CURSOR>
<LAYOUT>
<file name="maindlg" path="xml\dlg_main.xml" />
<file name="menu_test" path="xml\menu_test.xml" />
<file name="page_layout" path="xml\page_layout.xml" />
<file name="page_treebox" path="xml\page_treebox.xml" />
<file name="page_treectrl" path="xml\page_treectrl.xml" />
<file name="page_misc" path="xml\page_misc.xml" />
<file name="page_webkit" path="xml\page_webkit.xml" />
<file name="page_about" path="xml\page_about.xml" />
</LAYOUT>
<IMGX>
<file name="png_page_icons" path="image\page_icons.png" />
<file name="png_small_icons" path="image\small_icons.png" /> <file name="webbtn_back" path="image\webbtn_back.png" />
<file name="webbtn_forward" path="image\webbtn_forward.png" />
<file name="webbtn_refresh" path="image\webbtn_refresh.png" /> <file name="png_treeicon" path="image\TreeIcon.png"/>
<file name="png_menu_border" path="image\menuborder.png" /> <file name="png_vscroll" path="image\vscrollbar.png" /> <file name="png_tab_left" path="image\tab_left.png" />
<file name="png_tab_left_splitter" path="image\tab_left_splitter.png" />
<file name="png_tab_main" path="image\tab_main.png" />
<file name="btn_menu" path="image\btn_menu.png" />
</IMGX>
<GIF>
<file name="gif_horse" path="image\horse.gif"/>
<file name="gif_penguin" path="image\penguin.gif"/>
</GIF>
<rtf>
<file name="rtf_test" path="rtf\RTF测试.rtf"/>
</rtf>
<script>
<file name="lua_test" path="lua\test.lua"/>
</script>
<translator>
<file name="lang_cn" path="translation files\lang_cn.xml"/>
</translator>
</resource>
不管是系统资源还是用户资源都由资源管理模块管理。
实际上这两种资源可以合并到一个资源包中交给系统管理。有心人可能注意到了项目中使用的系统资源使用的是一个资源DLL,并且没有uires.idx文件。正是因为使用了资源DLL这一形式,才可以不提供uires.idx文件,因为PE资源本身已经有了分类命名(每一个资源都在一个类型下)。
DEMO中使用系统资源和用户资源
SApplication *theApp=new SApplication(pRenderFactory,hInstance);
//定义一人个资源提供对象,SOUI系统中实现了3种资源加载方式,分别是从文件加载,从EXE的资源加载及从ZIP压缩包加载
CAutoRefPtr<IResProvider> pResProvider;
#if (RES_TYPE == 0)//从文件加载
CreateResProvider(RES_FILE,(IObjRef**)&pResProvider);
if(!pResProvider->Init((LPARAM)_T("uires"),))
{
SASSERT();
return ;
}
#elif (RES_TYPE==1)//从EXE资源加载
CreateResProvider(RES_PE,(IObjRef**)&pResProvider);
pResProvider->Init((WPARAM)hInstance,);
#elif (RES_TYPE==2)//从ZIP包加载
bLoaded=pComMgr->CreateResProvider_ZIP((IObjRef**)&pResProvider);
SASSERT_FMT(bLoaded,_T("load interface [%s] failed!"),_T("resprovider_zip"));
ZIPRES_PARAM param;
param.ZipFile(pRenderFactory, _T("uires.zip"),"souizip");
bLoaded = pResProvider->Init((WPARAM)¶m,);
SASSERT(bLoaded);
#endif
//将创建的IResProvider交给SApplication对象
theApp->AddResProvider(pResProvider);
//加载系统资源
HMODULE hSysResource=LoadLibrary(SYS_NAMED_RESOURCE);
if(hSysResource)
{
CAutoRefPtr<IResProvider> sysSesProvider;
CreateResProvider(RES_PE,(IObjRef**)&sysSesProvider);
sysSesProvider->Init((WPARAM)hSysResource,);
theApp->LoadSystemNamedResource(sysSesProvider);
}
//加载全局资源描述XML
theApp->Init(_T("xml_init"));
可以看到这里根据预定义宏:RES_TYPE提供了3种参考资源加载形式来加载用户自定义资源。然后再采用SResProviderPE的资源加载器从系统资源DLL中加载系统资源。
加载系统资源一个关键步骤在于调用:
theApp->LoadSystemNamedResource(sysSesProvider);
这个函数从系统资源加载器中读取那些命名的系统资源。
所有资源加载完成后调用:
//加载全局资源描述XML
theApp->Init(_T("xml_init"));
来初始化资源中定义的全局Skin,Style,ObjAttr对象。
大家可以想一想怎么样把这两种资源使用一个资源加载器来完成。
第十一篇:SOUI系统资源管理的更多相关文章
- 第二十一篇:SOUI中的控件注册机制
Win32编程中,用户需要一个新控件时,需要向系统注册一个新的控件类型.注册以后,调用::CreateWindow时才能根据标识控件类型的字符串创建出一个新的控件窗口对象. 为了能够从XML描述的字符 ...
- 第十一篇 SQL Server安全审核
本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...
- 【译】第十一篇 SQL Server安全审核
本篇文章是SQL Server安全系列的第十一篇,详细内容请参考原文. SQL Server审核SQL Server审核是指你可以在数据库或服务器实例监控事件.审核日志包含你选择捕获的事件的列表,在服 ...
- 解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)
解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译) http://improve.dk/automated-testing-of-orcamdf-against ...
- 第十一篇 SQL Server代理维护计划
本篇文章是SQL Server代理系列的第十一篇,详细内容请参考原文 在这一系列的上一篇,我们看了使用代理帐户模仿Windows安全上下文完成作业步骤的工作.大多数子系统支持代理账户,同时子系统限制代 ...
- Python开发【第二十一篇】:Web框架之Django【基础】
Python开发[第二十一篇]:Web框架之Django[基础] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5237704.html Python之 ...
- 【译】第十一篇 SQL Server代理维护计划
本篇文章是SQL Server代理系列的第十一篇,详细内容请参考原文 在这一系列的上一篇,我们看了使用代理帐户模仿Windows安全上下文完成作业步骤的工作.大多数子系统支持代理账户,同时子系统限制代 ...
- 跟我学SpringCloud | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪
SpringCloud系列教程 | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪 Springboot: 2.1.6.RELEASE SpringCloud: ...
- Spring Cloud第十一篇 | 分布式配置中心高可用
本文是Spring Cloud专栏的第十一篇文章,了解前十篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...
随机推荐
- cf592d
题意:给出一个无根树,点数为10^5,所有边的长度为1.给定其中有一些点是受到攻击的. 现在要求一个人选定一个点作为起点,走遍所有的受攻击点(不用再回到起点). 需要的最短距离是多少,选定的起点是哪个 ...
- ios 在程序中使用iCloud
注意,这里说的使用icould不是用icloud进行系统备份,那个功能不需要我们写代码,备份到icloud的东西我们也不能操作.我们指的是以下这3种icloud使用方法: 这里有3中使用方法, Key ...
- 无IDE时编译和运行Java
最近 Java subreddit 出现了一篇”在没有IDE的情况下编译Java包” 的帖子,这个帖子抛出了这么一个问题,“是否存在一个命令可以编译一组处于同一文件夹下独立包内的java文件的方法(这 ...
- SAP 凭证类别
文章摘要:基本分为收.付.转三类,和传统的会计凭证分类一样.SA,总帐凭证.(转帐凭证)KA,供应商凭证.(付款凭证)KR,应付供应商凭证.(付款凭证)KG,供应商转来的红字发票,SAP称为贷方凭证. ...
- Effective C++ -----条款20:宁以pass-by-reference-to-const替换pass-by-value Prefer pass-by-reference-to-const to pass-by-value
尽量以pass-by-reference-to-const替换pass-by-value.前者通常比较高校,并可避免切割问题(slicing problem). 以上规则并不适用于内置类型,以及STL ...
- webclient 和httpclient 应用
//webclient应用 MyImageServerEntities db = new MyImageServerEntities(); public ActionResult Index() { ...
- 【编程题目】设计包含 min 函数的栈
2.设计包含 min 函数的栈(栈)定义栈的数据结构,要求添加一个 min 函数,能够得到栈的最小元素.要求函数 min.push 以及 pop 的时间复杂度都是 O(1). 我的思路: 用一个额外的 ...
- sql 查询表的所有详细信息
SELECT (case when a.colorder=1 then d.name else '' end) as 表名,--如果表名相同就返回空 a.colorder as 字段序号, a.nam ...
- 关于Visual Studio 2013 编译 multi-byte character set MFC程序出现 MSB8031 错误的解决办法
转自:http://blog.csdn.net/xiaochunzao/article/details/16987703 Visual Studio 2013 编译旧的 multi-byte char ...
- Linux解决关闭终端后终止服务问题
可使用nohup. 具体使用方法,参见:http://zjking.blog.51cto.com/976858/1117828