静态库中的 VC++ 资源

Posted

技术标签:

【中文标题】静态库中的 VC++ 资源【英文标题】:VC++ resources in a static library 【发布时间】:2009-02-10 08:39:48 【问题描述】:

是否可以将资源构建到静态库中并通过简单地与库链接来重用它们?

我主要考虑的是您调用库中的函数进而访问资源的情况。

【问题讨论】:

【参考方案1】:

在 Visual C++ (2008) 中使用静态库中的资源(图像、对话框等)唯一需要做的就是包含静态库的关联 .res 文件在你的项目中。这可以在“项目设置/链接器/输入/附加依赖项”中完成。

通过这个解决方案,静态库的资源被打包到 .exe 中,因此您不需要额外的 DLL。遗憾的是,Visual Studio 没有像 .lib 文件那样自动包含 .res 文件(使用“项目依赖项”功能时),但我认为这个额外的小步骤是可以接受的。

我已经为这个解决方案寻找了很长时间,现在它让我感到惊讶,它就是这么简单。唯一的问题是它完全没有记录。

【讨论】:

这可能不是原始问题所要求的,但这节省了我的一天! +1 对我也很有效! @kizzx2 我同意这不是问题所要求的,但我认为它实现了最终目标。 知道这是如何在手写的 makefile 中完成的吗?我在这里缺少记录在案的编译器选项或编译指示。 酷!非常感谢,Dimitri C.! 很好的答案!尽管您现在在链接时可能会遇到重复的资源 ID 错误(CVTRES 错误 1100)。不过幸运的是,对 resource.h 文件中的范围重新编号是一个很小的代价!【参考方案2】:

可以做到,但是比较痛苦:不能简单的跟静态库链接。

考虑一下:资源嵌入在 EXE 或 DLL 中。当静态库中的某些代码调用(例如)LoadIcon 时,它会从与其链接的 EXE 或 DLL 中获取资源。

因此,如果您的静态库需要可用资源,您有两种选择:

    您可以让库即时构建它们,然后使用(例如)CreateDialogIndirect。请参阅 Raymond Chen 的 "Building a dialog template at run-time"。 您可以将它们作为简单数组(即)char my_dialog_resource[] = .... ; 嵌入库中,然后使用(例如)CreateDialogIndirect。您可能需要找到(或编写)将.RES 文件转换为.CPP 文件的实用程序。 您可以发送带有资源脚本(.RC 文件)和相应头文件的 LIB 文件。然后你#include他们是相关的。您需要保留一系列资源 ID 供 LIB 使用,以免它们与主 EXE 或 DLL 的资源 ID 冲突。这就是 MFC 在用作静态库时所做的事情。或者您可以使用字符串资源 ID(这不适用于 STRINGTABLE 资源)。 您的静态库可以附带一个单独的资源 DLL。

【讨论】:

我实际上已经完成了您的选项 2,这并不难。您还需要为 .res 文件格式构建一个解析器,该文件格式有据可查,而且不会太糟糕。 @Roger Lipscombe 你能看出 Dimitri C. 的回答不是一个好主意的任何原因吗? 因为 .RES 文件需要确保它不会与资源 ID 冲突。我的#3 有同样的问题,除了你可以用一些预处理器魔法来缓解它。你不能用 Dimitri 的回答来做到这一点。 另外,我不确定 .RES 文件是否能保证跨链接器版本工作,而 .LIB 文件是。 jeff_t 的回答解释了如何做#3【参考方案3】:

我刚刚使用 MS Visual Studio 编译器完成了这个。我们正在将一些遗留项目从 DLL 转换为静态库。其中一些 DLL 中嵌入了对话框或字符串资源。我能够通过“TEXTINCLUDE”机制将这些 DLL 的 .RC 脚本编译到我们的主应用程序中,方法是将它们包含在主应用程序的 RC 脚本文件中。我发现通过直接编辑 RC 文件最容易做到这一点,但 Visual Studio 也提供了一个稍微“聪明”的机制。其他编译器的实现很可能不同。


直接操作主 RC 脚本:

.1。在“2 TEXTINCLUDE”部分中,包含定义库的资源 ID 的头文件。语法是

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

.2。在“3 TEXTINCLUDE”部分,包含您库中的 RC 脚本。

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

步骤 3 和 4 应该自动发生,但我发现自己输入它们更可靠,而不是依赖 Microsoft 的资源脚本编译器来处理。

.3。将带有库资源定义的头文件添加到只读符号列表中。此列表通常位于文件顶部附近。

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

