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
- Add text files named “Sample.txt” and “Sample2.txt” to the project with some random text.
Add a new resource file and header
- Add a new resource file (.rc) to your project. This example will use ExternalResources.rc.
-
Add a new header file (.h) to your project with the same name but a .h extension.
-
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
- 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