当前位置:C++技术网 > 精选软件 > RC资源文件使用分析:RC资源定义中不存在资源冲突

RC资源文件使用分析:RC资源定义中不存在资源冲突

更新时间:2019-01-28 14:23:57浏览次数:1+次

    对于windows的资源文件RC的使用,这里需要总结下。

   

    原先在MFC中,发现有一个宏,是IDR_MAINFRAME,它在Resource.h中是这么定义的:

#define IDR_MAINFRAME 128

    也就是定义了一个数字而已。我们使用这个宏时就相当使用一个常量数字128.当然,这个数字可能是其他的。

    然后在MFC中,有好几个地方都使用了这个宏,比如加载图标,比如加载字符串。我感觉很奇怪,这个一个资源ID怎么既用成图标的资源ID,又可以用成字符串的资源ID呢?还可以是更多的类型的资源ID。
    这是我以前的一个疑惑,不过一直都没有真正去了解。在了解资源冲突的时候,问了同事,他跟我讲了这个知识点,我才明白。惭愧惭愧。我还以为这个也算是资源冲突。
    一直的错误认识“如果两个资源宏定义的ID值重复了就是一种资源冲突”,然而,现在我知道了,这个想法是错误的!!哎,被蒙了这么久。那么下面我来好好将我学到的分享出来。很感谢那位大神同事!
    只要是宏,就是一个代替性的符号,如果我们将它定义为数字,那么这个宏就是一个常量数字,仅此而已。所以,前面看到的IDR_MAINFRAME就等同于128这个常量数字,没有其他任何含义了。
    在Resource.h头文件中,只是定义了一些资源中用到的宏的定义。实际上这些宏都是给一个资源ID定义一个好记的名字罢了。而这个文件中,全部存放的资源相关的宏定义。同时我们建议不要在这个头文件里放与资源无关的宏定义,在其他头文件又不是没有地方,不要来挤资源头文件咯。那么Resource.h头文件我们就清楚是干什么的了。其实Resource.h头文件也不是一定需要的,只是为了方便管理,才集中将这些宏放在一起,如果你只有一个资源宏定义,直接在cpp文件中定义就可以了。放在这个头文件里是为了好管理罢了。
    那么资源实际的定义文件就是rc文件了。在rc文件中,就和cpp文件中,写了各种代码,只不过,rc文件中的代码是资源编译器的脚本代码。它的语法和c/c++的不一样。
    在RC文件中,使用的是资源脚本语言语法,资源在这里定义。写好后,在编译时会调用资源编译器将描述的资源编译成二进制的资源,最后链接到exe文件中。在RC文件中,资源的描述定义是以节为单位的,也就是,资源是分为一组一组的。比如下面的两组对话框资源定义代码:

IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "关于 MFCApplication2"
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    ICON            IDR_MAINFRAME,IDC_STATIC,14,14,21,20
    LTEXT           "MFCApplication2,1.0 版",IDC_STATIC,42,14,114,8,SS_NOPREFIX
    LTEXT           "Copyright (C) 2015",IDC_STATIC,42,26,114,8
    DEFPUSHBUTTON   "确定",IDOK,113,41,50,14,WS_GROUP
END

IDD_MFCAPPLICATION2_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "MFCApplication2"
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,209,179,50,14
    PUSHBUTTON      "取消",IDCANCEL,263,179,50,14
    CTEXT           "TODO: 在此放置对话框控件。",IDC_STATIC,10,96,300,8
END

     这里定义了两个对话框,也就是两组,或者说两节代码段,每一组都是定义一个对话框,如果你熟悉RC脚本,你可以直接在RC文件里编辑保存,就可以修改窗口的界面了。如果你不熟,就在界面上操作,然后保存时VS会自动修改RC文件。

    这里不详细介绍RC的语法,只是大概介绍一下。在第一行的第二个,也就是DIALOGEX,这个表明了这个资源的类型是对话框,然后后面的一些都是描述这个对话框的样子,然后BEGIN到END之间,描述的是对话框里面有哪些控件,位于何处,控件文字等等信息。这样一组就是一个资源的全部描述。
    我们在看看字符串资源类型定义:
STRINGTABLE
BEGIN
    IDR_MAINFRAME  "主窗口"
END

     STRINGTABLE表示了资源类型是字符串,然后BEGIN和END之间包含的是这一组的字符串资源。你可以看到,这里的一个宏和字符串一一对应。那么通过这个宏即可得到这个字符串。而这个宏,也就是一个数字,也就是我们通过一个资源ID数字就可以获取这个字符串资源。

    我们在来看看图标资源类型定义:
IDR_MAINFRAME ICON  "res\\MFCApplication2.ico"

     更是简单粗暴,中间的ICON表示了资源类型是Icon资源,后面的是图标的路径。可以通过这个宏得到。

    然而问题是,这一个宏怎么在两个地方都有,那通过这个宏到底访问的是哪个资源呢?这个是不是就冲突了呢?
    我一开始以为这个是资源冲突的一种,后来请教同事后,然后想想,我们MFC程序里不经常这样的吗,怎么可能是冲突呢?说明我确实错了。
    对于资源的访问,我们都是要调用一个API函数才行的,不可能自己去手抓哦。而不同的会根据自己需要的类型,在资源文件中找对应的资源类型的那一组,然后根据ID找到这一组中对应的那个资源即可。
    也就是说,资源ID,并不是资源文件中全局的,而是在资源节或者说资源的一组定义的范围内的。只要在这一组范围内,ID不重复,就没有问题。当然,我们可以反过来想一想,如果一个宏(资源ID)对应两个字符串,试想,你能分辨出这个宏到底是和谁对应吗?
    这是定义的二义性错误,这是语法错误,这个谈不上是资源冲突哦。所以也论证了,这种情况谈不上是资源冲突。但是一个ID却可以对应多种类型的资源,比如字符串和图标。一开始你也可能认为这个是资源冲突了吧,但是API函数加载的时候的工作模式并不会遍历所有的类型来找这个资源ID的,而是只找他要的类型的那个ID。比如LoadIcon,自然只找ICON类型的资源,其他类型资源压根就不看,怎么可能冲突。LoadString则是只找STRINGTABLE类型的资源。
    总结一下,资源ID,按照资源类型分组,在这个类型下,是唯一的。不管你相同的类型写了多少组定义,始终不能重复ID,比如下面代码的就重复了,因为他们是同一种类型。
STRINGTABLE
BEGIN
    IDS_ABOUTBOX    "关于 MFCApplication2(&A)..."
    IDR_MAINFRAME   "22213"
END

STRINGTABLE
BEGIN
    IDR_MAINFRAME    "213"
END


     而资源冲突只会出现在MFC的多个Dll的资源使用的情况,我将会在后面的文章讲解。