Embedding user defined resources in Visual C++ binaries

For my current project I wanted the ability to package my entire C++ project (dependent DLLs and data files) as a single Windows executable. To do this I had to find a way to embed the data files into the executable at build time. There’s multiple ways to do this. I settled on using the Windows resource system, since the project is (currently) Windows only.

Add external files

  1. Add text files named “Sample.txt” and “Sample2.txt” to the project with some random text.

Add a new resource file and header

  1. Add a new resource file (.rc) to your project. This example will use ExternalResources.rc.

  2. Add a new header file (.h) to your project with the same name but a .h extension.

  3. Open up the header file and create one entry for each external file you want. Each ID must be unique – it’s a good idea to start around 1000 and go up from there. I use C_ for the definition, but it’s totally optionally. Name your resource however you want!

#define C_ASSET_NAME_HERE_1 1001
#define C_ASSET_NAME_HERE_2 1002
  1. Open up the resource file (Right click -> View code), and delete everything in the file. Copy the following code, and modify it to fit your needs. The #include <windows.h> statement allows you to declare a language for a resource, which I can cover in a later tutorial.
#include "ExtraResources.h"
#include <windows.h>

C_SAMPLE_TEXT_1 RCDATA "Sample.txt"
C_SAMPLE_TEXT_2 RCDATA "Sample2.txt"

Accessing resource data

std::vector<char> LoadEmbeddedResource(LPCTSTR name)
{
    std::vector<char> output;

    // If you need to retrieve resources embedded in a binary that is not the current running program, modify this
    // function to pass in a HMODULE value.
    HMODULE module = nullptr;

    auto resourceHandle = ::FindResource(module, name, RT_RCDATA);

    if (resourceHandle != nullptr)
    {
        auto dataHandle = ::LoadResource(module, resourceHandle);

        if (dataHandle != nullptr)
        {
            auto resourceSize = ::SizeofResource(module, resourceHandle);

            if (resourceSize != 0)
            {
                auto firstByte = reinterpret_cast<const char*>(::LockResource(dataHandle));

                if (firstByte != nullptr)
                {
                    output.resize(resourceSize);
                    std::copy(firstByte, firstByte + resourceSize, output.begin());

                    // No need to call ::FreeResource on any 32 or 64 bit version of Windows. See MSDN for details
                    // on why the call is not needed:
                    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648044%28v=vs.85%29.aspx
                }
            }
        }
    }

    return output;
}

Loading resources

#include "ExternalResources.h"

void Foo()
{
   auto data = LoadEmbeddedResource(MAKEINTRESOURCE(C_YOUR_EXTERNAL_ASSET_ID));
   auto string = std::string(data.begin(), data.end());
   OutputDebugStringA(string.c_str());
}

You’re all done, congrats!

References

http://stackoverflow.com/questions/7288279/how-to-embed-a-file-into-an-executable
https://msdn.microsoft.com/en-us/library/6e7446zd.aspx
https://msdn.microsoft.com/en-us/library/aa381054%28v=vs.85%29.aspx
https://msdn.microsoft.com/en-us/library/ms648046%28v=VS.85%29.aspx