Win32 二进制资源格式

来源:互联网 发布:linux用户权限777回退 编辑:程序博客网 时间:2024/04/28 04:36
Win32 二进制资源格式

作者:Floyd Rogers
翻译:和阳阳

前言

本文档由微软开发者技术支持(Microsoft Developer Support)编辑并发布。它描述了Win32资源的二进制格式。我们认为这可能对程序开发工作有所帮助,因此提供了本信息。不幸的是在Windows NT 最终版发布前本文档提供的信息可能会有所改变,微软并不会因为发布了本文档就负责保持本资源格式。任何关于此信息的后续问题将会在CompuServe MSWIN32论坛的第四区发布。

——Steve Firebaugh
微软开发者技术支持

1.概述
1.1 与Windows 16(Win 3.0/3.1) 对照比较
1.2 UNICODE字符串
1.3 双字(DWORD)对齐
2.总体信息
2.1 新语句
2.1.1 新按钮(Button)语句
2.1.1.1 AUTO3STATE
2.1.1.2 AUTOCHECKBOX
2.1.1.3 AUTORADIOBUTTON
2.1.1.4 PUSHBOX
2.1.1.5 STATE3(3STATE)
2.1.1.6 USERBUTTON
2.1.2 EXSTYLE语句
2.1.3 CHARACTERISTICS语句
2.1.4 VERSION语句
2.1.5 LANGUAGE语句
2.1.6 MESSAGETABLE语句
2.1.7 对UNICODE字符串的附加语法
3.资源头格式
3.1 DataSize
3.2 HeaderSize
3.3 Type
3.4 Names
3.5 附加资源头信息
3.5.1 DataVersion
3.5.2 MemoryFlags
3.5.3 LanguageId
3.5.4 Version与Characteristics
3.6 区分16位与32位资源文件
3.7 文件对齐
4.资源数据格式
4.1 版本资源
4.2 图标资源
4.3 菜单资源
4.4 对话框资源
4.5 光标资源
4.6 位图资源
4.7 字体和字体目录资源
4.8 字符串表资源
4.9 加速键表资源
4.10 用户定义的资源与RCDATA
4.11 名字表与错误表资源
4.12 版本资源
4.13 消息表(Messagetable)资源
5.修改日志


1.概述

本文档详细描述了Windows 32 API(Windows NT 3.1 和 Win32s)的二进制资源文件(.res)格式的结构。它与现有的Windows 16(Win 3.0/3.1)结构基本相似,但支持了更多更好的新特性,比如UNICODE字符串、版本头和双字对齐等等。因此若想支持这些新特性,资源编译器所生成的文件格式必须相对于Windows 16 有所改变。

1.1 Windows 3.0/3.1 与 Windows 32 对比

Windows 16资源文件包含了一个以上的二进制资源。每个资源都前置一个可变长度的数据结构,它包含:类型、名称、标志和大小。类型与名称域包含一个标识此类型的字符串或一个标识此资源ID的单字值。标志域告诉系统应当如何将此资源载入内存,大小域指出了资源的大小,以字节为单位。因此大小域实际上就是一个指向文件中下一个资源的指针。

Windows 32(NT和Win32s)资源文件继承了此节构,并扩充了头信息,增加了几个域。而且给一些预定义资源(如菜单和对话框)增加了一些域,并使这些结构能够单字或双字对齐,以及增加了UNICODE(16位字符)支持。

还有一个不同点,不过它没什么重要性。它不直接影响资源文件的结构,不过它影响了资源文件是如何合并在可执行文件中的方法。Windows NT使用COFF格式对象。因此,实际上Windows 32可执行文件格式与Windows 16大相径庭,SDK提供了一个转换工具:CVTRES,用来将一个资源文件转换为COFF对象。连接器会直接将这个对象合并到最终的可执行文件中去。和Windows 16一样,不能仅通过多次运行资源编译器来更新资源:必须重新进行连接。

不过Windows 32 API 提供了一些API,使程序可以枚举可执行文件内的所有资源,并可以单独更新其中的资源。

1.2 UNICODE字符串

资源文件中的所有字符串现在都存储为UNICODE格式。在这种格式下,所有的字符都由一个16位(单字)值表示。最开始的128个字符与Windows ANSI字符集完全相同(但这些字符都以16位格式存在,而不是8位)。第160-254个字符与标准Windows字符集相同(注意:第128-159个字符是非法Unicode代码点(codepoint))。这意味着字符串将以UNICODE_NULL结束,而不是单独一个NULL。资源编译器调用Windows API 中的MultiByteToWideChar函数将ASCII字符串转换位UNICODE字符串。所有溢出(escaped)的字符都被当作合法UNICODE字符直接存储。当这些字符串被程序以ASCII字符读出(例如使用LoadString API)时,系统将把它们再由UNICDOE转换为ASCII字符。

仅有的例外是在RCDATA语句中的字符串。这些“伪”字符串并不是真正的字符串,只被当作一些字节的集合。用户可能会用RCDATA语句存储一些自定义的数据结构,认为确定的数据会被存储在确定的位移。如果一个“伪”字符串被自动转换为UNICODE字符串存储起来,可想而知会发生什么样的事情。因此这些“伪”字符串必须以它的本来面目存储下来:ASCII字节。若想在RCDATA语句中包含UNICODE字符串,用户可以使用L前缀的字符串。

