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?
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"
If I open up the compiled binary in a hex editor, I can see my string:
Cool, but how did it get here?
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...
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.
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
.
The embedded resources are stored inside the DLL or EXE files.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With