注释
自联机文档中首次包含此说明以来,尚未更新以下技术说明。 因此,某些过程和主题可能过期或不正确。 有关最新信息,建议在在线文档索引中搜索您感兴趣的主题。
此说明介绍了 Visual C++ 资源编辑器如何支持在单个项目中共享或跨多个项目共享的多个资源文件和头文件,以及如何利用该支持。 此说明回答以下问题:
何时可能需要将项目拆分为多个资源文件和/或头文件,以及如何执行此作
如何在两个
.H文件之间共享通用的.RC头文件如何将项目资源划分为多个
.RC文件你(和工具)如何管理
.RC、.CPP和.H文件之间的生成依赖关系
应注意,如果将其他资源文件添加到项目,ClassWizard 将无法识别所添加文件中的资源。
此说明的结构如下,用于回答上述问题:
Visual C++如何管理资源文件和头文件 概述概述了 Visual C++中的“资源集包括”命令如何让你在同一项目中使用多个资源文件和头文件。
对 AppWizard 创建的
.RC和.H文件的分析 涉及 AppWizard 创建的应用程序所使用的多个资源和头文件。 这些文件用作你可能需要添加到项目的其他资源文件和头文件的很好模型。包括其他头文件 描述了可能需要包含多个头文件的情况,并提供了如何执行此操作的详细信息。
在两
.RC个文件之间共享头文件 显示了如何在不同项目中的多个.RC文件之间共享一个头文件,或者在同一项目中共享一个头文件。在同一项目中使用多个资源文件 描述你可能希望将项目分解为多个
.RC文件的位置,并提供有关如何执行此作的详细信息。强制实施不可编辑的 Visual C++ 文件 介绍了如何确保 Visual C++不会编辑和无意中重新格式化自定义资源。
管理由多个 Visual C++ 编辑
.RC的文件共享的符号 介绍了如何跨多个.RC文件共享相同的符号以及如何避免分配重复的 ID 数值。管理
.RC、.CPP和.H文件之间的依赖关系描述了 Visual C++ 如何避免不必要地重新编译依赖于资源符号文件的.CPP文件。Visual C++ 如何管理设置包括信息提供有关 Visual C++ 如何跟踪一个
.RC文件包括的多个(嵌套).RC文件和多个头文件的详细技术信息。
Visual C++如何管理资源文件和头文件概述
Visual C++ 将一个 .RC 资源文件和一个对应的 .H 头文件作为紧密耦合的文件对进行管理。 在编辑和保存 .RC 文件中的资源时,您间接地编辑并保存相应 .H 文件中的符号。 尽管你可以一次打开和编辑多个 .RC 文件(使用 Visual C++ 的 MDI 用户界面),但对于任何 .RC 给定文件,你间接编辑一个对应的头文件。
“资源视图的资源包括”对话框
若要访问 “资源包括”,请打开 “资源视图 ”,然后右键单击 .RC 该文件并选择“ 资源包括”。
符号头文件
默认情况下,Visual C++始终命名相应的头文件,而不考虑资源文件 RESOURCE.H的名称(例如 MYAPP.RC)。 Visual C++“资源包括”对话框中的“符号头文件:”部分可用于更改此头文件的名称。 在分区的编辑框中输入新的文件名。
注释
与 .RC 文件不在同一目录中的资源文件必须在相对路径前面加上转义“\”才能正确读取。
Read-Only 符号指令
尽管 Visual C++仅编辑任何给定 .RC 文件的一个头文件,但 Visual C++支持对其他只读头文件中定义的符号的引用。 只读符号指令:“资源包括”对话框中的一个部分,可用于将任意数量的其他只读头文件指定为只读符号指令。 “只读”限制意味着在文件中添加新资源 .RC 时,可以使用在只读头文件中定义的符号。 但是,如果删除资源,符号仍保留在只读头文件中定义。 无法更改分配给只读符号的数值。
编译时指令
Visual C++ 还支持资源文件的嵌套,即通过使用 .RC 指令将一个 #include 文件包含在另一个文件中。 使用 Visual C++ 编辑给定 .RC 文件时,包含的文件中的任何资源都看不到。 但是,编译 .RC 文件时,也会编译包含的文件。 编译时指令:“资源包括”对话框中的一个部分,可指定将任意数量的 文件包括为编译时指令.RC。
请注意,如果您将一个.RC文件读取到 Visual C++ 中,而该文件中包含了另一个未指定为 Compile-Time 指令的.RC文件,会发生什么情况。 将以前用文本编辑器手动维护的.RC文件导入到Visual C++时,可能会出现这种情况。 当 Visual C++读取包含 .RC 的文件时,它将包含的资源合并到父 .RC 文件中。 保存父 .RC 文件时, #include 语句实际上将被包含的资源替换。 如果不希望发生此合并,则应在将父#include文件读取到 Visual C++.RC,从中删除该语句;随后使用 Visual C++,将相同的#include语句作为 Compile-Time 指令添加回来。
在 .RC 文件中,Visual C++ 将以上三种设置包括信息(符号头文件、只读符号指令和编译时指令)保存在 #include 指令和 资源中TEXTINCLUDE。 TEXTINCLUDE 这些资源是通常不需要处理的实现细节,在Visual C++管理集包含信息的方式中进行了说明。
AppWizard 所创建的 .RC 和 .H 文件的分析
检查 AppWizard 生成的应用程序代码可深入了解 Visual C++如何管理多个资源文件和头文件。 下面检查的代码摘录来自 MYAPP 使用默认选项由 AppWizard 生成的应用程序。
AppWizard 创建的应用程序使用多个资源文件和多个头文件,如下图所示:
RESOURCE.H AFXRES.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
AFXRES.RC
AFXPRINT.RC
可以使用 Visual C++ File/Set Includes 命令查看这些多个文件关系。
MYAPP.RC
使用 Visual C++ 编辑的应用程序资源文件。
RESOURCE.H 是特定于应用程序的头文件。 它始终由 AppWizard 命名 RESOURCE.H ,与 Visual C++标头文件的默认命名一致。 此 #include 头文件是资源文件中的第一条语句(MYAPP.RC):
//Microsoft Visual C++ generated resource script
//
#include "resource.h"
RES\MYAPP.RC2
包含一些资源,这些资源不会被 Visual C++ 编辑,但会包含在最终编译的 .EXE 文件中。 默认情况下,AppWizard 不会创建此类资源,因为 Visual C++可以编辑所有标准资源,包括版本资源(此版本中的新功能)。 如果希望将自己的自定义格式化资源添加到此文件,则 AppWizard 会生成一个空文件。
如果使用自定义格式化资源,则可以使用 Visual C++ 文本编辑器将它们添加到 RES\MYAPP.RC2 和编辑它们。
AFXRES.RC 并 AFXPRINT.RC 包含框架的某些功能所需的标准资源。 与 RES\MYAPP.RC2 类似,框架提供的这两个资源文件包括在 MYAPP.RC 末尾,并且这两个资源文件在“设置包括”对话框的编译时指令中指定。 因此,在 Visual C++ 中编辑 MYAPP.RC 时,不会直接查看或编辑这些框架资源,而是编译为应用程序的二进制 .RES 文件和最终 .EXE 文件。 有关标准框架资源的详细信息,包括修改它们的过程,请参阅 技术说明 23。
AFXRES.H 定义了标准符号,例如框架使用的ID_FILE_NEW,并专门用于 AFXRES.RC。 AFXRES.H 还使用 #include 来包括 WINRES.H,其中包含 Visual C++ 生成的 WINDOWS.H 文件和 .RC 所需的 AFXRES.RC 子集。 编辑应用程序资源文件(AFXRES.H)时,MYAPP.RC 中定义的符号可用。 例如, ID_FILE_NEW 用于 FileNew 文件菜单资源中的 MYAPP.RC 菜单项。 无法更改或删除这些框架定义的符号。
包括其他头文件
AppWizard 创建的应用程序仅包含两个头文件: RESOURCE.H 和 AFXRES.H。 仅 RESOURCE.H 特定于应用程序。 在以下情况下,可能需要包括其他只读头文件:
头文件由外部源提供,或者你想要在同一项目的多个项目或多个部分之间共享头文件。
头文件包含你不希望 Visual C++ 在保存文件时更改或筛选出的格式与注释。 例如,你可能希望保留使用符号算术的 #define,例如:
#define RED 0
#define BLUE 1
#define GREEN 2
#define ID_COLOR_BUTTON 1001
#define ID_RED_BUTTON (ID_COLOR_BUTTON + RED)
#define ID_BLUE_BUTTON (ID_COLOR_BUTTON + BLUE)
#define ID_GREEN_BUTTON (ID_COLOR_BUTTON + GREEN)
可以通过使用 Resource Includes 命令,将 #include 语句指定为第二个 Read-Only 符号指令,以此来包含其他只读头文件,如下所示:
#include "afxres.h"
#include "second.h"
新的文件关系图现在如下所示:
AFXRES.H
RESOURCE.H SECOND.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
AFXRES.RC
AFXPRINT.RC
在两个 .RC 文件之间共享头文件
你可能想要在位于不同项目或同一项目中的两个.RC文件之间共享头文件。 为此,请将上述 Read-Only 指令技术应用于这两 .RC 个文件。 如果两 .RC 个文件适用于不同的应用程序(不同的项目),则结果如下图所示:
RESOURCE.H AFXRES.H RESOURCE.H
(for MYAPP1) SECOND.H (for MYAPP2)
\ / \ /
\ / \ /
MYAPP1.RC MYAPP2.RC
/ \ / \
/ \ / \
RES\MYAPP1.RC2 AFXRES.RC RES\MYAPP2.RC2
AFXPRINT.RC
下面讨论了第二个头文件由同一应用程序(项目)中的两 .RC 个文件共享的情况。
在同一项目中使用多个资源文件
Visual C++ 和资源编译器通过 .RC 指令将一个 #include 文件包括在另一文件中,以此支持同一项目中的多个 .RC 文件。 允许多层嵌套。 有多种原因可将项目的资源拆分为多个 .RC 文件:
如果将资源拆分为多个
.RC文件,则更容易管理多个项目团队成员之间的大量资源。 如果使用源代码管理包签出文件并签入更改,将资源拆分为多个.RC文件,则可以更好地控制管理对资源的更改。如果要对资源的某些部分使用预处理器指令(例如
#ifdef,#endif和#define),则必须将它们隔离在资源编译器编译的只读资源中。组件
.RC文件在 Visual C++ 中加载和保存的速度比一个复合.RC文件更快。如果要用文本编辑器以人工可读形式维护资源,应让该资源保存在一个文件中,并与 Visual C++ 编辑的文件分开。
如果需要将用户定义的资源保存在可由另一个专用数据编辑器解释的二进制或文本窗体中,则应将其保存在单独的
.RC文件中,以便 Visual C++不会将格式更改为十六进制数据。.WAVMFC 高级概念示例 SPEAKN 中的 (sound) 文件资源是一个很好的示例。
还可以在“设置包括”对话框的编译时指令中包括 SECOND.RC:
#include "res\myapp.rc2" // non-Visual C++ edited resources
#include "second.rc" // THE SECOND .RC FILE
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
下图演示了结果:
RESOURCE.H AFXRES.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
SECOND.RC
AFXRES.RC
AFXPRINT.RC
使用编译时指令时,可以将 Visual C++ 可编辑和不可编辑的资源组织到多个 .RC 文件中,其中,主 MYAPP.RC 不执行任何操作,但会通过 #include 指令包括其他 .RC 文件。 如果使用 Visual Studio C++项目 .MAK 文件,则应在项目中包括主 .RC 文件,以便所有包含的资源都与应用程序一起编译。
强制执行不可编辑的 Visual C++ 文件
以 AppWizard 创建的 RES\MYAPP.RC2 文件为例,你不希望其中资源被意外读取到 Visual C++、并在随后重新写出时丢失格式信息。 若要防止此问题,请将以下行放在文件的开头 RES\MYAPP.RC2 :
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Visual C++
#endif //APSTUDIO_INVOKED
当 Visual C++编译 .RC 该文件时,它将同时定义 APSTUDIO_INVOKED 和 RC_INVOKED. 如果由 AppWizard 创建的文件结构已损坏,且 Visual C++ 读取了上面的 #error 行,则会报告致命错误并中止读取 .RC 文件。
管理 Visual C++ 编辑的多个 .RC 文件所共享的符号
将资源拆分为要在 Visual C++ 中单独编辑的多个 .RC 文件时,会出现两个问题:
你可能希望跨多个
.RC文件共享相同的符号。你需要帮助 Visual C++避免将相同的 ID 数值分配给不同的资源(符号)。
下图展示了< c0 />和< c1 />文件的结构,以解决第一个问题。
MYAPP.RC
/ \
/ \
MYSTRS.H / MYSHARED.H \ MYMENUS.H
\ / / \ \ \
\ / / \ \ \
MYSTRS.RC MYMENUS.RC
在此示例中,字符串资源保存在一个资源文件中, MYSTRS.RC菜单保存在另一个 MYMENUS.RC资源文件中。 某些符号(如命令)可能需要在两个文件之间共享。 例如,可能是 ID_TOOLS_SPELL “工具”菜单中“拼写”项的菜单命令 ID;也可能是框架在应用程序主窗口状态栏中显示的命令提示符的字符串 ID。
符号 ID_TOOLS_SPELL 保存在共享头文件中 MYSHARED.H。 使用文本编辑器手动维护此共享头文件;视觉C++不会直接编辑它。 在 MYSTRS.RC 和 MYMENUS.RC 这两个资源文件中,可使用“资源包括”命令在 #include "MYSHARED.H" 的只读指令中指定 MYAPP.RC(如前所述)。
在尝试使用它来标识任何资源之前,预测要共享的符号是最方便的。 将符号添加到共享头文件中,如果尚未在 .RC 文件的 Read-Only 指令中包含共享头文件,请在使用该符号之前添加。 如果您没有预料到要以这种方式共享符号,那么在使用它于 MYMENUS.H 之前,您必须手动(使用文本编辑器)将符号的 #define 语句从 MYSHARED.H 移动到 MYSTRS.RC。
在多个 .RC 文件中管理符号时,还必须帮助 Visual C++避免将相同的 ID 数值分配给不同的资源(符号)。 对于任何给定 .RC 的文件,Visual C++增量分配四个 ID 域中的每一个 ID。 在编辑会话之间,Visual C++ 将跟踪它在 .RC 文件的符号头文件的每个域中分配的最后一个 ID。 下面是空(新)APS_NEXT文件中的.RC值:
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
_APS_NEXT_RESOURCE_VALUE 是用于对话框资源、菜单资源等的下一个符号值。 资源符号值的有效范围为 1 到 0x6FFF。
_APS_NEXT_COMMAND_VALUE 是将用于命令标识的下一个符号值。 命令符号值的有效范围为0x8000到0xDFFF。
_APS_NEXT_CONTROL_VALUE 是将用于对话控件的下一个符号值。 对话控件符号值的有效范围为 8 到 0xDFFF。
_APS_NEXT_SYMED_VALUE 是使用符号浏览器中的“新建”命令手动分配符号值时发出的下一个符号值。
Visual C++ 从略高于最低法定值的数值开始创建新文件 .RC。 AppWizard 还将将这些值初始化为更适合 MFC 应用程序的内容。 有关 ID 值范围的详细信息,请参阅 技术说明 20。
现在,每次创建新资源文件(即使在同一项目中)Visual C++都定义相同的 _APS_NEXT_ 值。 这意味着,如果在两个不同的 .RC 文件中添加多个对话,很可能将相同的 #define 值分配给不同的对话。 例如,IDD_MY_DLG1在第一个.RC文件中,可能会被分配与第二个IDD_MY_DLG2文件中的.RC相同的数字101。
若要避免此问题,应为相应 .RC 文件中四个 ID 域中的每个域保留一个单独的数值范围。 通过在开始添加资源_APS_NEXT手动更新.RC每个文件中的值来设置范围。 例如,如果第一个 .RC 文件使用默认值 _APS_NEXT ,则可能需要将以下 _APS_NEXT 值分配给第二 .RC 个文件:
#define _APS_NEXT_RESOURCE_VALUE 2000
#define _APS_NEXT_COMMAND_VALUE 42000
#define _APS_NEXT_CONTROL_VALUE 2000
#define _APS_NEXT_SYMED_VALUE 2000
当然,Visual C++ 仍然有可能在第一个 .RC 文件中分配如此多的 ID,以至于数值开始与为第二个 .RC 文件保留的 ID 发生重叠。 你应保留足够大的范围,避免发生此冲突。
在 .RC、.CPP 和 .H 文件之间管理依赖关系
当 Visual C++保存 .RC 文件时,它还会将符号更改保存到相应的 RESOURCE.H 文件。 引用 .CPP 文件中资源的任何 .RC 文件都必须使用 #include 来包括 RESOURCE.H 文件,此文件通常位于项目的主头文件中。 这将导致意外的副作用,因为开发环境的内部项目管理将浏览源文件的头依赖关系。 每次在 Visual C++ 中添加新符号时,都需要重新编译具有.CPP指令的所有#include "RESOURCE.H"文件。
Visual C++ 通过在文件RESOURCE.H的第一行包含以下注释来避免对RESOURCE.H的依赖:
//{{NO_DEPENDENCIES}}
开发环境通过忽略更改 RESOURCE.H 来解释此注释,以便不需要重新编译依赖 .CPP 文件。
视觉对象C++保存文件时始终将 //{{NO_DEPENDENCIES}} 注释行添加到 .RC 文件中。 某些情况下,避开对 RESOURCE.H 的生成依赖可能会导致在链接时检测不到运行时错误。 例如,如果使用符号浏览器更改分配给资源符号的数值,而引用该资源的文件没有重新编译,则在应用程序运行时将无法正确找到并加载资源。 在这种情况下,您应该显式重新编译您知道受 .CPP 中符号更改影响的任何 RESOURCE.H 文件,或选择“ 全部重新生成”。 如果需要频繁更改特定组资源的符号值,可能会发现将这些符号分解为单独的只读头文件更加方便和更安全,如上述部分中所述, 包括其他头文件。
Visual C++ 如何管理“设置包括”信息
如上所述,“文件”菜单“包括”命令允许你指定三种类型的信息:
符号头文件
Read-Only 符号指令
编译时指令
下表描述了 Visual C++ 如何在 .RC 文件中维护此信息。 无需此信息即可使用 Visual C++,但它可能会增强理解,以便可以更自信地使用“设置包括”功能。
上述三种类型的 Set Includes 信息都以两种形式存储在 .RC 文件中:(1)作为 #include 或资源编译器可解释的其他指令,(2)作为仅由 Visual C++ 解释的特殊 TEXTINCLUDE 资源。
TEXTINCLUDE 资源的目的是以易于演示的格式,将“设置包括”信息安全存储在 Visual C++ 的“设置包括”对话框中。 TEXTINCLUDE 是由 Visual C++ 定义的 资源类型 。 Visual C++ 识别三个特定的 TEXTINCLUDE 资源,其资源标识号为 1、2 和 3。
TEXTINCLUDE 资源 ID |
设置包括信息的类型 |
|---|---|
| 1 | 符号头文件 |
| 2 | Read-Only 符号指令 |
| 3 | 编译时指令 |
所有这三种类型的设置包括信息均由 AppWizard 创建的默认 MYAPP.RC 和 RESOURCE.H 文件阐释(如下所述)。 RC 语法需要在\0和""块之间添加额外的BEGIN和END标记,以分别指定零终止字符串和双引号字符。
符号头文件
资源编译器解释的符号头文件信息的形式只是一条 #include 语句:
#include "resource.h"
相应的 TEXTINCLUDE 资源为:
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
Read-Only 符号指令
Read-Only 符号指令以资源编译器可解释的形式包括在以下 MYAPP.RC 的顶部:
#include "afxres.h"
相应的 TEXTINCLUDE 资源为:
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
编译时指令
Compile-Time 指令包含在资源编译器可解释的以下形式中,位于MYAPP.RC 的末尾:
#ifndef APSTUDIO_INVOKED
///////////////////////
//
// From TEXTINCLUDE 3
//
#include "res\myapp.rc2" // non-Visual C++ edited resources
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
#endif // not APSTUDIO_INVOKED
该 #ifndef APSTUDIO_INVOKED 指令指示 Visual C++跳过 Compile-Time 指令。
相应的 TEXTINCLUDE 资源为:
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""res\myapp.rc2"" // non-Visual C++ edited resources\r\n"
"\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#include ""afxprint.rc"" // printing/print preview resources\r\n"
"\0"
END