40

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

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

4

7 回答 7

63

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

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

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

于 2009-10-27T14:10:33.893 回答
26

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

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

所以,如果你的静态库需要可用的资源,你有几个选择:

  1. 您可以让库即时构建它们,然后使用 (eg) CreateDialogIndirect。请参阅 Raymond Chen 的“在运行时构建对话框模板”
  2. 您可以将它们作为简单数组 (ie) 嵌入到库中char my_dialog_resource[] = { .... };,然后使用 (eg) CreateDialogIndirect。您可能需要找到(或编写)一个将文件转换为.RES文件的实用程序.CPP
  3. .RC您可以使用资源脚本(文件)和相应的头文件发送 LIB文件。然后,您将#include它们视为相关。您需要保留一系列资源 ID 供 LIB 使用,以免它们与主 EXE 或 DLL 的资源 ID 冲突。这就是 MFC 在用作静态库时所做的事情。或者您可以使用字符串资源 ID(这不适用于STRINGTABLE资源)。
  4. 您的静态库可以附带一个单独的资源 DLL。
于 2009-02-10T10:20:37.017 回答
11

我刚刚用 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 步应该自动发生,但我发现自己输入它们更可靠,而不是依靠微软的资源脚本编译器来处理。

.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 自动完成所有这些操作,但我发现它并不总是在我期望的时候适用。

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

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

要添加额外的包含路径:

  1. 打开主应用程序的属性对话框。
  2. 从左侧导航窗格中选择“配置属性/资源/常规”。
  3. 在属性列表中,在“其他包含目录”旁边输入任何相关路径。
于 2009-06-24T18:27:02.507 回答
2

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

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

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

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

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

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

于 2013-02-23T22:41:21.810 回答
1

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

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

于 2009-02-10T08:55:43.383 回答
0

推荐的方法是与您的库一起提供包含资源的 dll。

于 2009-02-11T10:17:29.567 回答
-3

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

  1. Icon 被转换为 BYTE 的静态数组。bin2c可以用于此。
  2. 数据被转换为 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;  
    }
    
  3. 使用 GetIcon 代替 LoadIcon。而不是调用:

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

然后打电话

m_hIcon = GetIcon()
于 2013-09-21T15:24:14.990 回答