VC++ resources in a static library
Asked Answered
M

8

41

Is it possible to build resources into a static library and reuse them by simply linking with the library?

I'm primarily thinking about the case where you call a function in the library which in turn accesses resources.

Melanimelania answered 10/2, 2009 at 8:39 Comment(0)
R
26

It can be done, but it's quite painful: You can't do it by simply linking with the static library.

Consider this: resources are embedded in an EXE or DLL. When some code in the static library calls (e.g.) LoadIcon, it'll get the resources from the EXE or DLL that it's linked with.

So, if your static library requires resources to be available, you've got a couple of options:

  1. You can have the library build them on the fly, and then use (e.g.) CreateDialogIndirect. See Raymond Chen's "Building a dialog template at run-time".
  2. You can have them embedded in the library as simple arrays (i.e.) char my_dialog_resource[] = { .... };, and then use (e.g.) CreateDialogIndirect. You'll probably need to find (or write) a utility that converts from .RES files to .CPP files.
  3. You can ship the LIB file with a resource script (.RC file) and corresponding header file. You then #include them as relevant. You'll need to reserve a range of resource IDs for the LIB to use, so that they don't collide with those of the main EXE or DLL. This is what MFC does when used as a static library. Or you can use string resource IDs (this doesn't work for STRINGTABLE resources).
  4. Your static library can ship with a separate resource DLL.
Rapparee answered 10/2, 2009 at 10:20 Comment(6)
I've actually done your option 2, and it's not all that hard. You need to build a parser for the .res file format as well, which is well documented and not too terrible.Materialize
@Roger Lipscombe Can you see any reason why Dimitri C.'s answer isn't a good idea?Diction
Because the .RES file needs to ensure that it doesn't clash with resource IDs. My #3 has the same problem, except that you can mitigate it with some preprocessor magic. You can't do this with Dimitri's answer.Rapparee
Also, I'm not sure that the .RES file is guaranteed to work across linker versions, whereas the .LIB file is.Rapparee
jeff_t's answer explains how to do #3Tepefy
If I want to embed an icon in a static library, how should I do it?Indoor
F
64

The only thing you need to do to use resources (images, dialogs, etc...) in a static library in Visual C++ (2008), is include the static library's associated .res file in your project. This can be done at "Project settings/Linker/Input/Additional dependencies".

With this solution, the resources of the static library are packed into the .exe, so you don't need an extra DLL. Regrettably, Visual Studio does not include the .res file automatically as it does for the .lib file (when using the "project dependencies"-feature), but I think this small extra step is acceptable.

I have looked for a very long time for this solution, and now it surprises me it is that simple. The only problem is that it is totally undocumented.

Fluorocarbon answered 27/10, 2009 at 14:10 Comment(6)
This probably wasn't what the original question asked for, but this saved my day! +1Septicemia
Worked great for me too! @Septicemia I agree isn't exactly what the question asked for, but I think it accomplishes the ultimate goal.Diction
Any idea how this is done in a hand written makefile? I'm misssing a documented compiler option or pragma here.Ganoid
Great answer! Chances are though you will now run into duplicate resource ID errors while linking (CVTRES error 1100). Fortunately though, renumbering the ranges in the resource.h files is a small price to pay!Fults
I took over somebody's project with a similar setup and didn't realize that the main app was linking against the library AND the .res file, and due to a build script error the .res file wasn't properly updated, which resulted in the main app spitting out strange errors about controls not being found even though they were clearly there. It all makes sense now - they weren't in the .res file which was out of date.Guillerminaguillermo
It was surprising but works like charm, thx :)Bascomb
R
26

It can be done, but it's quite painful: You can't do it by simply linking with the static library.

Consider this: resources are embedded in an EXE or DLL. When some code in the static library calls (e.g.) LoadIcon, it'll get the resources from the EXE or DLL that it's linked with.

