Ресурсы VС++ в статической библиотеке
Можно ли создавать ресурсы в статической библиотеке и повторно использовать их, просто связываясь с библиотекой?
Я в первую очередь думаю о случае, когда вы вызываете функцию в библиотеке, которая, в свою очередь, обращается к ресурсам.
Ответы
Ответ 1
Это можно сделать, но это довольно болезненно: вы не можете сделать это, просто связавшись со статической библиотекой.
Рассмотрим это: ресурсы встроены в EXE или DLL. Когда какой-то код в статической библиотеке вызывает (например,) LoadIcon, он будет получать ресурсы из EXE или DLL, с которыми он связан.
Итак, если ваша статическая библиотека требует наличия ресурсов, у вас есть несколько вариантов:
- Вы можете создать библиотеку на лету, а затем использовать (например,
CreateDialogIndirect
). См. Raymond Chen "Создание шаблона диалога во время выполнения" .
- Вы можете встроить их в библиотеку в виде простых массивов (т.е.)
char my_dialog_resource[] = { .... };
, а затем использовать (например,) CreateDialogIndirect
. Вам, вероятно, потребуется найти (или написать) утилиту, которая преобразует файлы .RES
в файлы .CPP
.
- Вы можете отправить файл LIB с ресурсом script (
.RC
file) и соответствующим файлом заголовка. Вы тогда #include
их как релевантные. Вам необходимо зарезервировать ряд идентификаторов ресурсов для использования LIB, чтобы они не сталкивались с целями основного EXE или DLL. Это то, что MFC делает, когда используется как статическая библиотека. Или вы можете использовать строковые идентификаторы ресурсов (это не работает для ресурсов STRINGTABLE
).
- Ваша статическая библиотека может поставляться с отдельной библиотекой ресурсов.
Ответ 2
Единственное, что вам нужно сделать, чтобы использовать ресурсы (изображения, диалоги и т.д.) в статической библиотеке в Visual С++ (2008), - это включить связанный с статической библиотекой файл .res в вашем проекте. Это можно сделать в разделе "Настройки проекта /Linker/Input/Additional dependencies".
С помощью этого решения ресурсы статической библиотеки упаковываются в .exe, поэтому вам не нужна дополнительная DLL. К сожалению, Visual Studio не включает файл .res автоматически, как и для .lib файла (при использовании "зависимостей проекта" файла), но я думаю, что этот небольшой дополнительный шаг является приемлемым.
Я искал очень долгое время для этого решения, и теперь это удивляет меня, что все так просто. Единственная проблема заключается в том, что она полностью недокументирована.
Ответ 3
Я просто просмотрел это с помощью компилятора MS Visual Studio. Мы конвертировали некоторые устаревшие проекты из DLL в статические библиотеки. В некоторых из этих DLL были встроены диалоговые или строковые ресурсы. Мне удалось скомпилировать сценарии .RC для этих DLL в нашем основном приложении, включив их в основной файл RC script через механизм "TEXTINCLUDE". Мне было проще сделать это, отредактировав RC файл напрямую, но Visual Studio предоставляет немного более "волшебный" механизм. Реализация, скорее всего, отличается в других компиляторах.
Чтобы напрямую манипулировать основным RC script:
0,1. В разделе "2 TEXTINCLUDE" укажите файл заголовка, который определяет идентификаторы ресурсов для вашей библиотеки. Синтаксис
2 TEXTINCLUDE
BEGIN
"#include ""my_first_lib_header.h""\r\n"
"#include ""my_second_lib_header.h""\0"
END
0,2. В разделе "3 TEXTINCLUDE" включите RC script из вашей библиотеки.
3 TEXTINCLUDE
BEGIN
"#include ""my_first_library.rc""\r\n"
"#include ""my_second_library.rc""\0"
END
Шаги 3 и 4 должны выполняться автоматически, но я обнаружил, что было более надежно просто вводить их самостоятельно, а не в зависимости от компилятора Microsoft script, чтобы заботиться о вещах.
0,3. Добавьте файл заголовка с указанием ресурсов ваших библиотек в список только для чтения. Этот список обычно находится в верхней части файла.
#define APSTUDIO_READONLY_SYMBOLS
#include "my_first_lib_header.h"
#include "my_second_lib_header.h"
#undef APSTUDIO_READONLY_SYMBOLS
0,4. Включите библиотеку RC script в разделе APSTUDIO_INVOKED. Обычно это находится в нижней части файла.
#ifndef APSTUDIO_INVOKED
#include "my_first_library.rc"
#include "my_second_library.rc"
#endif
Вы также можете сделать все это автоматически через IDE визуальной студии, но я обнаружил, что это не всегда применяется, когда я ожидал этого.
- Откройте окно "Просмотр ресурсов" в Visual Studio.
- Щелкните правой кнопкой мыши файл основного ресурса приложения и выберите "Ресурсы Включает..." из контекстного меню.
- В поле с надписью "Директивы символов только для чтения" добавьте операторы include для файлов .h, которые определяют идентификатор ресурса для ваших библиотек.
- В поле с надписью "Директивы времени компиляции" добавьте операторы include для вашей библиотеки .rc script.
- Нажмите "ОК". Вы также можете вручную запустить компиляцию RC script, чтобы убедиться, что это произойдет.
Если ваш ресурс библиотеки script ссылается на любые файлы на диске (текстовые файлы, файлы значков и т.д.), вам нужно убедиться, что основной проект приложения знает, где их найти. Вы можете скопировать эти файлы туда, где ваше приложение может их найти, или вы можете добавить дополнительный путь include в настройках компилятора.
Чтобы добавить дополнительный путь include:
- Откройте диалоговое окно свойств основного приложения.
- Выберите "Свойства конфигурации/Ресурсы/Общие" в левой навигационной панели.
- В списке свойств введите любые подходящие пути рядом с "Дополнительные каталоги включения".
Ответ 4
Я так не думаю. Статическая библиотека не имеет собственного HINSTANCE. Этот код выполняется в контексте DLL или EXE, который связывает его. Поэтому все ресурсы, которые вы попытаетесь загрузить из статического библиотечного кода, будут включать в себя библиотеку DLL/EXE.
Я использовал повторное использование ресурсов с помощью DLL, хотя он имеет собственное адресное пространство, и вы можете вызвать LoadResource с помощью DLL HINSTANCE.
Ответ 5
Рекомендуемый способ - предоставить библиотеку dll ресурсы вместе с вашей библиотекой.
Ответ 6
Как и в Visual Studio 2010, средства разработки Microsoft, по-видимому, не могут должным образом обрабатывать скомпилированные данные ресурсов внутри статических библиотек вообще.
Чтобы распространять скомпилированный файл ресурсов (файл .res
), у вас есть два варианта:
- Распространять файлы
.res
по отдельности и указывать код клиента для их сопоставления;
- Используйте
cvtres
, чтобы объединить несколько файлов .res
в один объект (.obj
) и предоставить его отдельно.
Обратите внимание, что вы не можете использовать lib в объектных файлах, созданных с помощью cvtres
. Если предоставляется несколько объектных файлов, lib
жалуется, как будто было указано несколько файлов .res
; если предоставляется один объектный файл, lib
не жалуется, но компоновщик просто игнорирует встроенные данные ресурсов в файле lib.
Возможно, существует способ заставить компоновщика читать и связывать libbed в данных ресурсов (с некоторыми параметрами командной строки, манипуляциями разделами и т.д.), поскольку данные ресурсов действительно доступны в библиотека (как показывает dumpbin
). До сих пор я не нашел решения, и, если кто-то не хочет взломать инструменты для разработки, ничего лучше этого простого решения, вероятно, не стоит усилий.
Единственный способ отправить данные ресурсов в статическую библиотеку (в данном случае со статической библиотекой) - это распределить ресурсы отдельно и явно связать их в клиентском коде. Использование cvtres
может сократить количество файлов распределенных ресурсов до одного, если у вас их много.
Ответ 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;
}
-
Вместо LoadIcon используется GetIcon.
Вместо вызова:
m_hIcon = ::LoadIcon(hInstanceIcon, MAKEINTRESOURCE(pXMB->nIcon));
Затем вызовите
m_hIcon = GetIcon()