.4。在 APSTUDIO_INVOKED 部分中包含您的库的 RC 脚本。这通常位于文件的底部。

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

您也可以通过 Visual Studio IDE 自动完成所有这些操作,但我发现它并不总是如我预期的那样适用。

    在 Visual Studio 中打开“资源视图”窗口。 右键单击主应用程序的资源文件,然后从上下文菜单中选择“资源包含...”。 在标有“只读符号指令”的框中,为定义库资源 ID 的 .h 文件添加包含语句。 在标有“编译时指令”的框中,为您的库的 .rc 脚本添加包含语句。 单击确定。您可能还想手动触发 RC 脚本编译,以确保它发生。

如果您的库的资源脚本引用磁盘上的任何文件(文本文件、图标文件等),您需要确保主应用程序项目知道在哪里可以找到它们。您可以将这些文件复制到应用程序可以找到它们的位置,也可以在编译器设置中添加额外的包含路径。

添加额外的包含路径:

    打开主应用程序的属性对话框。 从左侧导航窗格中选择“配置属性/资源/常规”。 在属性列表中,在“其他包含目录”旁边输入任何相关路径。

【讨论】:

谢谢!这拯救了我的一天!虽然我能够省略 VS2010 上的头文件的包含。在我刚刚为 .rc 文件添加了两个条目之后它就起作用了。【参考方案4】:

根据 Visual Studio 2010,微软的开发工具显然根本无法正确处理静态库中的已编译资源数据。

要分发已编译的资源文件(.res 文件),您有两种选择:

    分别分发.res文件,并指示客户端代码链接它们; 使用cvtres 将多个.res 文件合并为一个对象(.obj) 文件,并分别提供。

请注意,您不能在使用cvtres 创建的目标文件中进行库。如果提供了多个目标文件,lib 会抱怨好像提供了多个 .res 文件;如果提供了单个目标文件,lib 不会抱怨,但链接器会简单地忽略 lib 文件中嵌入的资源数据。

可能有一种方法可以强制链接器读取和链接资源数据中的 libbed(使用一些命令行选项、部分操作等),因为资源数据确实在库(正如dumpbin 所揭示的那样)。到目前为止,我还没有找到解决方案,除非有人愿意破解开发工具,否则任何比这个简单解决方案更好的东西都可能不值得。

在静态库(在本例中,静态库)中传送资源数据的唯一方法是单独分发资源并在客户端代码中显式链接它们。使用cvtres 可以将分布式资源文件的数量减少到一个,如果你有很多的话。

【讨论】:

运送一个 cvt-res 转换的 obj 文件的缺点是 link.exe 只允许一个包含资源数据的 obj 文件,否则会发出 LNK1241。因此,如果不止一个库提供了一个 cvt-res 转换的 obj 文件,那就是个坏消息。 link.exe 有一个 /wholearchive 标志,根据文档强制将所有 .obj 文件包含在 lib 文件中。但是文档似乎不正确,据我所知,它仅强制包含在静态库索引中具有条目的所有 obj 文件。由于 res 文件不导出符号,因此它们不会在索引中引用,因此即使在传递 /wholearchive 时也不包含在内。【参考方案5】:

我不这么认为。静态库没有自己的 HINSTANCE。它的代码在链接它的 DLL 或 EXE 的上下文中执行。这就是为什么您将尝试从静态库的代码中加载的所有资源都将属于那个封闭的 DLL/EXE。

不过,我使用 DLL 进行了这种资源重用,只要它有自己的地址空间,您可以使用 DLL 的 HINSTANCE 调用 LoadResource。

【讨论】:

【参考方案6】:

推荐的方法是提供包含资源的 dll 以及您的库。

【讨论】:

【参考方案7】:

当使用以下方法时,任何资源(在此示例中为图标)都可以用作静态库的组成部分,并且此类库可以被任何类型的应用程序使用,包括控制台应用程序(它不'没有任何资源段)。

    图标被转换为 BYTE 的静态数组。 bin2c 可以用于此。

    数据被转换为 HICON 句柄。以下是我的做法:

    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;  
    
    

    使用 GetIcon 代替 LoadIcon。 而不是调用:

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

然后调用

m_hIcon = GetIcon()

【讨论】:

以上是关于静态库中的 VC++ 资源的主要内容,如果未能解决你的问题,请参考以下文章

QT creator 如何调用VC写的静态库

plist文件无法打包进.a静态库中

MSVC 2019:遗留库中的 libc 外部未解决

静态库中的外部指针为空,当不是静态库时工作正常

gcc找不到静态库中的函数

静态库中的 MagicalRecord:如何加载数据模型?