1.3 双字(DWORD)对齐

为使二进制资源文件更容易读写,在Windows 32下,文件中的所有对象都是双字对齐的。包括头信息(headers)和数据项(data entries)。这并不会改变资源数据结构中域的顺序,但会在这些域中间增加一些填充域。

font和fontdir结构是仅有的一个例外。因为这两个结构是直接拷贝自别的文件的,它们并不被RC(Resource Compiler,资源编译器)所使用。

2.一般信息

资源编译器通过分析资源描述文件(.rc)、包含任何其它的资源数据文件(如.ICO图标,.CUR光标,.BMP位图,.FNT字体文件等)来创建资源文件。资源文件包含了在可执行文件中创建资源表的所有必须的信息。资源文件的主要目的是为了加速“编辑-编译-链接”循环,因为它使得资源不必被重新编译。

当前存在大约十几种预定义资源类型。包括菜单、对话框、加速键、字符串、图标、光标、位图、字体和版本信息。Windows系统使用这些资源定义应用程序窗口的显示。资源脚本使得程序编制者可以以一种简易的可编辑形式来表现这些特性。其它类型被保留作应用程序自定义的数据所使用。资源编译器不会尝试修改这些用户自定义的数据(比如将16位数据转换为32位数据)。

Windows 32可执行文件并不是一个分段的映像。在16位可执行文件中,每个资源都被单独放在一个段内。Windows 32可执行文件将所有资源统一放在一个对象或区域中。Windows 32可执行文件同时提供一个二进制排序的资源表以允许快速查询一个特定的资源,而不是像16位可执行文件那样只提供一个仅可进行线性查询的表。正因如此,Windows 32可执行文件更加复杂,很难直接对其进行更新,因此Windows 32 API提供了一些函数用来直接修改资源数据。

能够将资源文件转换为一个COFF对象的CVTRES转换工具会创建一个资源表。这个表包含三个目录,以类型、名称、语言为索引顺序。类型和名称目录均由两部分组成:一部分是类型或名称的字符串标识,另一部分是它们的单字值标识。由于类型或名称的字符串标识会比单字值标识占用更多的空间,因此微软并不推荐使用。

应当注意的是,由于资源文件里的所有字符串(包括类型和名称的标识字符串)都是UNICODE字符,因此用户程序中的LoadBitmap等函数都必须传入相应的UNICODE字符串作为参数(仅当程序使用UNICODE版本的API集合而不是ASCII版本的API集合时)。可以使用定义在winnt.h文件中的TEXT宏修饰字符串来自动达到此目的。

第三个目录,语言,使程序开发者可以在一个单独的可执行文件中提供多语言支持。例如,一个映像文件可以很容易地同时支持法语、加拿大法语以及比利时法语三种语言的资源。理论上一个应用程序可以同时支持UNICODE标准所支持的所有语言,虽然最终的可执行文件可能会大得难以承受。不过,由于系统提供了可以修改映像内资源的工具,因此应用程序的安装程序可以为每个用户自定义程序的映像文件,从而删掉不需要的语言以节省空间,减小最终的映像文件的大小。

2.1 新语句

Windows 32资源编译器可以处理几个新添加的语句。

2.1.1 新按钮语句

这几个语句允许程序员自由使用,就像PUSHBUTTON、DEFAULTPUSHBUTTON等语句一样。

它们的语法与PUSHBUTTON等相同。

2.1.1.1 AUTO3STATE

允许声明一个AUTO3STATE按钮。

2.1.1.2 AUTOCHECKBOX

允许声明一个AUTOCHECKBOX按钮。

2.1.1.3 AUTORADIOBUTTON

允许声明一个AUTORADIOBUTTON按钮。

2.1.1.5 PUSHBOX

允许声明一个PUSHBOX按钮。

2.1.1.6 STATE3

允许声明一个3STATE按钮(由于语法而将3放在了后面)。

2.1.1.7 USERBUTTON

允许声明一个USERBUTTON用户定义的按钮。

2.1.2 EXSTYLE语句

这个语句允许程序员指名一个对话框或控件窗口的扩展样式(WS_EX_xxx)。有三种声明方法,取决于需要什么。

它可以被直接放在DIALOG语句下面(像CAPTION和STYLE语句一样),以应用于对话框窗口。

    EXSTYLE <flags>

它可以与内存标志一起被放在DIALOG语句中。

    FOOBAR DIALOG [MemFlags...] [EXSTYLE=<flags>] x, y, dx, dy
   
它也可以被单独放在CONTROL、PUSHBUTTON、LTEXT等语句的结尾部分。

    AUTOCHECHBOX "autocheckbox", id, x, y, dx, dy
    [styleflags] [exstyleflags]

2.1.3 CHARACTERISTICS语句

这个语句允许程序员指定可能被(第三方)资源文件读写工具使用的资源信息。它对系统没有任何意义,而且也不会存储在映像文件中。

    CHARACTERISTICS <用户定义的DWORD值>

2.1.4 VERSION语句