So, if your static library requires resources to be available, you've got a couple of options:

  1. You can have the library build them on the fly, and then use (e.g.) CreateDialogIndirect. See Raymond Chen's "Building a dialog template at run-time".
  2. You can have them embedded in the library as simple arrays (i.e.) char my_dialog_resource[] = { .... };, and then use (e.g.) CreateDialogIndirect. You'll probably need to find (or write) a utility that converts from .RES files to .CPP files.
  3. You can ship the LIB file with a resource script (.RC file) and corresponding header file. You then #include them as relevant. You'll need to reserve a range of resource IDs for the LIB to use, so that they don't collide with those of the main EXE or DLL. This is what MFC does when used as a static library. Or you can use string resource IDs (this doesn't work for STRINGTABLE resources).
  4. Your static library can ship with a separate resource DLL.
Rapparee answered 10/2, 2009 at 10:20 Comment(6)
I've actually done your option 2, and it's not all that hard. You need to build a parser for the .res file format as well, which is well documented and not too terrible.Materialize
@Roger Lipscombe Can you see any reason why Dimitri C.'s answer isn't a good idea?Diction
Because the .RES file needs to ensure that it doesn't clash with resource IDs. My #3 has the same problem, except that you can mitigate it with some preprocessor magic. You can't do this with Dimitri's answer.Rapparee
Also, I'm not sure that the .RES file is guaranteed to work across linker versions, whereas the .LIB file is.Rapparee
jeff_t's answer explains how to do #3Tepefy
If I want to embed an icon in a static library, how should I do it?Indoor
R
12

I just went through this with the MS Visual Studio compiler. We were converting some legacy projects from DLLs into static libraries. Several of these DLLs had dialog or string resources embedded in them. I was able to compile the .RC scripts for these DLLs into our main application by including them in the main application's RC script file via the "TEXTINCLUDE" mechanism. I found it easiest to do this by editing the RC file directly, but Visual Studio provides a slightly more "wizardy" mechanism as well. The implementation is most likely different in other compilers.


To manipulate the main RC script directly:

.1. In the "2 TEXTINCLUDE" section, include the header file that defines the resource IDs for your library. The syntax is

2 TEXTINCLUDE 
BEGIN
    "#include ""my_first_lib_header.h""\r\n"
    "#include ""my_second_lib_header.h""\0" 
END

.2. In the "3 TEXTINCLUDE" section, include the RC script from your library.

3 TEXTINCLUDE
BEGIN
    "#include ""my_first_library.rc""\r\n"
    "#include ""my_second_library.rc""\0"
END

Steps 3 and 4 should happen automatically, but I found it was more reliable to just enter them myself, rather than depending on Microsoft's resource script compiler to take care of things.

.3. Add the header file with your libraries resource defines to the read only symbols list. This list is usually near the top of the file.

#define APSTUDIO_READONLY_SYMBOLS
#include "my_first_lib_header.h"
#include "my_second_lib_header.h"
#undef APSTUDIO_READONLY_SYMBOLS

.4. Include your library's RC script in the APSTUDIO_INVOKED section. This is usually at the bottom of the file.

#ifndef APSTUDIO_INVOKED
#include "my_first_library.rc"
#include "my_second_library.rc"
#endif 

You can also do all of this automatically through the visual studio IDE, but I found it didn't always apply when I expected it to.

  1. Open the "Resource View" window in Visual Studio.
  2. Right-click on your main application's resource file and choose "Resource Includes..." from the context menu.
  3. In the box labeled "Read-only symbol directives," add the include statements for the .h files that define the resource ID's for your libraries.
  4. In the box labeled "Compile-time directives," add the include statements for your library's .rc script.
  5. Click okay. You may also want to manually trigger the RC script compilation, to make sure it happens.

If your library's resource script references any files on disk (text files, icons files, etc.), you'll need to make sure that the main application project knows where to find them. You can either copy these files to somewhere your application can find them or you can add an additional include path in the compiler settings.

To add an additional include path:

  1. Open up the properties dialog for your main application.
  2. Select "Configuration Properties/Resources/General" from the left-hand navigation pane.
  3. In the properties list, Enter any pertinent paths next to "Additional Include Directories."
Recusancy answered 24/6, 2009 at 18:27 Comment(1)
Thanks! This saved my day! Although I was able to omit the includes of the headers on VS2010. It worked after I just added the two entries for the .rc files.Oration
L
2

