在 WinAPI 中使用资源名称

Using resource names in WinAPI

在WinAPI中,您可以通过FindResource and LoadResource访问资源。

根据the documentation for FindResource,可以指定资源名称:

lpName [in]

Type: LPCTSTR

The name of the resource. Alternately, rather than a pointer, this parameter can be MAKEINTRESOURCE(ID), where ID is the integer identifier of the resource. For more information, see the Remarks section below.

我有两个问题:

首先,这似乎并不准确,因为指定 ID 或文件名都不起作用。 lpName 参数的正确输入值是多少?
This other question seemed to have this issue as well

其次,我想知道是否可以在 运行 时检索资源的文件名。这可能吗?还是打包成资源后文件名被丢弃?

测试代码

#include <Windows.h>
#include <tchar.h>
#include "resource.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    //This is the only test that succeeds.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_DRAWING1), _T("BINARY")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_DRAWING1); BINARY"), _T(""), MB_ICONERROR);
    }

    //This one fails.
    if (!FindResource(hInstance, _T("IDR_DRAWING1"), _T("BINARY")))
    {
        MessageBox(NULL, _T("\"IDR_DRAWING1\"; BINARY"), _T(""), MB_ICONERROR);
    }

    //ICON - Each fails.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDI_ICON1), _T("ICON")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDI_ICON1); ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDI_ICON1"), _T("ICON")))
    {
        MessageBox(NULL, _T("\"IDI_ICON1\"; ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDI_ICON1), RT_ICON))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDI_ICON1); RT_ICON"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDI_ICON1"), RT_ICON))
    {
        MessageBox(NULL, _T("\"IDI_ICON1\"; RT_ICON"), _T(""), MB_ICONERROR);
    }

    //HTML - Each fails.
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_HTML1), _T("HTML")))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_HTML1); HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDR_HTML1"), _T("HTML")))
    {
        MessageBox(NULL, _T("\"IDR_HTML1\"; HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, MAKEINTRESOURCE(IDR_HTML1), RT_HTML))
    {
        MessageBox(NULL, _T("MAKEINTRESOURCE(IDR_HTML1); RT_HTML"), _T(""), MB_ICONERROR);
    }
    if (!FindResource(hInstance, _T("IDR_HTML1"), RT_HTML))
    {
        MessageBox(NULL, _T("\"IDR_HTML1\"; RT_HTML"), _T(""), MB_ICONERROR);
    }
    return 0;
}

Resource.rc
这不是整个文件,因为它包含大量样板代码。下面是相关的资源声明。

IDR_DRAWING1            BINARY                  "Drawing1.dwg"
IDI_ICON1               ICON                    "icon1.ico"
IDR_HTML1               HTML                    "html1.htm"

.ico 和 .htm 是使用 Visual Studio 自动创建的;通过添加相应类型的新资源。所以他们的格式不应该搞乱 FindResource 声明。

resource.h

#define IDR_DRAWING1                    101
#define IDI_ICON1                       102
#define IDR_HTML1                       103

EDIT:

Per Ben Voigt's comment, I've gone ahead and changed the Resource.rc file so non-numeric names are used:

DWG1                    BINARY                  "Drawing1.dwg"
ICON1                   ICON                    "icon1.ico"
HTML1                   HTML                    "html1.htm"

Now, the resource.h file isn't used at all. Following are the new relevant tests:

FindResource(hInstance, _T("DWG1"), _T("BINARY")); //Succeeds now.
FindResource(hInstance, _T("ICON1"), _T("ICON")); //Still fails.
FindResource(hInstance, _T("ICON1"), RT_ICON); //Still fails.
FindResource(hInstance, _T("HTML1"), _T("HTML")); //Still fails.
FindResource(hInstance, _T("HTML1"), RT_HTML); //Still fails.

So, my expectations are met for my binary resource, but what is happening with the ICON and HTML?

is the file name discarded once the file is packaged as a resource?

是的,完全正确。

例如ICON statement第一列是资源名,第三列是文件名..

nameID ICON filename

有些资源根本不是来自外部文件,例如 MENU statement 直接从资源脚本中嵌套的 MENUMENUITEM 语句获取数据。 DIALOGSTRINGTABLEVERSIONVERSIONINFO 只是一些不作为单独文件存在的其他公共资源。

解决方案和结果:

好的,所以我终于得到了可以预见的结果。

您查找资源的方式部分取决于资源类型。在这种情况下,DWG1 是一个 BINARY 资源,它是一种自定义类型。 resource types 中不存在预定义的 RT_*,因此您需要将类型指定为 LPCTSTR:

FindResource(hInstance, _T("DWG1"), _T("BINARY"));

对于预定义的资源类型,您不能将类型名称指定为 LPCTSTR。相反,您必须使用相应的 RT_* 值。这可能是因为每个 RT_* 值对应一个 MAKEINTRESOURCE(WORD),如果我的理解是正确的,该宏结果指向 portable executable file 中的无效地址,而不是资源类型。

This questions addresses MAKEINTRESOURCE

ICON1 资源的问题是类型应该是 RT_GROUP_ICON 而不是 RT_ICON。这两种类型的区别在于前者是hardware-independent,后者是hardware-dependent。虽然,我不知道为什么 RT_ICON 不起作用。

最后,HTML1 的问题是我的错,因为我没有确保引用的文件确实有数据。在构建期间,它可能被省略,因为它本质上是一个空资源。用于此资源的正确类型是 RT_HTML.

现在,关于名字。正如 Ben Voigt 在对 his answer 的评论中提到的,需要 non-numeric 名称才能将名称指定为 LPCTSTR。如果使用数字名称,则必须使用 MAKEINTRESOURCE 代替。

Visual Studio 的资源编辑器使得用字符串而不是数字命名资源有点麻烦,因为默认情况下它会为每个资源创建宏。这些宏然后在预处理阶段用数字替换资源名称。

为了将名称更改为字符串,您有 2 个选项:

  • 用双引号将资源(在属性中找到)的 ID 括起来。这可以防止资源编辑器为该名称创建宏。但是,如果将引号放在先前设置的 ID.
  • 周围,则宏可能已经存在

  • 或者,在任何文本编辑器中打开资源脚本文件并选择这样的名称。同样,这个想法是为了防止生成宏。因此,分离资源脚本和资源 header 是可行的,或者只需确保宏不存在于 header.