这个语句使程序可以在资源文件中指定资源的版本号(供读写资源文件的工具使用)。它对系统没有任何意义,而且也不会存储在映像文件中。

    VERSION <用户定义的DWORD值>

2.1.5 LANGUAGE语句

LANGUAGE语句用来指定资源或资源的一段所使用的语言。它可以被放在资源脚本文件中一个单行语句(比如ICON、CURSOR、BITMAP语句)所能放置到的任何地方。它的作用域从它定义的位置开始,直到下一个LANGUAGE语句,或者文件尾。

    LANGUAGE <主数字>, <次数字>

其中<主数字>表示语言ID,<次数字>表示字语言ID。应当使用在winnt.h文件中定义的值。

LANGUAGE语句也可能连同其它可选语句(如CAPTION、STYLE等)被放在MENU、DIALOG、STRINGTABLE、ACCELERATOR和RCDATA资源的BEGIN语句之前,此时它的作用域仅限于此资源。

2.1.6 MESSAGETABLE语句

MESSAGETABLE语句被用来包含一个消息表。消息表是一个有特殊意义的字符串表,通常包含一些错误消息或信息,而且可能含有格式字符串信息(如“/n”、“/t”等),它的格式是:

    <nameid> MESSAGETABLE <filename>

2.1.7 UNICODE字符串的附加语法

在资源脚本中,使用双引号括起来的字符串都被看作是ASCII字符串(在当前代码页),除非使用"L"或"l"字符前缀,例如:
    L"这是一个Unicode字符串"
使用这种语法定义UNICODE字符串有两个效果。在RCDATA语句中,它会使编译器将字符串存储为UNICODE而不是ASCII。在任何情况下,使用这种语法,嵌入的不可见字符将被翻译为UNICODE代码点——一个16位UNICODE字符,例如:
    L"这是第一行,/x2028这是第二行"
其中0x2028 UNICODE字符是分行符(Line Separator)。任何UNICODE字符都可以被以此种方式嵌入到任何资源脚本字符串中。

3.资源头格式

整个文件的格式实际上只是一些资源文件项简单的连接到了一起。每个资源都包含一个单独的资源信息(比如一个对话框或一个字符串表)。

每个项都由一个资源头和其后的资源数据组成。一个资源头(双字对齐的)由4个元素组成:两个表示资源头和资源数据大小的双字、一个资源类型、一个资源名称,还可能有一些附加的资源信息。资源的数据跟在资源头后面,每种资源类型有其自己的数据格式。

3.1 DataSize

这个域给出了跟在资源头后面的数据的大小(不包括在此资源与后面的任何资源之间的填充数据(编者:也就是说这是此资源的实际大小))。

3.2 HeaderSize

HeaderSize域给出了资源头结构的大小。

3.3 Type

Type域要么是一个数字,要么是一个指向表示类型名称的以空值结尾的UNICODE字符串。这个可变的类型被称为“名称或序数”(Name or Ordinal)域,它经常出现在资源文件中ID出现的地方。

名称或序数域的第一个单字域标志出这个域到底是一个数字还是一个字符串。如果它等于0xffff(一个非法UNICODE字符),那么在它后面的单字信息就是一个类型序号(一个数字)。否则,这个域就是一个UNICODE字符串。

如果类型域是一个数字,那它就代表一个标准的或者用户自定义的资源类型。所有标准的Windows 资源类型都被赋予一个特定值,如下表所示。这个表摘自用来生成RC(资源编译器)的头文件,它包含了绝大多数资源类型的类型序数:

    /*预定义的资源类型*/
    #define    RT_NEWRESOURCE        0x2000
    #define    RT_ERROR            0x7fff
    #define    RT_CURSOR            1
    #define    RT_BITMAP            2
    #define    RT_ICON                3
    #define    RT_MENU                4
    #define    RT_DIALOG            5
    #define    RT_STRING            6
    #define    RT_FONTDIR            7
    #define    RT_FONT                8
    #define    RT_ACCELERATORS        9
    #define    RT_RCDATA            10
    #define    RT_MESSAGETABLE        11
    #define    RT_GROUP_CURSOR        12
    #define    RT_GROUP_ICON        14
    #define    RT_VERSION            16
    #define    RT_NEWBITMAP        (RT_BITMAP | RT_NEWRESOURCE)
    #define    RT_NEWMENU            (RT_MENU | RT_NEWRESOURCE)
    #define    RT_NEWDIALOG        (RT_DIALOG | RT_NEWRESOURCE)

如果类型域是一个字符串,那么这个类型是一个用户自定义的类型。

3.4 Names

名称域标识了每一个资源。根类型域一样,名称域也是一个数字或者一个字符串,区分方法也跟类型域一样。

注意:在类型域和名称域之间不需要填充(双字对齐),由于它们只包含单字数据,名称域将总是自动对齐。不过在名称域之后可能会有一个单字填充,使余下的资源头能够双字对齐。

3.5 附加头信息

附加信息包含更多的关于资源数据的信息,包括大小和语言ID。包含附加信息的资源头结构如下所示:

