Search code examples
.netresourcesc++-climanifestresx

Why can't I load an image resource from my resx file?


I have a controls library which I've added a .resx file to (ImageResources.resx). It contains two .png images which I subsequently added.

In that same library I have a control which loads a couple of images to do some custom drawing, but I don't seem to be able to load the resources:

void GTableLayoutPanel::SetBorderImagesFromManifest(String^ topLeftCornerImageName, String^ topImageName)
{
    // Grab the assembly this is being called from
    Assembly^ assembly = Assembly::GetExecutingAssembly();

    // Grab the images from the assembly
    Stream^ stream = assembly->GetManifestResourceStream(topLeftCornerImageName);
    Image^ topLeftImage = System::Drawing::Image::FromStream(stream);
    stream = assembly->GetManifestResourceStream(topImageName);
    Image^ topImage = System::Drawing::Image::FromStream(stream);

    // Update the internal store from the supplied images
    SetBorderImages(topLeftImage, topImage);
}

...gives me errors complaining that stream is null which suggests my call to GetManifestResourceStream is failing.

The images are called group_box_top.png and group_box_top_left.png and I'm calling the image loader as follows:

SetBorderImagesFromManifest("group_box_top_left.png", "group_box_top.png");

I've also tried:

SetBorderImagesFromManifest("group_box_top_left", "group_box_top");

...because the files appear in the .resx file without the .png extensions, but this gives the same error.

Have I missed a step here?

[Edit] I tried the suggestion in that final link and I get:

MyControlsLib.ImageResources.resources

So now I've tried referencing:

Stream^ strm1 = assembly->GetManifestResourceStream("MyControlsLib.ImageResources.resources.group_box_top_left");
Stream^ strm2 = assembly->GetManifestResourceStream("MyControlsLib.ImageResources.resources.group_box_top_left.png");
Stream^ strm3 = assembly->GetManifestResourceStream("MyControlsLib.ImageResources.group_box_top_left");
Stream^ strm4 = assembly->GetManifestResourceStream("MyControlsLib.ImageResources.group_box_top_left.png");

...all of which return nullptr :-/


Solution

  • I finally got the magic combination for a C++/CLI solution. So just in case anyone else has this issue:

    Method 1 (via .resx file)

    1. Add the image files (I used .pngs, but bitmaps etc. work too).
    2. Add a resource file:
      1. Go to Solution Explorer.
      2. Right-click on Resource Files in the project you're adding to
      3. Select Add > New Item..
      4. Choose Visual C++ > Resource > Assembly Resource File (.resx). I nameed mine "ImageResources.resx"
    3. Add the images:
      1. Double click on "ImageResources.resx" in Solution Explorer
      2. Click on the Add Resource button and select Add Existing File...
      3. Select the images you want to embed. I added group_box_top.png and group_box_top_left.png which appear in the .resx file as group_box_top and group_box_top_left.

    You can then grab the images from the manifest with:

    // Grab the assembly this is being called from
    Assembly^ assembly = Assembly::GetExecutingAssembly();
    AssemblyName^ assemblyName = assembly->GetName();
    
    // Grab the images from the assembly
    ResourceManager^ rm = gcnew ResourceManager(assemblyName->Name+".ImageResources", assembly);
    Bitmap^ topLeftImage = (Bitmap^)rm->GetObject("group_box_top_left");
    Bitmap^ topImage = (Bitmap^)rm->GetObject("group_box_top");
    

    Note that the ImageResources string passed into the ResourceManager constructor must match the name of your .resx file.

    Method 2 (via linker)

    1. Add the files:
      1. Right-click on your project
      2. Go to Linker -> Input
      3. Add the files you want to embed to the Embed Managed Resource File property. I added PingSend.wav here.

    To get access to the files simply do:

    System::Reflection::Assembly^ assembly = Assembly::GetExecutingAssembly();
    System::Media::Sonudplayer^ pingPlayer = gcnew System::Media::SoundPlayer(assembly->GetManifestResourceStream("PingSend.wav"));
    

    ..which in this case, loads the audio file ready to play back.