.NET 应用程序中的嵌入式资源存储在哪里?

发布于 2024-11-01 20:46:06 字数 173 浏览 2 评论 0原文

如果我将资源添加到 Resources.resx,它们是否嵌入到可执行文件中?

我在某处读到资源存储在程序集中,但是项目中的所有文件(包括输出文件夹中的文件)都是程序集的一部分吗?

或者,程序集是否只是编译器生成的文件和模块,程序集中不包含资源?

有人可以确切地说明这是如何工作的吗?

If I add resources to Resources.resx, are they are embedded within the executable?

I have read somewhere that resources are stored within the assembly, but are all files in the project (including files in the output folder) part of the assembly?

Or, is the assembly just files and modules produced by compiler and resources aren't included in the assembly?

Can somebody clarify exactly how this works?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蒲公英的约定 2024-11-08 20:46:06

快速回答

您添加到 C# 程序的资源存储在 PE 的资源部分中
二进制。

例如,如果我添加一个字符串 MyTestString,其值为 "Charles is Cool"

添加了字符串的资源编辑器的屏幕截图

如果我在十六进制编辑器中打开编译后的二进制文件,我可以查看我的字符串:

“来自十六进制编辑器的屏幕截图"

很酷,但是它是怎么到这里的呢?

C# 端

资源编辑器生成 Resources.Designer.cs,这是它​​生成的内容

/// <summary>
///   Looks up a localized string similar to Charles is Cool.
/// </summary>
internal static string MyTestString {
    get {
        return ResourceManager.GetString("MyTestString", resourceCulture);
    }
}

但是等等...我在这里没有看到 “Charles is Cool”...它去了哪里? GetString() 从哪里获取它?

好吧,最终它对资源卑躬屈膝(MSFT的术语,而不是我的),我想我们应该将其视为 ManifestBasedResourceGroveler,它最终会让我们extern GetResource(),肯定的这表明我们已经脱离了 .NET 宇宙的边缘...您知道这意味着...

CoreCLR 方面

我们可以查看 coreclr 存储库以获取如何实现它的示例,

如果您要跟踪在这里编写代码你最终会到达 PEDecoder::GetResource()

const void *PEDecoder::GetResource(COUNT_T offset, COUNT_T *pSize) const
{
    CONTRACT(const void *)
    {
        INSTANCE_CHECK;
        PRECONDITION(CheckCorHeader());
        PRECONDITION(CheckPointer(pSize, NULL_OK));
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACT_END;

    IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;

    // 403571: Prefix complained correctly about need to always perform rva check
    if (CheckResource(offset) == FALSE)
        return NULL;

    void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset);
    // Holds if CheckResource(offset) == TRUE
    PREFIX_ASSUME(resourceBlob != NULL);

     if (pSize != NULL)
        *pSize = GET_UNALIGNED_VAL32(resourceBlob);

    RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD));
}

基本上找到了 PE 头的偏移量(参见 IMAGE_COR20_HEADER) 。查看 Wikipedia 上的可移植可执行文件,您将看到 svg 图像有一个 ResourceTable 部分(RVA,又名 相对虚拟地址

我在这里得到的是,资源不是'无论如何,它们不是 C# 或 .NET 功能,它们只是 PE 二进制格式始终具有的托管 API。

对于任意二进制资源

请注意,我所说的所有内容都适用于任意数据,如果您要向资源中添加一些二进制文件,Resources 会将其作为 byte[] 呈现给您> 但实际上它会将其编码为其他东西。

虽然在 Visual Studio 中打开 resx 文件会为您提供导入的二进制文件的相当无趣的路径,但请注意,如果您以 JetBrains DotPeek 并查看 resx 文件,该文件的路径已被替换为字符串,

<data name="binary" type="System.Byte[], mscorlib">
    <value>CGdTCb7vyv7AD/6rze8=</value>
</data>

而我无法在 Visual Studio 端找到任何代码来证明它,我会猜测该字符串是(二进制)资源文件中字节的 Base64 编码版本,因为运行 Convert.ToBase64String(Resources.binary) 给出的字符串与编译的二进制文件的 resx 文件中找到的字符串相同。对于那些想在家尝试的人,我的二进制文件仅包含 08 67 53 09 BE EF CA FE C0 0F FE AB CD EF

The quick answer

The resources you add to a C# program are stored in the resources section of the PE
binary.

For example, if I add a string, MyTestString, with the value "Charles is Cool"

A screenshot of the resource editor with the string added to it

If I open up the compiled binary in a hex editor, I can see my string:

A screenshot from a hex editor

Cool, but how did it get here?

The C# side

The resource editor generates Resources.Designer.cs, here's what it generated

/// <summary>
///   Looks up a localized string similar to Charles is Cool.
/// </summary>
internal static string MyTestString {
    get {
        return ResourceManager.GetString("MyTestString", resourceCulture);
    }
}

But wait... I don't see "Charles is Cool" anywhere in here... where did it go? Where is GetString() getting it from?

Well eventually it grovels for the resource (MSFT's terminology, not mine), I suppose we should consider this a ManifestBasedResourceGroveler, which eventually gets us to extern GetResource(), a sure sign that we've fallen off the edge of the .NET universe... you know that that means...

The CoreCLR Side

We can look at the coreclr repository for an example of how this is implemented,

If you were to trace the code here you eventually come to PEDecoder::GetResource()

const void *PEDecoder::GetResource(COUNT_T offset, COUNT_T *pSize) const
{
    CONTRACT(const void *)
    {
        INSTANCE_CHECK;
        PRECONDITION(CheckCorHeader());
        PRECONDITION(CheckPointer(pSize, NULL_OK));
        NOTHROW;
        GC_NOTRIGGER;
    }
    CONTRACT_END;

    IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;

    // 403571: Prefix complained correctly about need to always perform rva check
    if (CheckResource(offset) == FALSE)
        return NULL;

    void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset);
    // Holds if CheckResource(offset) == TRUE
    PREFIX_ASSUME(resourceBlob != NULL);

     if (pSize != NULL)
        *pSize = GET_UNALIGNED_VAL32(resourceBlob);

    RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD));
}

Which basically finds the offset from the PE header (see IMAGE_COR20_HEADER). Check out the Portable Executable article on Wikipedia, you will see that the svg image has a section for ResourceTable (RVA, aka Relative Virtual Address)

What I'm getting at here is, Resources aren't by any means a C# or .NET feature, they're just a managed API into something the PE binary format always had.

For arbitrary binary resources

Note that everything I said works for arbitrary data, if you were to add some binary file to your resources, Resources would present it to you as a byte[] but really it would encode it as something else.

While opening up the resx file in Visual Studio gives you a rather uninteresting path to the binary file that you imported, note that if you open up the compiled binary in something like JetBrains DotPeek and look at the resx file, the path to the file has been replaced with a string,

<data name="binary" type="System.Byte[], mscorlib">
    <value>CGdTCb7vyv7AD/6rze8=</value>
</data>

While I was not able to find any code on the Visual Studio side to prove it, I would guess this string is a base64 encoded version of the bytes in the (binary) resource file because running Convert.ToBase64String(Resources.binary) gives me the same string that's found in the compiled binary's resx file. For those of you who want to try this at home my binary file just contained 08 67 53 09 BE EF CA FE C0 0F FE AB CD EF.

各空 2024-11-08 20:46:06

嵌入式资源存储在 DLL 或 EXE 文件内。

The embedded resources are stored inside the DLL or EXE files.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文