As per Visual Studio 2010, the development tools from Microsoft apparently cannot properly handle compiled resource data inside static libraries at all.

To distribute a compiled resource file (a .res file), you have two choices:

  1. Distribute the .res files separately, and instruct the client code to link against them;
  2. Use cvtres to merge several .res files into a single object (.obj) file, and provide it separately.

Note that you can't lib in object files created with cvtres. If multiple object files are provided, lib complains as though as multiple .res files were given; if a single object file is provided, lib does not complain, but the linker simply ignores the embedded resource data in the lib file.

It might be the case that there is a way to force the linker to read and link the libbed in resource data (with some command-line option, section manipulation and so on), since the resource data is indeed available in the library (as dumpbin reveals). So far, I haven't found a solution, and, unless one is willing to hack the development tools, anything better than this simple solution is probably not worth the effort.

The only way to ship resource data in a static library (in this case, with a static library) is to distribute the resources separately and explicitly link them in the client code. Using cvtres can reduce the number of distributed resource files to one, if you have many of them.

Lancelancelet answered 23/2, 2013 at 22:41 Comment(2)
Shipping a cvt-res converted obj file has the drawback that link.exe allows only a single obj file containing resource data and emits LNK1241 otherwise. So if more than one library ships a cvt-res converted obj file, that's bad news.Bondstone
link.exe has a /wholearchive flag that according to the documentation forces inclusion of all .obj files in a lib file. But the documentation seems to be incorrect, from what I can tell it only forces inclusion of all obj files that have entries in the static library's index. And since res files don't export symbols, they're not referenced in the index and hence not included even when passing /wholearchive.Bondstone
P
1

I don't think so. Static library doesn't have it's own HINSTANCE. It's code is executed in the context of DLL or EXE which links it. That's why all the resources you'll try to load from the static library's code will be of that enclosing DLL/EXE.

I did that kind of resources reuse with a DLL though, as far as it has it's own address space, and you can call LoadResource with DLL's HINSTANCE.

Pitchy answered 10/2, 2009 at 8:55 Comment(0)
W
0

The recommended way is to provide a dll with the resources together with your library.

Westland answered 11/2, 2009 at 10:17 Comment(0)
M
0

Yes, resources can be included in static libraries, however generally linkers will discard any resource from static libraries. As such you should provide compiled resources (.res) together with your static library (.lib), then users should link to both files.

Anyway with a bit of work it's possible to have everything in a single .lib file. This is specific to the MSVC toolchain.

  1. Use cvtres to convert the compiled resource file (.res) to COFF/PE format (.obj) specifying a symbol name: cvtres /define:my_library_resources my_library.res
  2. In a source file of your library, add the following linker option: #pragma comment(linker, "/include:my_library_resources")

This way the resources will be visible to the linker and will not be discarded.

Finally, note that PE Resources can be indexed either by number or by name. Of course, for static libraries it's best to use names!

Mikey answered 29/8, 2023 at 10:32 Comment(0)
I
-3

When the following method is used, any resource (in this example, an icon) can be used as an integral part of a static library and such library can be used by any type of application, including a console one (which doesn't have any resource segment whatsoever).

  1. Icon is converted to a static array of BYTE. bin2c can be used for that.
  2. Data is converted into a HICON handle. Here is how I have done that:

    HICON GetIcon()
    { 
       DWORD dwTmp;
       int offset;
       HANDLE hFile;
       HICON hIcon = NULL;
       offset = LookupIconIdFromDirectoryEx(s_byIconData, TRUE, 0, 0, LR_DEFAULTCOLOR);
       if (offset != 0)
       {
          hIcon = CreateIconFromResourceEx(s_byIconData + offset, 0, TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
       }
       return hIcon;  
    }
    
  3. GetIcon is used instead of LoadIcon. Instead of calling:

m_hIcon = ::LoadIcon(hInstanceIcon, MAKEINTRESOURCE(pXMB->nIcon));

Then call

m_hIcon = GetIcon()
Indoor answered 21/9, 2013 at 15:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.