You can create an extension method on the ResourceManager
.
public static class ResourceExtensions { public static MemoryStream GetMemoryStream(this ResourceManager resourceManager, String name) { object resource = resourceManager.GetObject(name); if (resource is byte[]) { return new MemoryStream((byte[])resource); } else { throw new System.InvalidCastException("The specified resource is not a binary resource."); } } }
Call
ResourceManager resourceManager = Properties.Resources.ResourceManager; MemoryStream stream = resourceManager.GetMemoryStream("binaryResource");
Although it seems that this is also good.
MemoryStream stream = new MemoryStream(Properties.Resources.SomeBinaryResource);
I am not sure that I would modify the resource file, since they are fragile to change, and I am sure that there are scripts in which Visual Studio will overwrite the changes.
The problem with this, in your opinion, is that it creates a copy of the data in memory, creating an amount of memory. For light resources that are short-lived, this is not a problem, but it can be a big problem.
The answer is short: you cannot escape the memory trace with the ResourceManager
. The problem is that both ResourceManager.GetObject(String)
and ResourceManager.GetStream(String)
create a copy of the data. Even if GetStream(String)
returns UnmanagedMemoryStream
, it actually calls GetObject(String)
calls internally, and a copy is still created. If you are debugging an application and viewing it, you will see that the memory is still allocated.
I tried several ways around this using pointers in the unsafe
context, and reflection and nothing worked. ResourceManager
simply not flexible or optimized.
However, I managed to find a solution, but for this I need to use Embedded Resources
. This does not change anything, except that you set the build action of your resource files to the Embedded Resource
for Build Action
. Using this, you can use reflection to create an UnmanagedMemoryStream
that does not create a copy of the data.
private UnmanagedMemoryStream GetUnmanagedMemoryStream(String embeddedResourceName) { Assembly assembly = Assembly.GetExecutingAssembly(); string[] resourceNames = assembly.GetManifestResourceNames(); string resourceName = resourceNames.SingleOrDefault(resource => resource.EndsWith(embeddedResourceName, StringComparison.InvariantCultureIgnoreCase)); if (resourceName != null) { return (UnmanagedMemoryStream)assembly.GetManifestResourceStream(resourceName); } else { throw new System.ArgumentException("The specified embedded resource could not be found.", "embeddedResourceName"); } }
I have not tested this extensively, but it really works. My test data was a small 17 megabyte file. The working set memory of my test application starts at a speed of about 50 megabytes, and after retrieving the resource into the stream does not change. When using the ResourceManager
it will immediately increase the working set by the size of the resource.
You probably need to change the call to EndsWith
, which checks the correct resource name in the manifest, because the resource names are slightly different from accessing it directly through the ResourceManager
.
I am really disappointed that I could not find a solution using the existing ResourceManager
, but it is not flexible enough.
Edit I wrote a detailed blog article about this subject .