struct tagResource {
    DWORD    DataSize;            //不包含头结构的资源数据的大小
    DWORD    HeaderSize;            //附加头的长度
    [类型序号或字符串]            //类型标识,id或字符串
    [名称序号或字符串]            //名称标识,id或字符串
    DWORD    DataVersion;        //预定义的资源数据版本
    WORD    MemoryFalgs;        //资源状态
    WORD    LanguageId;            //对NLS的UNICODE支持
    DWORD    Version;            //资源数据的版本
    DWORD    Characteristics;    //数据的特征
    };

附加信息结构总是从资源文件中双字对齐的地方开始,因此在名称域与ResAdditional结构之间可能会有一些填充数据。

3.5.1 DataVersion

DataVersion域用来检查资源头里后跟的数据信息的格式。这可能会在将来用来将附加信息输入到预定义格式中去。

3.5.2 MemoryFlags

wMemoryFlags域解释一个给定资源的状态。这些属性通过在.RC脚本中添加标志来给定。脚本标志包含以下标志值:

    #define    MOVEABLE        0x0010
    #define    FIXED            ~MOVEABLE
    #define    PURE            0x0020
    #define    IMPURE            ~PURE
    #define    PRELOAD            0x0040
    #define    LOADONCALL        ~PRELOAD
    #define    DISCARDABLE        0x1000

NT系统的资源编译器总是忽略MOVEABLE、IMPURE和PRELOAD标志。

3.5.3 LanguageId

语言ID包含在每个资源中,用来指定字符串的语言,当它们需要被翻译回单字节字符串时,将会需要这些信息。因此,可能会有多种资源拥有相同的类型和名称等,但仅仅语言不同。

NLS规范的附录A中有语言ID的相关文档,winnt.h中也有。资源的语言由LANGUAGE语句指定。

3.5.4 Version和Characteristics

当前的资源文件格式中也包含版本和特征域。它们分别由VERSION和CHARACTERISTICS域指定。

3.6 区分16位和32位资源文件

软件开发商可能会开发一些同时支持16位和32位资源文件的资源读写工具,微软为此提供了一个方法:使用非法的类型和名称序数值来区分。

方法是在资源文件中放置一个非法资源。微软选择了以下八个字节:

    0x00 0x00 0x00 0x00 0x20 0x00 0x00 0x00

假设资源文件是16位的。此时类型域是非法的,因为第一个字节告诉我们它是一个字符串,但一个0长度的字符串是非法的。因此这是一个非法的16位资源头,也就是说这个文件是32位的。

假设资源文件是32位的。由上可知数据的大小为0,当然不可能有这种情况。

Windows 32资源编译器在每个32位资源文件中前置这几个字节的数据(后跟的是一个附加数据结构,描述一个长度为0、类型序数为0、名称序数为0的资源),用来区分16位和32位资源文件。任何想要读取资源文件的工具都应忽略这个资源。

3.7 文件对齐

有时,将资源分开到几个脚本中、分开编译这些资源文件,最后将它们合并起来会很有用,因此很有必要指出资源文件都是填充到双字大小的。如果不包括这个填充,可能会导致文件中的资源不能双字对齐。

4. 资源数据格式

对任何预定义的数据类型,所有结构都是双字对齐的,包括位图、图标、字体头结构等等。因此,数据总是从双字边界开始。

4.1 版本资源

版本资源用来记录使用资源文件的程序的版本。版本资源包含固定数量的信息。它的结构如下:

typedef struct tagVS_FIXEDFILEINFO {
    DWORD    dwSignature;        //e.g.    0xfeef04bd
    DWORD    dwStrucVersion;        //e.g.    0x00000042 = "0.42"
    DWORD    dwFileVersionMS;    //e.g.    0x00030075 = "3.75"
    DWORD    dwFileVersionLS;    //e.g.    0x00000031 = "0.31"
    DWORD    dwProductVersionMS;    //e.g.    0x00030010 = "3.10"
    DWORD    dwProductVersionLS;    //e.g.    0x00000031 = "0.31"
    DWORD    dwFileFlagsMask;    // = 0x3f 对应版本 "0.42"
    DWORD    dwFileFlags;        //e.g.    VFF_DEBUG | VFF_PRERELEASE
    DWORD    dwFileOS;            //e.g.    VOS_DOS_WINDOWS16
    DWORD    dwFileType;            //e.g.    VFT_DRIVER
    DWORD    dwFileSubType;        //e.g.    VFT2_DRV_KEYBOARD
    DWORD    dwFileDateMS;        //e.g.    0
    DWORD    dwFileDateLS;        //e.g.    0
    } VS_FIXEDFILEINFO;

4.2 图标资源

.RC脚本中的ICON语句创建的并不是一个单独的资源对象,而是一组资源。这使得Windows程序具有一定的设备独立性(根据不同的硬件配置使用不同的象素位图图标)。Windows组合一组不同象素位数和个数的位图并将它们作为一个图标资源。但在.RES和.EXE文件中,它们被以一组资源的格式存储。这些组被以组件(在此,是不同的图标[type 3])在前,后跟组头信息([Type 14])的格式存储。组头包含必要的信息以使Windows能够选择合适的图标来显示。

组件有如下的格式:

    [资源头(type = 3)]
   
    [DIB头]
    [图标XOR(异或)掩码的颜色DIBits (Color DIBits of icon XOR mask)]
    [AND(与)掩码的单色DIBits (Monochrome DIBits of AND mask)]

