目录

解析易语言窗口控件资源

基础介绍

通过寻找易语言静态编译的程序规律,我们可以找到一处结构体,寻找的特征码是50 64 89 25 00 00 00 00 81 EC AC 01 00 00 53 56 57,找到后这个结构体大概是下面这个样子:

struct EHead
{
	unsigned int dwMagic;       //未知,值固定为3
	unsigned int szNone2;       //未知,值固定为0
	unsigned int szNone3;       //未知,好像是个随机数,修改不影响程序
	unsigned int lpStartCode;   //用户代码起始地址,不可修改
	unsigned int lpEString;     //常量资源,如果没有常量资源,则为0
	unsigned int dwEStringSize; //常量资源大小,如果没有常量资源,则为0
	unsigned int lpEWindow;     //创建组件信息
	unsigned int dwEWindowSize; //创建组件信息大小
	unsigned int dwLibNum;      //支持库数量
	unsigned int lpLibEntry;    //支持库信息入口
	unsigned int dwApiCount;    //Api数量
	unsigned int lpModuleName;  //指向模块名称
	unsigned int lpApiName;     //指向Api名称
};

可以看到这些结构体包含着易语言程序中的很多重要数据,今天我们要研究的是界面资源部分。

易语言所有的GUI信息都放在lpEWindow这个指针所指向的位置,dwEWindowSize则为信息的总大小,通过对这个信息进行解析,理论上我们可以还原与易语言IDE中几乎一模一样的控件设计。

具体解析规则,我就懒得介绍了,请自行参考E-Decompiler 源码中的GuiParser::ParseGUIResource函数。通过不断尝试进行拆解还原,我们最终大概可以看到这样一个结构:

/images/易语言窗口/窗口结构.png
窗口结构

每个窗口都是一棵树,窗口内的控件则为这个树的子节点,不难理解,通过窗口ID -> 控件ID,我们就能对应到每个控件了。其中值得说明的是窗口本身也属于控件,每个窗口的第一个控件就是窗口控件。

控件类型

/images/易语言窗口/控件类型.png
控件类型

在上图中我们可以看到,窗口.取窗口句柄()这样一个命令的参数中,0x52010001表示控件所属窗口ID,0x6010000表示控件ID,而0x10001这个参数则表示控件的类型ID。

每个控件都有一个控件类型ID,比如窗口的控件类型ID是0x10001,按钮的控件类型是0x1000B。

这个控件类型ID是有规律的,因为易语言有一个支持库机制,我们不妨看看易语言核心支持库的介绍:

提供了51种库定义数据类型,提供了686种命令,提供了335个库定义常量

而编辑框、按钮这些控件就属于核心支持库中的数据类型的一部分,控件ID中的高四位对应的是控件所属支持库在易语言程序中的加载顺序,低四位对应的则是控件在所属支持库中的数据类型数组中的下标,因此窗口控件ID0x10001表示的含义其实是程序所加载的第一个支持库中的第一种控件。

由于系统支持库默认最先加载,所以核心支持库中的控件的高四位必定是1。

控件属性

在易语言中,属性可以分基本属性附加属性两种,像左边顶边宽度高度这些几乎每个控件都有的属性就属于基本属性了,这些字段每个控件排列顺序、排列方式都是一样的,我们可以通用解析。基础属性之后跟着的则是每个控件独特的附加属性,以序列化的形式存储,因此如果我们想要把这些属性还原出来,就必须对每个控件的属性进行反序列化解析。

另外,易语言为了节省空间,控件的附加属性都有一个默认值,即如果控件在IDE中的附加属性未进行任何改动,那么控件的信息结构中,附加属性字段占用大小为0。

控件事件

事实上,控件的事件属于控件基本属性的一部分,事件的结构体很简单:

struct ControlEvent
{
	int EventIndex;          //事件索引
	uint32 EventOffset;      //事件地址便宜
};

EventIndex表示事件类型,EventOffset值等于事件地址相对于用户代码起始地址的偏移。

每个控件都有自己的一张事件列表,例如0表示窗口_创建完毕事件,1表示窗口_可否被关闭事件。

控件的属性,2112 =>

总结

其实还有一些细节,像子控件、父控件,菜单控件之类的,由于作者太懒也没有进行介绍,本文只是一个科普贴。。。。。。

啥也不说了,都在代码里。