Ресурсы VC ++ в статической библиотеке
-
22-08-2019 - |
Вопрос
Можно ли встроить ресурсы в статическую библиотеку и повторно использовать их, просто связавшись с библиотекой?
В первую очередь я имею в виду случай, когда вы вызываете функцию в библиотеке, которая, в свою очередь, обращается к ресурсам.
Решение
Это можно сделать, но это довольно болезненно:Вы не можете сделать это, просто связавшись со статической библиотекой.
Подумайте об этом:ресурсы встроены в EXE или DLL.Когда некоторый код в статической библиотеке вызывает (например) LoadIcon, он получит ресурсы из EXE или DLL, с которыми он связан.
Итак, если вашей статической библиотеке требуются доступные ресурсы, у вас есть пара вариантов:
- Вы можете попросить библиотеку создать их "на лету", а затем использовать (например)
CreateDialogIndirect
.Посмотрите на Раймонда Чена "Создание шаблона диалогового окна во время выполнения". - Вы можете встроить их в библиотеку в виде простых массивов (т.е.)
char my_dialog_resource[] = { .... };
, а затем используйте (например)CreateDialogIndirect
.Вероятно, вам нужно будет найти (или написать) утилиту, которая преобразует из.RES
файлы для.CPP
Файлы. - Вы можете отправить LIB-файл с ресурсным скриптом (
.RC
file) и соответствующий заголовочный файл.Ты тогда#include
их по мере необходимости.Вам нужно будет зарезервировать диапазон идентификаторов ресурсов для использования библиотекой, чтобы они не конфликтовали с идентификаторами основного EXE или DLL.Это то, что делает MFC при использовании в качестве статической библиотеки.Или вы можете использовать строковые идентификаторы ресурсов (это не работает дляSTRINGTABLE
ресурсы). - Ваша статическая библиотека может поставляться с отдельной ресурсной библиотекой DLL.
Другие советы
Единственное, что вам нужно сделать, чтобы использовать ресурсы (изображения, диалоги и т.д.) В статической библиотеке в Visual C ++ (2008), это включите связанный со статической библиотекой файл .res в вашем проекте.Это можно сделать в разделе "Настройки проекта / Компоновщик / Ввод / Дополнительные зависимости".
С помощью этого решения ресурсы статической библиотеки упакованы в .exe, поэтому вам не нужна дополнительная DLL.К сожалению, Visual Studio не включает файл .res автоматически, как это делается для .файл lib (при использовании функции "зависимости проекта"), но я думаю, что этот небольшой дополнительный шаг приемлем.
Я очень долго искал это решение, и теперь меня удивляет, что оно настолько простое.Единственная проблема заключается в том, что она полностью недокументирована.
Я только что прошел через это с помощью компилятора MS Visual Studio.Мы конвертировали некоторые устаревшие проекты из DLL в статические библиотеки.В некоторые из этих DLL-файлов были встроены диалоговые или строковые ресурсы.Я смог скомпилировать .RC-скрипты для этих DLL-файлов в наше основное приложение, включив их в файл RC-скрипта основного приложения с помощью механизма "TEXTINCLUDE".Я обнаружил, что проще всего сделать это, отредактировав RC-файл напрямую, но Visual Studio также предоставляет немного более "волшебный" механизм.Реализация, скорее всего, отличается в других компиляторах.
Для непосредственного управления основным RC-скриптом:
.1.В разделе "2 TEXTINCLUDE" включите заголовочный файл, который определяет идентификаторы ресурсов для вашей библиотеки.Синтаксис таков
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.Включите RC-скрипт вашей библиотеки в раздел APSTUDIO_INVOKED.Обычно это находится в нижней части файла.
#ifndef APSTUDIO_INVOKED
#include "my_first_library.rc"
#include "my_second_library.rc"
#endif
Вы также можете сделать все это автоматически через IDE visual Studio, но я обнаружил, что это не всегда применяется тогда, когда я ожидал.
- Откройте окно "Просмотр ресурсов" в Visual Studio.
- Щелкните правой кнопкой мыши на файле ресурсов вашего основного приложения и выберите "Ресурс включает в себя ..." из контекстного меню.
- В поле с надписью "Символьные директивы только для чтения" добавьте инструкции include для файлов .h, которые определяют идентификаторы ресурсов для ваших библиотек.
- В поле с надписью "Директивы времени компиляции" добавьте инструкции include для сценария .rc вашей библиотеки.
- Нажмите кнопку ок.Вы также можете вручную запустить компиляцию RC-скрипта, чтобы убедиться, что это произойдет.
Если сценарий ресурсов вашей библиотеки ссылается на какие-либо файлы на диске (текстовые файлы, файлы значков и т.д.), вам нужно убедиться, что основной проект приложения знает, где их найти.Вы можете либо скопировать эти файлы туда, где ваше приложение сможет их найти, либо добавить дополнительный путь включения в настройках компилятора.
Чтобы добавить дополнительный включаемый путь:
- Откройте диалоговое окно свойств вашего основного приложения.
- Выберите "Свойства конфигурации / Ресурсы / Общие" на левой панели навигации.
- В списке свойств введите любые соответствующие пути рядом с надписью "Дополнительные включаемые каталоги".
Я так не думаю.Статическая библиотека не имеет своего собственного ПРЕПЯТСТВИЯ.Этот код выполняется в контексте DLL или EXE, который связывает его.Вот почему все ресурсы, которые вы попытаетесь загрузить из кода статической библиотеки, будут относиться к этой прилагаемой DLL / EXE.
Однако я использовал такого рода ресурсы повторно с библиотекой DLL, поскольку у нее есть собственное адресное пространство, и вы можете вызвать LoadResource с помощью DLL HINSTANCE.
Согласно Visual Studio 2010, средства разработки от Microsoft, по-видимому, вообще не могут должным образом обрабатывать скомпилированные данные ресурсов внутри статических библиотек.
Для распространения скомпилированного файла ресурсов (a .res
файл), у вас есть два варианта:
- Распространять
.res
файлы отдельно и проинструктируйте клиентский код связать их; - Использование
cvtres
чтобы объединить несколько.res
файлы в один объект (.obj
) файл и предоставьте его отдельно.
Обратите внимание, что вы не можете использовать lib в объектных файлах, созданных с помощью cvtres
.Если предоставлено несколько объектных файлов, lib
жалуется, как будто как многократный .res
файлы были предоставлены;если предоставлен один объектный файл, lib
не жалуется, но компоновщик просто игнорирует встроенные данные ресурса в файле lib.
Возможно, есть способ заставить компоновщик прочитать и связать библиотеку с данными ресурса (с помощью некоторого параметра командной строки, манипулирования разделами и т.д.), Поскольку данные ресурса действительно доступны в библиотеке (как dumpbin
раскрывает).Пока я не нашел решения, и, если только кто-то не готов взломать инструменты разработки, ничего лучшего, чем это простое решение, вероятно, не стоит затраченных усилий.
Единственный способ отправить данные ресурса в статическую библиотеку (в данном случае, с статическая библиотека) заключается в том, чтобы распределять ресурсы отдельно и явно связывать их в клиентском коде.Используя cvtres
вы можете уменьшить количество распределенных файлов ресурсов до одного, если у вас их много.
Рекомендуемый способ - предоставить dll ресурсы вместе с вашей библиотекой.
При использовании следующего метода любой ресурс (в данном примере значок) может использоваться как неотъемлемая часть статической библиотеки, и такая библиотека может использоваться любым типом приложения, включая консольное (у которого вообще нет никакого сегмента ресурса).
- Значок преобразуется в статический массив из байт. 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()