Duilib 源码分析之 xml 加载篇
来源:互联网 发布:淘宝主词是什么 编辑:程序博客网 时间:2024/06/03 22:53
大家都知道, Duilib 的界面内容除了在代码中动态加入外,最常用的就是通过写好 xml 来加载了。今天就介绍一下 Duilib 是如何读取到 xml 并将 xml 内容加载到内存中的。
我们来按加载的流程来分析一下,流程如下:
其实这个流程图并没有太大意义,在这里只不过是想说明一下,xml 加载的时机是在 WM_CREATE 的消息相应函数中处理的。
首先贴出加载资源的代码如下:
CDialogBuilder builder; CDuiString strResourcePath=m_PaintManager.GetResourcePath(); if (strResourcePath.IsEmpty()) { strResourcePath=m_PaintManager.GetInstancePath(); strResourcePath+=GetSkinFolder().GetData(); } m_PaintManager.SetResourcePath(strResourcePath.GetData()); switch(GetResourceType()) { case UILIB_ZIP: m_PaintManager.SetResourceZip(GetZIPFileName().GetData(), true); break; case UILIB_ZIPRESOURCE: { HRSRC hResource = ::FindResource(m_PaintManager.GetResourceDll(), GetResourceID(), _T("ZIPRES")); if( hResource == NULL ) return 0L; DWORD dwSize = 0; HGLOBAL hGlobal = ::LoadResource(m_PaintManager.GetResourceDll(), hResource); if( hGlobal == NULL ) {#if defined(WIN32) && !defined(UNDER_CE) ::FreeResource(hResource);#endif return 0L; } dwSize = ::SizeofResource(m_PaintManager.GetResourceDll(), hResource); if( dwSize == 0 ) return 0L; m_lpResourceZIPBuffer = new BYTE[ dwSize ]; if (m_lpResourceZIPBuffer != NULL) { ::CopyMemory(m_lpResourceZIPBuffer, (LPBYTE)::LockResource(hGlobal), dwSize); }#if defined(WIN32) && !defined(UNDER_CE) ::FreeResource(hResource);#endif m_PaintManager.SetResourceZip(m_lpResourceZIPBuffer, dwSize); } break; } CControlUI* pRoot=NULL; if (GetResourceType()==UILIB_RESOURCE) { STRINGorID xml(_ttoi(GetSkinFile().GetData())); pRoot = builder.Create(xml, _T("xml"), this, &m_PaintManager); } else pRoot = builder.Create(GetSkinFile().GetData(), (UINT)0, this, &m_PaintManager);
以上代码共处理了资源加载的 4 种方式
UILIB_FILE (默认)
加载资源时使用绝对路径或者相对路径。使用这种方式要指定 xml 所在文件夹和 xml 的文件名,也就是要重载 GetSkinFolder() 和 GetSkinFile()。 实际读取 xml 资源时,xml 路径 = GetResourcePath+SkinFile,而 ResourcePath 在未主动设置的情况下 = 模块所在路径+ SkinFolder(上面代码前 5 行),所以我们只需指定目录和文件名即可,当然要将目录放在和模块同一级别的目录下。UILIB_ZIP
这种方式和第一种差不多,只不过 xml 要从 zip 中读取了。使用这种方式要重载 GetSkinFolder() , GetZIPFileName() 和 GetSkinFile() ,查找 zip 文件的方式和第一种方式一样,在模块所在文件夹下的 SkinFolder + ZIPFileName。上述代码的第 12 行SetResourceZip
就是预先设置了 zip 文件名,后续会通过GetResourceZip
得到 zip 名,这个函数和 UILIB_FILE 加载方式中的GetSkinFile()
作用是一样的,都是为了找到指定文件夹下的文件。UILIB_RESOURCE
这种方式是将 xml 文件作为 exe 的资源来进行加载。首先要将 xml 文件添加到 rc 中,资源类型为 “xml”,因为后续从资源中读取 xml 时是从 “xml” 类型中查找的,其次要重载 GetSkinFile(),但要注意,此时不能再返回文件名了,而是要返回 xml 对应的资源 ID,其实是一个通过MAKEINTRESOURCE
得到的地址为 ID 的指针 。UILIB_ZIPRESOURCE
这种方式相当于 UILIB_RESOURCE + UILIB_ZIP。首先要将资源文件压缩到一个 zip 文件中,然后将此 zip 添加到 rc 中,资源类型为 “ZIPRES”,然后重载 GetResourceID(), 返回值为:(例)MAKEINTRESOURCE(IDR_ZIPRES1)
,其中 IDR_ZIPRES1 为 zip 文件的资源 ID。
到这里,如果读者只是想了解如何使用上述 4 中资源加载方式的话,下面的可以不必再读。如果你还想知道以上 4 中方式到底是如何将 xml 的内容加载到内存的话,下面会给予介绍:
这部分实现主要是依靠以下 3 个函数
CControlUI CDialogBuilder::Create(STRINGorID xml, LPCTSTR type, IDialogBuilderCallback* pCallback,CPaintManagerUI* pManager,
CControlUI* pParent);bool CMarkup::LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding = XMLFILE_ENCODING_UTF8);
bool CMarkup::LoadFromFile(LPCTSTR pstrFilename, int encoding = XMLFILE_ENCODING_UTF8);
下面分类型进行分析:
UILIB_FILE
调用CMarkup::LoadFromFile
,在已经知道了 xml 的全路径的情况下,通过 CreateFile 、 GetFileSize 、 ReadFile 将文件内容读取到内存,然后调用 LoadFromMem 将 xml 内容赋值给 m_pstrXML, 后续解析 xml 时就是读取的m_pstrXML
中的内容UILIB_ZIP
解压由类 TUZip 实现- 先调用 OpenZip 打开 zip 文件,传入参数为 zip 路径和打开类型
ZIP_FILENAME
, 得到 HZIP 类型的文件句柄 - 然后调用 FindZipItem ——传入 zip 中所查找的文件名,查询 zip 中是否存在所查文件
- 存在所查找文件的情况下,调用 UnzipItem 将 xml 的内容读取到已申请的 Buffer 中——pByte,然后调用 LoadFromMem
- 先调用 OpenZip 打开 zip 文件,传入参数为 zip 路径和打开类型
UILIB_RESOURCE
- 调用 HRSRC FindResource(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType) 查找资源,参数分别为 exe 的实例句柄、资源的 ID 信息、资源类型(UILIB_RESOURCE 加载形式的情况下,这个参数为 “xml”,所以在添加资源的时候要指定类型为 “xml”),确定指定模块中指定类型和名称的资源所在位置
- 调用 HGLOBAL LoadResource(HMODULE hModule,HRSRC hResInfo), 装载指定资源到全局存储器
- 调用 LPVOID LockResource(HGLOBAL hResData) 获取到资源在内存中的第一个字节的指针,调用 DWORD SizeofResource(HMODULE hModule,HRSRC hResInfo) 获取资源的字节数
- 根据上面函数获取到的指针和字节大小,再调用 LoadFromMem
UILIB_ZIPRESOURCE
实现方式相当于 UILIB_RESOURCE + UILIB_ZIP,这里不再赘述了
以上就是 4 中方式 xml 内容加载到内存中的实现方法,首地址为 m_pstrXML,后续的解析就是针对 m_pstrXML 来进行,相关实现原理会在后续的帖子中进行介绍。
知识点小清单:
CDialogBuilder::Create
函数中有一个宏 :HIWORD
,定义为((WORD)((((DWORD_PTR)(_dw)) >> 16) & 0xffff))
, 含义是取 4 字节内存的高 16 位,为什么要这么做呢 ? 这是因为只有当 xml 加载类型为 UILIB_RESOURCE 时,HIWORD(xml.m_lpstr) != NULL
才会为 false,所以 else 中执行的是读取 Resource 中的内容,而对于 UILIB_RESOURCE 类型的 CDialogBuilder::Create,第一个参数传入的是 MAKEINTRESOURCE(nID),而资源的 ID 不会超过 65535,也就是最大为 2 个字节,所以高 16 位肯定为 0. 而另外 3 种 xml 的加载方式, 第一个参数传入的是指向 xml 名称的字符串首地址,高 16 不可能为 0 (0x0000-0xFFFF 属于内存预留区,进程内存区不会存在高 16 为 0 的情况)。CMarkup::LoadFromMem
中有以下代码:if( dwSize >= 3 && pByte[0] == 0xEF && pByte[1] == 0xBB && pByte[2] == 0xBF ) { pByte += 3; dwSize -= 3;}
对于 UTF-8 编码,类似 WINDOWS 自带的记事本等软件保存文件时,会在文件开头加上 0xEF 0xBB 0xBF 三个字节,也就是所谓的 BOM(Byte Order Mark), 所以如果发现开头有这三个字节,则略过这三个字节处理剩下的部分。
- Duilib 源码分析之 xml 加载篇
- Duilib 源码分析之 xml 解析篇
- DuiLib之-----加载XML
- duilib之源码分析
- duilib之源码分析
- Duilib之源码分析
- duilib之源码分析
- Duilib 源码分析之字体篇
- Duilib 源码分析之控件 name 篇
- Duilib 源码分析之消息流程篇
- Duilib 源码分析之 CResourceManager 篇
- Duilib 源码分析之 CControlUI 篇
- Duilib 源码分析之 Timer 篇
- Duilib 源码分析之 PaintBorder 篇
- Duilib 源码分析之 ToolTip 篇
- Duilib 源码分析之 Shortcut 篇
- Duilib 源码分析之 CScrollBarUI 篇
- Duilib 源码分析之 CLabelUI 篇
- vue.js学习笔记之安装以及项目的创建和运行
- JavaScript之call,apply,bind方法的区别和共同点
- Bootstrap File Input 真正 解决跨域问题
- 知识卡片$http
- 我为什么要写作?
- Duilib 源码分析之 xml 加载篇
- MyBatis精通之路之分页功能的实现(数组分页、sql分页、拦截器,RowBounds分页)
- linux文件系统对磁盘的操作
- mysql重连,连接丢失:The last packet successfully received from the server
- webView模拟登陆
- Vijos 1096题:津津的储蓄计划
- Android自定义View-蜘蛛网属性图(五边形图)
- 学习安卓入门感悟
- 字符串截取(例如:String strParameter = "https://test.shaanxi.qxac.qixinaiche.com/qxac/my/userSignIn.jsp?par)