每个组件都有一个唯一的序数ID(相对于其它图标组件)。

DIB(设备无关位图)头的域分别描述了掩码的信息,仅有两项例外。第一,高度域同时描述 异或 和 与掩码。在将两个DIB转换为DDB(设备相关位图)之前,高度应当被除2。掩码的大小是固定的,占DIB头大小的一半。第二,每个象素的位数和位的数量参考异或掩码。与掩码总是单色的,只有一个面,每象素一位。在使用图标之前,请先参考一下windows sdk参考材料中关于DIB的信息。由于图标组件的格式与.ico文件非常相似,因此Windows SDK参考文档的9.2节还是很有用的。Windows 32应用程序不应使用DDB。

组头如下描述:

    [组头(type = 14)]

struct IconHeader {
  WORD   wReserved;          // 当前为0
  WORD   wType;              // 图标在此等于1
  WORD   wCount;             // 组件的数量
  WORD   padding;            // 为使双字对齐的填充数据
  };

下面的结构每个组件都有一个:

struct ResourceDirectory {
  BYTE   bWidth;
  BYTE   bHeight;
  BYTE   bColorCount;
  BYTE   bReserved;
  WORD   wPlanes;
  WORD   wBitCount;
  DWORD  lBytesInRes;        // 指向组件
  WORD   wNameOrdinal;
  WORD   padding;            // 填充数据
  };
    
注意:组头由固定的头和重复的组件数据组成。这两部分都有固定的长度,以使能对组件信息进行随机访问。

组头包含了所有来自.ico头和资源描述器的数据。

4.3 菜单资源

菜单资源由一个菜单头后跟一个菜单项的序列组成。有两种类型的菜单项:弹出式(popup)的和一般的菜单项。MENUITEM SEPERATOR是一种特殊的一般菜单项,它的名称为空、ID为0、标志也为0。它们的格式如下:

    [资源头(type = 4)]

struct MenuHeader {
  WORD   wVersion;           //当前为0
  WORD   cbHeaderSize;       //也为0
  };

下面这些项对每一个菜单项都重复:

弹出式菜单项(以 fItemFlags & POPUP 为标志):

struct PopupMenuItem {
  WORD   fItemFlags;
  WCHAR  szItemText[];
  };
    
一般菜单项(以 !(fItemFlags & POPUP) 为标志):
 
struct NormalMenuItem {
  WORD   fItemFlags;
  WORD   wMenuID;
  WCHAR  szItemText[];
  };
    
wVersion与cbHeaderSize结构成员标识菜单模板的版本。对Windows3.0它们皆为0,但将来可能会增加。

单字fItemFlags是一个描述菜单项的标志集合。若POPUP位被设置,则此项为弹出式的。否则它就是一个一般菜单项。还有几个其它的位标志:

    #define    GRAYED         0x0001   // 灰色
    #define    INACTIVE       0x0002   // 未激活
    #define    BITMAP         0x0004   // 位图
    #define    OWNERDRAW      0x0100   // 自画
    #define    CHECKED        0x0008   // 已选中
    #define    POPUP          0x0010   // (内部使用)
    #define    MENUBARBREAK   0x0020   // 'MENUBARBREAK' keyword (不知何意)
    #define    MENUBREAK      0x0040   // 'MENUBREAK' keyword (不知何意)
    #define    ENDMENU        0x0080   // (内部使用)
    
在POPUP菜单的子项中的最后一个菜单项的fItemFlags会被标以ENDMENU(使用“OR”实现)。这是很重要的,因为弹出式菜单项是可以递归的,可能会有好几级菜单。当菜单递归时,菜单项被按顺序插入其中。应用程序可以通过检查菜单项是否有ENDMENU标志来跟踪这个菜单层次。

4.4 对话框资源

一个对话框被包含在一个单个的资源中,它有一个头,还有一部分重复声明对话框中的每一个控件。头格式如下所示:

    [资源头 (type = 5)]
   
struct DialogBoxHeader {
    DWORD lStyle;
    DWORD lExtendedStyle; // NT扩展样式
    WORD NumberOfItems;
    WORD x;
    WORD y;
    WORD cx;
    WORD cy;
    [名称或序数] MenuName;
    [名称或序数] ClassName;
    WCHAR szCaption[];
    WORD wPointSize; // 仅当对话框设置了字体时此域才会使用
    WCHAR szFontName[]; // 同上
    };
   
lStyle项是一个标准窗口样式,由windows.h文件中的标志组成。对话框的默认样式如下:

    WS_POPUP | WS_BORDER | WS_SYSMENU
   
lExtendedStyle项用来指定扩展窗口样式。当在DIALOG语句或其它可设置的语句中指定了扩展样式,那么它们的值就会被存储在这个双字域中。

标记为“名称或序数”的项与资源文件中其余地方的设置方式相同——存储一个名称或一个序数ID。想前面说的一样,若第一个字节为0xffff,则后面的两个字节就是一个序数ID,否则它(包括第一个字节)就是一个以空字符结束的字符串。空字符串用一个单字的0表示。

