Ресурсы VC ++ в статической библиотеке - PullRequest
34 голосов
/ 10 февраля 2009

Можно ли встроить ресурсы в статическую библиотеку и использовать их повторно, просто связавшись с библиотекой?

Я в первую очередь думаю о случае, когда вы вызываете функцию из библиотеки, которая в свою очередь обращается к ресурсам.

Ответы [ 7 ]

56 голосов
/ 27 октября 2009

Единственное, что вам нужно сделать, чтобы использовать ресурсы (изображения, диалоги и т. Д.) В статической библиотеке в Visual C ++ (2008), - это включить связанный файл статической библиотеки .res ваш проект. Это можно сделать в разделе «Настройки проекта / Линкер / Ввод / Дополнительные зависимости».

В этом решении ресурсы статической библиотеки упакованы в .exe, поэтому вам не нужна дополнительная DLL. К сожалению, Visual Studio не включает файл .res автоматически, как он делает для файла .lib (при использовании функции «зависимости проекта»), но я думаю, что этот небольшой дополнительный шаг приемлем.

Я очень долго искал это решение, и теперь меня удивляет, что это так просто. Единственная проблема заключается в том, что это полностью без документов.

24 голосов
/ 10 февраля 2009

Это можно сделать, но это довольно болезненно: вы не можете сделать это, просто связавшись со статической библиотекой.

Учтите следующее: ресурсы встроены в EXE или DLL. Когда некоторый код в статической библиотеке вызывает (например,) LoadIcon, он получает ресурсы из EXE или DLL, с которыми он связан.

Итак, если вашей статической библиотеке требуются ресурсы, у вас есть несколько вариантов:

  1. Вы можете заставить библиотеку создавать их на лету, а затем использовать (например) CreateDialogIndirect. См. Рэймонда Чена "Создание шаблона диалога во время выполнения" .
  2. Вы можете встроить их в библиотеку в виде простых массивов (т.е.) char my_dialog_resource[] = { .... };, а затем использовать (например,) CreateDialogIndirect. Возможно, вам понадобится найти (или написать) утилиту, которая преобразует файлы .RES в файлы .CPP.
  3. Вы можете отправить файл LIB со скриптом ресурса (файл .RC) и соответствующим файлом заголовка. Вы тогда #include их актуально. Вам нужно зарезервировать диапазон идентификаторов ресурсов для использования в LIB, чтобы они не конфликтовали с идентификаторами основного EXE или DLL. Это то, что делает MFC при использовании в качестве статической библиотеки. Или вы можете использовать строковые идентификаторы ресурсов (это не работает для STRINGTABLE ресурсов).
  4. Ваша статическая библиотека может поставляться с отдельной библиотекой ресурсов.
10 голосов
/ 24 июня 2009

Я только что прошел через компилятор MS Visual Studio. Мы преобразовывали некоторые устаревшие проекты из DLL в статические библиотеки. В некоторых из этих DLL были встроены диалоговые или строковые ресурсы. Мне удалось скомпилировать сценарии .RC для этих библиотек DLL в наше основное приложение, включив их в RC-файл сценария основного приложения через механизм «TEXTINCLUDE». Мне было проще всего это сделать, отредактировав RC-файл напрямую, но Visual Studio также предоставляет немного более «магический» механизм. Реализация, скорее всего, отличается в других компиляторах.


Для непосредственного управления основным сценарием RC:

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-скрипт из вашей библиотеки.

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

Шаги 3 и 4 должны выполняться автоматически, но я обнаружил, что было бы надежнее просто ввести их самостоятельно, а не зависеть от компилятора сценариев ресурсов Microsoft, который позаботился бы о вещах.

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-скрипт вашей библиотеки в раздел APSTUDIO_INVOKED. Обычно это внизу файла.

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

Вы также можете делать все это автоматически через IDE Visual Studio, но я обнаружил, что это не всегда применимо, когда я ожидал.

  1. Откройте окно «Просмотр ресурсов» в Visual Studio.
  2. Щелкните правой кнопкой мыши файл ресурсов вашего основного приложения и выберите «Ресурс включает ...» в контекстном меню.
  3. В поле «Директивы символов только для чтения» добавьте операторы включения для файлов .h, которые определяют идентификаторы ресурсов для ваших библиотек.
  4. В поле «Директивы времени компиляции» добавьте операторы include для скрипта .rc вашей библиотеки.
  5. Нажмите хорошо. Вы также можете вручную запустить компиляцию сценария RC, чтобы убедиться, что это произойдет.

Если скрипт ресурсов вашей библиотеки ссылается на какие-либо файлы на диске (текстовые файлы, файлы значков и т. Д.), Вам необходимо убедиться, что основной проект приложения знает, где их найти. Вы можете скопировать эти файлы туда, где их может найти ваше приложение, или добавить дополнительный путь включения в настройках компилятора.

Чтобы добавить дополнительный путь включения:

  1. Откройте диалоговое окно свойств вашего основного приложения.
  2. Выберите «Свойства конфигурации / Ресурсы / Общие» в левой навигационной панели.
  3. В списке свойств введите любые подходящие пути рядом с «Дополнительные каталоги включения».
1 голос
/ 24 февраля 2013

Согласно Visual Studio 2010, средства разработки от Microsoft, по-видимому, вообще не могут должным образом обрабатывать скомпилированные данные ресурсов внутри статических библиотек.

Чтобы распространить скомпилированный файл ресурсов (файл .res), у вас есть два варианта:

  1. Распространите .res файлы отдельно и дайте клиентскому коду ссылку на них;
  2. Используйте cvtres для объединения нескольких файлов .res в один файл объекта (.obj) и предоставьте его отдельно.

Обратите внимание, что вы не можете загружать объектные файлы, созданные с помощью cvtres. Если предоставляется несколько объектных файлов, lib жалуется, как если бы было предоставлено несколько файлов .res; если предоставляется один объектный файл, lib не жалуется, но компоновщик просто игнорирует встроенные данные ресурса в файле lib.

Может случиться так, что есть способ заставить компоновщик прочитать и связать libbed в данных ресурса (с некоторыми опциями командной строки, манипулированием разделами и т. Д.), Поскольку данные ресурса действительно доступны в библиотека (как показывает dumpbin). Пока что я не нашел решения, и, если кто-то не захочет взломать инструменты разработки, что-то лучше этого простого решения, вероятно, не стоит усилий.

Единственный способ отправить данные ресурсов в статическую библиотеку (в данном случае с статической библиотекой) - это распределить ресурсы по отдельности и явно связать их в коде клиента. Использование cvtres может сократить количество файлов распределенных ресурсов до одного, если у вас их много.

1 голос
/ 10 февраля 2009

Я так не думаю. Статическая библиотека не имеет своего HINSTANCE. Его код выполняется в контексте DLL или EXE, который связывает его. Вот почему все ресурсы, которые вы попытаетесь загрузить из кода статической библиотеки, будут относиться к этой DLL / EXE.

Я сделал такое повторное использование ресурсов с DLL, хотя у него есть собственное адресное пространство, и вы можете вызвать LoadResource с помощью DLL HINSTANCE.

0 голосов
/ 21 сентября 2013

Когда используется следующий метод, любой ресурс (в данном примере значок) может использоваться как неотъемлемая часть статической библиотеки, и такая библиотека может использоваться любым типом приложений, включая консольное (которое не не имеет никакого сегмента ресурса вообще).

  1. Значок преобразуется в статический массив 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()
0 голосов
/ 11 февраля 2009

Рекомендуется предоставить dll с ресурсами вместе с вашей библиотекой.

...