简单回答
您添加到C#程序中的资源存储在PE二进制文件的资源部分中。
例如,如果我添加一个字符串MyTestString
,其值为"Charles is Cool"
![A screenshot of the resource editor with the string added to it](https://istack.dev59.com/MOnRo.webp)
如果我在十六进制编辑器中打开已编译的二进制文件,我就可以看到我的字符串:
![A screenshot from a hex editor](https://istack.dev59.com/7js0J.webp)
很酷,但它是如何到达这里的?
C#方面
资源编辑器生成Resources.Designer.cs,以下是它生成的内容。
internal static string MyTestString {
get {
return ResourceManager.GetString("MyTestString", resourceCulture);
}
}
但是等等...我在这里没有看到"Charles is Cool"...它去哪了?
GetString()
从哪里获取它?
嗯,最终它会
搜索资源(MSFT的术语,不是我的),我想我们应该考虑这是一个
ManifestBasedResourceGroveler
,最终带我们到
extern GetResource()
,这是一个明确的迹象,表明我们已经跌出了.NET宇宙...你知道那意味着什么...
核心CLR方面
我们可以查看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;
if (CheckResource(offset) == FALSE)
return NULL;
void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset);
PREFIX_ASSUME(resourceBlob != NULL);
if (pSize != NULL)
*pSize = GET_UNALIGNED_VAL32(resourceBlob);
RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD));
}
这基本上是从PE头部找到的偏移量(参见IMAGE_COR20_HEADER
)。请查看维基百科上的可移植可执行文件文章,您会发现svg图像有一个ResourceTable部分(RVA,又称相对虚拟地址)
我的意思是,资源绝不是C#或.NET功能,它们只是一种管理API,可以进入PE二进制格式始终具有的内容。
对于任意二进制资源
请注意,我所说的一切都适用于任意数据,如果您要将某些二进制文件添加到资源中,则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
。