wPointSize和szFontName项仅当对话框中包含FONT语句是才会设置。可以通过检查lStyle项确定对话框是否包含字体设置。若lStyle & DS_SETFONT (DS_SETFONT = 0x40)为真,则这两项就会被设置。

每个控件的数据始于一个双字边界(因此在两个控件之间可能会存在填充),它们的格式如下:

struct ControlData {
    DWORD lStyle;
    DWORD lExtendedStyle;
    WORD x;
    WORD y;
    WORD cx;
    WORD cy;
    WORD wId;
    [名称或序数] ClassId;
    [名称或序数] Text;
    WORD nExtraStuff;
    };

与前面一样,lStyle项是一个标准窗口样式,由windows.h文件中的标志组成。控件的类型由class指定。为节省空间、加速处理,许多通用Windows类型都以一个单字表示。由于UNICODE中0x8000是一个合法字符,类型序数都前置0xffff,根Type和Name域的序数表示方式相似。单字类型如下列表所示:

    #define BUTTON 0x8000
    #define EDIT 0x8100
    #define STATIC 0x8200
    #define LISTBOX 0x8300
    #define SCROLLBAR 0x8400
    #define COMBOBOX 0x8500
   
lExtendedStyle双字用来指定此控件的扩展样式。扩展样式标志置于CONTROL语句的最后,跟在坐标后面。

控件数据最后的附加信息当前并不使用,但将来可能会被用来存储菜单项信息。通常它的长度为0。

对话框脚本中使用的绝大多数语句都被映射为这些类型(包括它们的样式)。这些样式的值可以在windows.h中找到。所有对话框控件都有默认的WS_CHILD和WS_VISIBLE样式。以下列表为脚本语句使用的默认样式:

    语句               默认类型       默认样式
    CONTROL         None           WS_CHILD|WS_VISIBLE
    LTEXT           STATIC         ES_LEFT
    RTEXT           STATIC         ES_RIGHT
    CTEXT           STATIC         ES_CENTER
    LISTBOX         LISTBOX        WS_BORDER | LBS_NOTIFY
    CHECKBOX        BUTTON         BS_CHECKBOX | WS_TABSTOP
    PUSHBUTTON      BUTTON         BS_PUSHBUTTON | WS_TABSTOP
    GROUPBOX        BUTTON         BS_GROUPBOX
    DEFPUSHBUTTON   BUTTON         BS_DEFPUSHBUTTON | WS_TABSTOP
    RADIOBUTTON     BUTTON         BS_RADIOBUTTON
    AUTOCHECKBOX    BUTTON         BS_AUTOCHECKBOX
    AUTO3STATE      BUTTON         BS_AUTO3STATE
    AUTORADIOBUTTON BUTTON         BS_AUTORADIOBUTTON
    PUSHBOX         BUTTON         BS_PUSHBOX
    STATE3          BUTTON         BS_3STATE
    EDITTEXT        EDIT           ES_LEFT|WS_BORDER|WS_TABSTOP
    COMBOBOX        COMBOBOX       None
    ICON            STATIC         SS_ICON
    SCROLLBAR       SCROLLBAR      None
    
控件文本存储在上面介绍的“名称或序数”域中。

4.5 光标资源

光标资源很像图标资源。它们以成组的格式存储,前置组头。组头也使用固定长度的组件索引,允许随机访问每个单独组件。光标头的结构如下:

    [资源头 (type = 12)]

struct CursorHeader {
  WORD   wReserved;          // 当前为0
  WORD   wType;              // 图标 = 2
  WORD   cwCount;            // 组件数量
  WORD   padding;            // 填充数据
  };
    
下一部分对每个组件资源都是重复的,起始于双字边界:

struct ResourceDirectory {
  WORD   wWidth;
  WORD   wHeight;
  WORD   wPlanes;
  WORD   wBitCount;
  DWORD  lBytesInRes;        // 指向组件
  WORD   wNameOrdinal;
  WORD   padding;            // 填充数据
  };
    
每个光标组件和图标组件也都相似。不过有一个重要的不同点:光标有一个“热点”,而图标没有。下面是组件的结构:

    [资源头 (Type = 1)]
    
struct CursorComponent {
  short  xHotspot;
  short  yHotspot;
  }
    [单色异或掩码(Monochrome XOR mask)]
    [单色异或掩码(Monochrome AND mask)]
    
这些掩码是拷贝自.cur文件的位图。在这一点上,光标资源和图标资源之间的不同点是:图标资源有颜色DIB异或掩码,而光标没有。虽然光标位图是单色位图,没有DIB头和颜色表,但为了方便处理,它们的位仍然以DIB格式存储,关于DIB格式参见SDK参考手册。

4.6 位图资源

Windows 32 可以读取两种设备无关位图(DIB)。DIB的一般类型是Windows 3.0 DIB格式。另一个类型用于OS/2 1.1和1.2版。位图资源包含一个设备无关位图,它的格式是两者之一。可通过它们的头结构来区分这两种DIB。它们的第一个域(双字)存储的都是各自结构的大小。Windows SDK参考3.0第二卷第七节对两种格式都有介绍。一般DIB的头结构是BITMAPINFOHEADER,OS/2 DIB的头结构是BITMAPCOREHEADER。正确的大小值必须赋予结构的第一项(一个双字)。

    [一般资源头(type = 2)]
   
    [BITMAPINFOHEADER 或 BITMAPCOREHEADER]
    [颜色表(若不是24位图形)]
    [位图数据]

