19

I have custom binary resources (animated cursors) that would like to store as resources in a static lib in Visual Studio C++. It turns out that custom binary resources will not get loaded by ::LoadCursor() or found by ::FindResource() if it is a custom resource and in a static library.

This question gives some work around.

Following its advice, if I add the *.res file to an exe as a "Configuration Property->Linker->Additional Dependency" then the static library will be able to find the resource.

BUT if the static library is part of a dll and I link it in as an Additional Dependency it is not found again!

How can I link the resources in a dll?

Or just make the binary be found in the static lib? The methods in the question are pretty cumbersome.

Community
  • 1
  • 1
meissnersd
  • 1,272
  • 2
  • 12
  • 21
  • It is pretty unclear whether or not you forgot to add the required .res file in the DLL project. The most typical failure mode is passing the wrong module handle to FindResource(). It must be the DLL's module handle. You get it from DllMain(). And yes, gluing this together is cumbersome by design. – Hans Passant Feb 11 '12 at 16:52

3 Answers3

45

In Add Resource dialog click Import, select "All Files (.)" so that it allows you to import file of any type, and then just select the file you want there. When Custom Resource Type dialog pops up, type RCDATA into "Resource type" field.

If you open .rc file, you will see something like this:

/////////////////////////////////////////////////////////////////////////////
//
// RCDATA
//

IDR_RCDATA1          RCDATA               "myfile.whatever"

and it will generate resource.h with following line:

#define IDR_RCDATA1                  101

In code you access it like this:

#include "resource.h"
#include <windows.h>

int main(int argc, char* argv[])
{
    HRSRC myResource = ::FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
    HGLOBAL myResourceData = ::LoadResource(NULL, myResource);
    void* pMyBinaryData = ::LockResource(myResourceData);
    return 0;
}

where pMyBinaryData is pointer to first byte of this executable. For more information visit Resource Functions

Here's an example how you would save binary resource like this on disk:

#include "resource.h"
#include <windows.h>
#include <fstream>

int main(int argc, char* argv[])
{
    HRSRC myResource = ::FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
    unsigned int myResourceSize = ::SizeofResource(NULL, myResource);
    HGLOBAL myResourceData = ::LoadResource(NULL, myResource);
    void* pMyBinaryData = ::LockResource(myResourceData);

    std::ofstream f("C:\\x.bin", std::ios::out | std::ios::binary);
    f.write((char*)pMyBinaryData, myResourceSize);
    f.close();

    return 0;
}

When you build project with resource like that, this resource will become part of your program (dll).

Dan Nissenbaum
  • 13,558
  • 21
  • 105
  • 181
LihO
  • 41,190
  • 11
  • 99
  • 167
  • 1
    And also not to forget to unlock the resource: `BOOL bResult = ::UnlockResource(hRes);` and `bResult = ::FreeResource(hRes);` – DitherSky Mar 10 '12 at 19:12
  • 12
    @DitherSky: No. It is not needed anymore, check documentation of [FreeResource function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms648044(v=vs.85).aspx): *"This function is obsolete and is only supported for backward compatibility with 16-bit Windows. For 32-bit Windows applications, it is not necessary to free the resources loaded using LoadResource."* – LihO Mar 10 '12 at 22:14
  • 1
    `UnlockResource` is just a no-op macro in the SDK, yet weirdly `FreeResource` has a real no-op export on `kernel32` for backcomp. – wqw Nov 16 '17 at 12:00
  • 1
    Works well if the resource is inside the calling application - if the resource is inside a libary being called from something else the first parameter of the ::FindResource call needs to be the module handle of the library where the resource is embedded - HRSRC myResource = ::FindResource(hModule, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA); – Malcolm Swaine Jun 30 '19 at 02:15
  • 1
    First parameter of **FindResource** is the module. _NULL_ will refer to the exe of the process. So this won't load the resource of the DLL. – Ralph Erdt Jul 17 '19 at 09:20
  • Don't forget to clean up the mess! FreeResource(myResourceData); – Digital Human Mar 16 '20 at 18:24
  • Also, thank you for this answer! This is the only right way. Works like a char. I was stuck at the RCDATA part. Maybe you can explain what types there are ? I have read about TEXT. Couldn't get that working though. @LihO – Digital Human Mar 16 '20 at 18:29
  • But what if we don't need the resource no more but the app keeps running? – user13947194 Jan 22 '22 at 00:24
1

The Problem of @LihO answer is:

The first parameter of FindResource is the ModuleID of the Module containing the resources. If this is set to NULL the function will search in created process (.exe), not the DLL.

But how to get the HMODULE insinde a static LIB?

  • add a function / parameter, which will get the HMODULE from the DLL. The HMODULE / HINSTANCE (is the same) can be retrieved in DLLMain.
  • Try this GetCurrentModule

Edit:

See also: Add lib resource to a library

Ralph Erdt
  • 537
  • 4
  • 16
1

In case you use dll using MFC (and CWinApp), you can obtain the HMODULE from CWinApp.

extern MyDllApp theApp;
HMODULE module = (HMODULE)theApp.m_hInstance;
HRSRC myResource = ::FindResource(module,
            MAKEINTRESOURCE(IDR_FILE_RESOURCE), _T("GROUP_NAME"));

If you supply NULL in FindResource, application won't find your resource.

Michal Pokluda
  • 390
  • 4
  • 11