Embedding DLL and binary files in the Win32 Executable
Embedding DLL and binary files in the Executable applications Though for most of the cases, it’s recommended keep the resources and other libraries separate from the main executable. This makes the main application executable size small, and thus that application will take less memory on load and will load/unload the external resources as required. Yet sometimes, it makes sense to append the external files as part of executable. A popular example of this is self extracting installer. The basic technique behind this whole process is simple. Here is what needs to be done:
Write a resource script file which provides path to the external files you want to append in the final resource object.
- Use the Resource Compiler to build a RES file from the above script.
- Bind the RES file with executable.
- Call the APIs to extract and load the resources from the executable memory space at run-time.
- First one to add a Resource Script file in the executable project. Here is a sample code to define the binary resource in
- the resource script file
IDR_TBEHK_DEBUG BINARY MOVEABLE PURE "..debugTaskbarExtHk.dll" |
IDR_TBEHK_DEBUG BINARY MOVEABLE PURE "..debugTaskbarExtHk.dll"
Please note that “IDR_TBEHK_DEBUG” is user defined constant and must be declared before this line. Here is how I have defined this at top:
#define IDR_TBEHK_DEBUG130 |
#define IDR_TBEHK_DEBUG130
There is catch here. Some of the files are conditional and thus we may need some checks on which version of file to include. One way of doing this is to use the standard C++ compiler directives (these syntax is almost same for Resource Compiler):
#ifdef _DEBUG IDR_TBEHK_DEBUG BINARY MOVEABLE PURE "..debugTaskbarExtHk.dll" #else// _DEBUG IDR_TBEHK_RELEASE BINARY MOVEABLE PURE "..releaseTaskbarExtHk.dll" #endif |
#ifdef _DEBUG IDR_TBEHK_DEBUG BINARY MOVEABLE PURE "..debugTaskbarExtHk.dll" #else// _DEBUG IDR_TBEHK_RELEASE BINARY MOVEABLE PURE "..releaseTaskbarExtHk.dll" #endif
But wait, we also need to define the value for the _DEBUG symbol (the one defined for C++ project will not work here, as this file is read by resource compiler and is a totally different context). In Visual Studio, you can easily define these Resource preprocessor directives in the project properties. Simply add “_DEBUG” for the DEBUG configuration and it will add the conditional embedding of the correct version.
Another simple option for including binary libraries is to change the project settings is to set a single output folder for both Debug and Release configurations and then just include that single file. Once the RC file is ready, it can be compiled to RES file using the Resource Compiler. Here is a sample command line for this: rc.exe /fo”Release/TaskbarExt.res” “.TaskbarExt.rc” If you are using Visual Studio, then it will automatically call this for you and will build the RES file and will also bind it with executable.
The next big part is finding and loading the resource from the executable. Here is a sample code to do this:
bool ExtractResource(const HINSTANCE hInstance, WORD resourceID, LPCTSTR szOutputFilename) { bool bSuccess = false; try { // First find and load the required resource HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(resourceID), _T("BINARY")); HGLOBAL hFileResource = LoadResource(hInstance, hResource); // Now open and map this to a disk file LPVOID lpFile = LockResource(hFileResource); DWORD dwSize = SizeofResource(hInstance, hResource); // Open the file and filemap HANDLE hFile = CreateFile(szOutputFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE hFilemap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL); LPVOID lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, 0, 0, 0); // Write the fileCopyMemory(lpBaseAddress, lpFile, dwSize); // Unmap the file and close the handles UnmapViewOfFile(lpBaseAddress); CloseHandle(hFilemap); CloseHandle(hFile); } catch(...) { // Ignore all type of errors } return bSuccess; } |
bool ExtractResource(const HINSTANCE hInstance, WORD resourceID, LPCTSTR szOutputFilename) { bool bSuccess = false; try { // First find and load the required resource HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(resourceID), _T("BINARY")); HGLOBAL hFileResource = LoadResource(hInstance, hResource); // Now open and map this to a disk file LPVOID lpFile = LockResource(hFileResource); DWORD dwSize = SizeofResource(hInstance, hResource); // Open the file and filemap HANDLE hFile = CreateFile(szOutputFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE hFilemap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL); LPVOID lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, 0, 0, 0); // Write the fileCopyMemory(lpBaseAddress, lpFile, dwSize); // Unmap the file and close the handles UnmapViewOfFile(lpBaseAddress); CloseHandle(hFilemap); CloseHandle(hFile); } catch(...) { // Ignore all type of errors } return bSuccess; }
Once you are done with resource, you can remove it by calling the DeleteFile API.
Simple is that! Isn’t it?
Tags: C++, DLL, Resouces, Win32
This entry was posted
on Wednesday, November 7th, 2007 at 12:41 am and is filed under Visual C++, Win32 API.
You can follow any responses to this entry through the RSS 2.0 feed.
You can leave a response, or trackback from your own site.
Feedback & Comments
18 Responses