注意颜色表是可选项。除24位色位图外的所有位图格式都有一个颜色表。表长度可由下述公式计算出来:
    OS/2:         2×每象素位数×3(单位为字节)
    Windows:     2×每象素位数×4(单位为字节)

位图数据直接放在颜色表后面。

注意:位图文件有一个未对齐的头结构(BITMAPFILEHEADER结构)。但这个头并不存储在资源文件中,因为它只对标识文件类型有用(DIB还是DDB)。

4.7 字体和字体目录资源

字体资源不同于其它的资源,它们并不会被添加到指定应用程序的资源中去。字体资源被添加到一个.exe文件中,然后更名为.fon文件,作为库供应用程序使用。

字体资源使用一个资源组结构。几种字体组件构成一个字体组。每个组件对应.rc文件中的一个FONT语句。组头包含所有访问指定字体的必要信息,后跟所有字体组件。字体组件的格式如下:

    [正常资源头(type = 8)]
   
    [后跟.fnt文件的全部内容,作为资源体(.fnt文件的格式参考Windows SDK)]

字体资源的组头通常处于.res文件的最后。但与光标和图标资源不同,在.res文件中,字体组并不需要是连续的。字体声明可能遍及整个.rc文件,与其它资源声明混在一起。组头是由资源编译器自动加在.res文件尾部的。产生.res文件的程序必须手动添加FONTDIR项。组头结构如下:

    [一般资源头(type = 7)]
   
    WORD NumberofFonts;    //.res文件中包含的字体的总数
   
剩下的数据对每个字体都重复:

WORD fontOrdinal;
struct FontDirEntry {
  WORD   dfVersion;
  DWORD  dfSize;
  char   dfCopyright[60];
  WORD   dfType;
  WORD   dfPoints;
  WORD   dfVertRes;
  WORD   dfHorizRes;
  WORD   dfAscent;
  WORD   dfInternalLeading;
  WORD   dfExternalLeading;
  BYTE   dfItalic;
  BYTE   dfUnderline;
  BYTE   dfStrikeOut;
  WORD   dfWeight;
  BYTE   dfCharSet;
  WORD   dfPixWidth;
  WORD   dfPixHeight;
  BYTE   dfPitchAndFamily;
  WORD   dfAvgWidth;
  WORD   dfMaxWidth;
  BYTE   dfFirstChar;
  BYTE   dfLastChar;
  BYTE   dfDefaultChar;
  BYTE   dfBreakChar;
  WORD   dfWidthBytes;
  DWORD  dfDevice;
  DWORD  dfFace;
  DWORD  dfReserved;
  char   szDeviceName[];
  char   szFaceName[];
  };
    
4.8 字符串表资源

这些表由16个字符串所组成的块(blocks of 16 strings)构成。这些块的组织取决于字符串的ID。ID的最低4位指定字符串在块中的位置,高12位指定字符串在哪个块中。每个块存储为一个资源项。每个字符串或错误表资源块的存储格式如下:

    [一般资源头(type = 6)]
   
        [字符串块。字符串为Pascal格式,在其第一个单字存储其长度。不管有没有字符串,16个块都会填满。其中没有字符串的位置其单字长度值置0。]

注意在资源文件中块不一定会按顺序存储。每个块都被赋予一个序数ID。这个ID值等于字符串ID的高12位再加1(因此序数ID总大于等于1)。块以其在.rc文件中出现的次序存储在.res文件中,再由CVTRES工具将其顺序存储在COFF对象中,再到最终的映像文件中去。

4.9 加速键表资源

加速键表存储为单个资源,允许多个加速键表同时存在。加速键表的格式很简单:没有表头,表中的每个项都是一个四字项。表的最后一项标以十六进制值0x0080(fFlags |= 0x0080)。由于所有项都是定长的,因此可以对其进行随机访问(表中元素数可由资源的长度除以8获得)。下面是加速键表的结构:

    [一般资源头(type = 9)]

下面的结构对每个表项都重复:

struct AccelTableEntry {
  WORD   fFlags;
  WORD   wAscii;
  WORD   wId;
  WORD   padding;
  };

4.10 用户自定义的资源与RCDATA

资源编译器允许程序员包含自定义的资源。用户可以选择一个没有被定义为标准类型的名称来包含一些想要作为资源存储的数据。这个数据可以来自外部文件,也可以放在BEGIN语句和END语句之间。作为可选项,程序员可以将此类型定义为RCDATA来达到相同的效果。

可以想象,这种资源的格式是非常简单的,因为资源编译器不知道这些数据的任何逻辑结构。其组织结构如下:

    [一般资源头(对RCDATA,type = 10;有名称的类型则为用户自定义的类型)]
   
    [BEGIN和END之间的数据或者来自外部文件的数据将不经过翻译而被直接包含在.res文件中去]

4.11 名字表与错误表资源

Windows二进制资源文件格式不支持名字表和错误表资源。

