Ресурсы VC ++ в статической библиотеке

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

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


person sharkin    schedule 10.02.2009    source источник


Ответы (7)


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

Учтите следующее: ресурсы встроены в 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. Ваша статическая библиотека может поставляться с отдельной ресурсной DLL.
person Roger Lipscombe    schedule 10.02.2009
comment
Я действительно сделал ваш вариант 2, и это не так уж и сложно. Вам также необходимо создать синтаксический анализатор для файла формата .res, который хорошо документирован и не слишком ужасен. - person slicedlime; 14.08.2009
comment
@Roger Lipscombe Можете ли вы понять причину, по которой ответ Дмитрия К. не является хорошей идеей? - person DataGraham; 18.01.2012
comment
Поскольку файл .RES должен гарантировать, что он не конфликтует с идентификаторами ресурсов. У моего №3 та же проблема, за исключением того, что вы можете смягчить ее с помощью некоторой магии препроцессора. Вы не можете этого сделать с ответом Дмитрия. - person Roger Lipscombe; 18.01.2012
comment
Кроме того, я не уверен, что файл .RES гарантированно будет работать с разными версиями компоновщика, тогда как файл .LIB. - person Roger Lipscombe; 18.01.2012
comment
Ответ jeff_t объясняет, как сделать № 3 - person jnnnnn; 09.01.2015
comment
Если я хочу встроить значок в статическую библиотеку, как мне это сделать? - person Michael Haephrati; 30.03.2017

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

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

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

person Dimitri C.    schedule 27.10.2009
comment
Вероятно, исходный вопрос был не таким, но это спасло меня! +1 - person kizzx2; 13.07.2010
comment
У меня тоже отлично сработало! @ kizzx2 Я согласен, что вопрос не совсем такой, но я думаю, что он достигает конечной цели. - person DataGraham; 18.01.2012
comment
Есть идеи, как это делается в написанном от руки make-файле? Я пропускаю здесь задокументированный параметр компилятора или прагму. - person Lothar; 07.02.2012
comment
Прохладный! Спасибо большое, Дмитрий К.! - person Helge Klein; 01.05.2012
comment
Отличный ответ! Скорее всего, теперь вы столкнетесь с ошибками повторяющихся идентификаторов ресурсов при связывании (ошибка CVTRES 1100). К счастью, изменение нумерации диапазонов в файлах resource.h - небольшая плата! - person E. van Putten; 12.10.2017
comment
Я взял на себя чей-то проект с аналогичной настройкой и не понимал, что основное приложение связывается с библиотекой И файлом .res, и из-за ошибки сценария сборки файл .res не был должным образом обновлен, что привело к Главное приложение выдает странные ошибки о том, что элементы управления не найдены, хотя они явно там были. Теперь все имеет смысл - их не было в устаревшем .res файле. - person Lucky Luke; 29.12.2018
comment
Это было удивительно, но работает как шарм, спасибо :) - person Gabor Somogyi; 05.07.2021

Я только что прошел через это с компилятором 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, но я обнаружил, что это не всегда применимо, когда я ожидал.

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

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

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

  1. Откройте диалоговое окно свойств вашего основного приложения.
  2. В левой навигационной панели выберите «Свойства конфигурации / Ресурсы / Общие».
  3. В списке свойств введите подходящие пути рядом с полем «Дополнительные каталоги включения».
person jeff_t    schedule 24.06.2009
comment
Спасибо! Это спасло мне день! Хотя мне удалось опустить включение заголовков на VS2010. Это сработало после того, как я просто добавил две записи для файлов .rc. - person lowglider; 30.07.2012

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

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

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

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

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

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

person alecov    schedule 23.02.2013
comment
Доставка преобразованного в cvt-res файла obj имеет недостаток, заключающийся в том, что link.exe допускает только один файл obj, содержащий данные ресурсов, и выдает LNK1241 в противном случае. Так что, если более чем одна библиотека поставляет obj-файл, преобразованный cvt-res, это плохие новости. - person thakis; 07.06.2019
comment
link.exe имеет флаг / allarchive, который, согласно документации, принудительно включает все файлы .obj в файл lib. Но документация кажется неверной, насколько я могу судить, она принудительно включает только все файлы obj, которые имеют записи в индексе статической библиотеки. И поскольку файлы res не экспортируют символы, они не упоминаются в индексе и, следовательно, не включаются даже при передаче / allarchive. - person thakis; 07.06.2019

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

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

person Ivan Krechetov    schedule 10.02.2009

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

person Mihai Nita    schedule 11.02.2009

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

  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()
person Michael Haephrati    schedule 21.09.2013