是否可以将资源构建到静态库中并通过简单地与库链接来重用它们?
我主要考虑的是您在库中调用一个函数进而访问资源的情况。
要在 Visual C++ (2008) 的静态库中使用资源(图像、对话框等),您唯一需要做的就是在项目中包含静态库的关联 .res 文件。这可以在“项目设置/链接器/输入/附加依赖项”中完成。
使用此解决方案,静态库的资源被打包到 .exe 中,因此您不需要额外的 DLL。遗憾的是,Visual Studio 没有像 .lib 文件那样自动包含 .res 文件(使用“项目依赖项”功能时),但我认为这个额外的小步骤是可以接受的。
我已经为这个解决方案寻找了很长时间,现在让我惊讶的是它就是这么简单。唯一的问题是它完全没有记录。
可以做到,但是比较痛苦:不能简单的通过静态库的链接来做到。
考虑一下:资源嵌入在 EXE 或 DLL 中。当静态库中的某些代码调用(例如)LoadIcon 时,它将从与其链接的 EXE 或 DLL 中获取资源。
所以,如果你的静态库需要可用的资源,你有几个选择:
CreateDialogIndirect
。请参阅 Raymond Chen 的“在运行时构建对话框模板”。char my_dialog_resource[] = { .... };
,然后使用 (eg) CreateDialogIndirect
。您可能需要找到(或编写)一个将文件转换为.RES
文件的实用程序.CPP
。.RC
您可以使用资源脚本(文件)和相应的头文件发送 LIB文件。然后,您将#include
它们视为相关。您需要保留一系列资源 ID 供 LIB 使用,以免它们与主 EXE 或 DLL 的资源 ID 冲突。这就是 MFC 在用作静态库时所做的事情。或者您可以使用字符串资源 ID(这不适用于STRINGTABLE
资源)。我刚刚用 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 自动完成所有这些操作,但我发现它并不总是在我期望的时候适用。
如果您的库的资源脚本引用磁盘上的任何文件(文本文件、图标文件等),您需要确保主应用程序项目知道在哪里可以找到它们。您可以将这些文件复制到应用程序可以找到它们的位置,也可以在编译器设置中添加额外的包含路径。
要添加额外的包含路径:
根据 Visual Studio 2010,微软的开发工具显然根本无法正确处理静态库中的已编译资源数据。
要分发已编译的资源文件(.res
文件),您有两种选择:
.res
文件,并指示客户端代码链接它们;cvtres
将多个.res
文件合并为一个对象 ( .obj
) 文件,并单独提供。请注意,您不能在使用cvtres
. 如果提供了多个目标文件,lib
就好像提供了多个.res
文件一样抱怨;如果提供了单个目标文件,lib
则不会抱怨,但链接器会简单地忽略 lib 文件中嵌入的资源数据。
可能有一种方法可以强制链接器读取和链接资源数据中的 libbed(使用一些命令行选项、部分操作等),因为资源数据确实在库中可用(如dumpbin
揭示)。到目前为止,我还没有找到解决方案,除非有人愿意破解开发工具,否则任何比这个简单解决方案更好的东西都可能不值得付出努力。
在静态库(在这种情况下,使用静态库)中传送资源数据的唯一方法是单独分发资源并在客户端代码中显式链接它们。使用cvtres
可以将分布式资源文件的数量减少到一个,如果你有很多的话。
我不这么认为。静态库没有自己的 HINSTANCE。它的代码在链接它的 DLL 或 EXE 的上下文中执行。这就是为什么您将尝试从静态库的代码中加载的所有资源都将属于那个封闭的 DLL/EXE。
不过,我使用 DLL 进行了这种资源重用,只要它有自己的地址空间,您可以使用 DLL 的 HINSTANCE 调用 LoadResource。
推荐的方法是与您的库一起提供包含资源的 dll。
当使用以下方法时,任何资源(在此示例中为图标)都可以用作静态库的组成部分,并且此类库可以被任何类型的应用程序使用,包括控制台应用程序(没有任何资源段)。
数据被转换为 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()