4.12 版本资源

版本资源指定可以被安装程序使用(用来确定系统应当安装此程序或动态链接库的哪一个版本)的信息。也有一个API集合用来查询版本资源。版本资源中存储了三种主要的信息:在VS_FIXEDFILEINFO结构中的主要信息、在可变文件信息(VarFileInfo)结构中的语言信息以及在StringFileInfo中的用户定义的字符串信息。对Windows 32,版本信息资源中的字符串以Unicode格式存储,以备资源的本地化。每块信息都是双字对齐的。

版本资源的结构由VS_VERSION_INFO结构描述。

VS_VERSION_INFO {
    WORD wLength;             /* 版本资源的长度 */
    WORD wValueLength;        /* 这个块的值域长度 */
    WORD wType;               /* 信息类型:  1==字符串, 0==二进制 */
    WCHAR szKey[];            /* Unicode 字符串 KEY 域 */
    [WORD Padding1;]          /* 可能的填充数据 */
    VS_FIXEDFILEINFO Value;   /* 固定文件信息 结构 */
    BYTE Children[];      /* VarFileInfo 或 StringFileInfo 数据的位置 */
};
 
固定文件信息 结构包含版本的基本信息,包括产品和文件的版本号、文件的类型。
 
typedef struct tagVS_FIXEDFILEINFO {
    DWORD dwSignature;        /* 签名 - 总是 0xfeef04bd */
    DWORD dwStrucVersion;     /* 结构版本,当前为0 */
    DWORD dwFileVersionMS;    /* 主版本信息 */
    DWORD dwFileVersionLS;    /* 次版本信息 */
    DWORD dwProductVersionMS; /* 主产品版本号 */
    DWORD dwProductVersionLS; /* 次产品版本号 */
    DWORD dwFileFlagMask;     /* 文件标志掩码 */
    DWORD dwFileFlags;        /*  调试/零售/预发布/... */
    DWORD dwFileOS;           /* 操作系统类型,将总是 Windows 32 值 */
    DWORD dwFileType;         /* 文件类型 (dll/exe/drv/... )*/
    DWORD dwFileSubtype;      /* 文件子类型 */
    DWORD dwFileDateMS;       /* 日期主要部分 */
    DWORD dwFileDateLS;       /* 日期次要部分 */
} VS_FIXEDFILEINFO;
 
用户定义的字符串信息包含在StringFileInfo结构中,它是一个双字符串的集合:信息关键字和信息本身。
 
StringFileInfo {
    WCHAR       szKey[];      /* Unicode "StringFileInfo" */
    [WORD        padding;]    /* 可能的填充数据 */
    StringTable Children[];
};
 
StringTable {
    WCHAR      szKey[];   /* 表示语言的 Unicode 字符串 - 8 字节 */
    String Children[];    /* 子String结构的数组 */
}
 
String {
    WCHAR   szKey[];          /* Unicode编码的KEY字符串 */
                         /* 注意:有一个预定义的关键字列表 */
    [WORD   padding;]         /* 可能的填充数据 */
    WCHAR Value[];            /* Unicode编码的KEY值 */
} String;
 
可变文件信息(VarFileInfo)块包含一个这个版本的应用程序或动态链接库所支持的语言列表 。
 
VarFileInfo {
    WCHAR szKey[];            /* Unicode "VarFileInfo" */
    [WORD padding;];          /* 可能的填充数据 */
    Var        Children[];    /* 子数组 */
};
 
Var {
    WCHAR szKey[];       /* Unicode "Translation" (或其它用户定义的关键字) */
    [WORD padding;]      /* 可能的填充数据 */
    WORD  Value[];       /* 一般情况下是语言的一个或多个值 */
};
 
4.13 消息表(Messagetable)资源

消息表是一个包含已格式化的文本的资源。此文本用来显示一个错误消息或消息框。它代替了错误表资源(从来没使用过)。它的数据由一个MESSAGE_RESOURCE_DATA结构组成。此结构包含一个或多个MESSAGE_RESOURCE_BLOCK,而后者又包含一个或多个MESSAGE_RESOURCE_ENTRY结构。这个结构与STRINGTABLE资源相似。

typedef struct _MESSAGE_RESOURCE_DATA {
    ULONG NumberOfBlocks;
    MESSAGE_RESOURCE_BLOCK Blocks[];
} MESSAGE_RESOURCE_DATA, *PMESSAGE_RESOURCE_DATA;
 
typedef struct _MESSAGE_RESOURCE_BLOCK {
    ULONG LowId;
    ULONG HighId;
    ULONG OffsetToEntries;
} MESSAGE_RESOURCE_BLOCK, *PMESSAGE_RESOURCE_BLOCK;
 
typedef struct _MESSAGE_RESOURCE_ENTRY {
    USHORT Length;
    USHORT Flags;
    UCHAR Text[];
} MESSAGE_RESOURCE_ENTRY, *PMESSAGE_RESOURCE_ENTRY;
 
若USHORT标志(Flags)是MESSAGE_RESOURCE_UNICODE(值为0x0001),则字符串在当前代码页将会被编码为UNICODE,而不是ASCII。
(完)
 
原创粉丝点击