21 Декабря 2011
Не буду рассказывать про то, что такое файл ресурсов, для чего нужен, и как его подключить к проекту (кто не в курсе этих вопросов – дальше можно читать только для общего образования), а сразу опишу проблему. Предположим, у нас есть файл, скажем, resources.rc, в котором содержится все необходимое проекту добро и успешно используется под Windows с помощью нехитрого набора функций LoadResource, FindResource и т.п. Задача: собрать этот же проект под Linux, в процессе подключить ресурсы к исполняемому файлу и, естественно, прочитать их в своей программе.
При сборке проекта в Windows файл определения ресурсов (.rc) компилируется в двоичный вид (.res) и на этапе компоновки подключается к exe-файлу, фактически прописываясь после запуска где-то в оперативной памяти. Напомню, что определить местоположение искомого ресурса позволяет функция:
Технология отлажена, сомнению не подвергается. Уже на этом этапе в Linux возникает несколько вопросов. Во-первых, чем откомпилировать исходный файл ресурсов в двоичный формат? Во-вторых, как подключить его к формируемому исполняемому файлу проекта? И, в-третьих, как найти и прочитать в файле нужный ресурс?
Шаг 1. Компилируем файл ресурсов
Самый простой шаг. С данной задачей вполне справляется утилита wrc (Wine Resource Compiler), которая умеет компилировать файл ресурсов в 16- или 32-разрядный двоичный формат. Применение простое:
Шаг 2. Подключение файла ресурсов
У нас уже имеется скомпилированный файл ресурсов, представленный в двоичном виде, однако, как это ни странно, пока что он представляет собой всего лишь набор символов. В таком виде его даже можно будет включить в сборку, но найти его в памяти во время работы программы будет проблематично, и наши ресурсы так и останутся болтаться внутри бинарника космическим мусором. На помощь приходит утилита objcopy, которая умеет преобразовывать объектные (и исполняемые) файлы в разные "правильные" форматы, которые потом уже можно будет прилинковать. С ее помощью преобразуем наш двоичный файл ресурсов (resources.res) в объектный (resources.o). Включаем магию:
Говорим, что на входе у нас двоичный файл (-I binary), на выходе хотим получить файл в формате объектных файлов на 32-разрядной платформе x86 (-O elf32-i386), причем он должен выглядеть как "исполняемый" файл (-B i386). В таком виде его уже можно будет подключать к проекту:
Я использую cmake, поэтому в мой CMakeLists.txt добавил несколько строчек:
На этом почти вся магия кончается. Остается невыясненным только один вопрос: как найти ресурсы в готовом executable файле? Компоновщик, выполнив свою работу, великодушно оставил в собранном файле несколько меток:
По этим меткам теперь можно прочитать содержимое файла ресурсов.
Шаг 3. Поиск и чтение ресурсов в программе
Для начала определим переменные, соответствующие вышеописанным меткам компоновщика:
При определении использую тип char, поскольку в моем проекте в ресурсах хранятся только ссылки на текстовые файлы в формате xml. Дополнительно определю привычные типы, не известные Linux, исключительно для удобства работы
Для применения FindResource не хватает только hModule. Под Windows в эту переменную будет записан хендл текущего модуля, в Linux – адрес начала блока ресурсов в нашем файле
Все готово для поиска нужного ресурса, кроме того, что в Linux отсутствует соответствующая функция, которую придется изготовить самостоятельно. Поскольку все ресурсы, которые были подключены к нашему проекту, сохранены в формате файла res, неплохо было бы для начала узнать его формат. Читаем в MSDN, что в основе формата лежит структура RESOURCEHEADER
Под Linux я также переопределил типы
и добавил константу, обозначающую тип ресурса: в моем случае это простые данные
Итак, в файле ресурсов после структуры RESOURCEHEADER следуют данные, размер которых составляет DataSize байт. Этой информации почти достаточно для того, чтобы распотрошить его полностью. Последняя засада, которая поджидает на пути к цели: каждый ресурс, включающий структуру и сами данные, выровнен по двойному слову. То есть, в случае если его размер в байтах не кратен четырем, в хвост к нему дописывается недостающее количество нулевых символов. У меня получилась такая функция для поиска ресурса типа RC_DATA по заданному имени
Теперь функция FindResource вернет нам адрес начала искомого ресурса, который будем использовать далее
И более ничто не мешает прочитать содержимое памяти в свою переменную
Resource::~Resource()
Безусловно, рассмотренный пример использования в Linux привычных для Windows файлов определения ресурсов довольно примитивен, поскольку "заточен" только под один тип данных (RT_RCDATA). Однако вполне представляет собой основу детской пирамидки с уже одетым на нее небольшим колечком, поверх которого можно набросать еще много всего.
< Предыдущая |
---|