Для чего используются файлы ресурсов

Обновлено: 04.07.2024

Я расстраиваюсь из-за каждого учебника / книги, использующего файлы ресурсов, но никогда не объясняю, почему они их используют или как они работают под капотом. Мне не удалось найти никакой информации по этому поводу. Я надеюсь, что кто-нибудь сможет создать публичный ответ, чтобы все могли найти его позже.

Некоторая соответствующая информация может включать .

В чем смысл файлов ресурсов?

Это функция, Windows или языковые компиляторы?

Почему бы вам просто не создать графический интерфейс только с помощью кода?

Существуют ли ситуации, когда можно использовать только файл ресурсов или только код?

Как записи файла ресурсов преобразуются в фактические объекты окна во время выполнения?

Что именно компилятор ресурсов делает с записями и что содержит скомпилированный формат?

Разница во времени загрузки создается с использованием файлов ресурсов, а не кода?

Что вы порекомендуете по использованию ресурсов или кода?

Любая дополнительная информация будет принята с благодарностью.

1 ответ

Традиционно при разработке Windows файлы ресурсов встраиваются непосредственно в исполняемый двоичный файл. Я не думаю, что этот конкретный подход к ресурсам широко используется за пределами Windows, но Java имеет кроссплатформенный способ делать то же самое, сохраняя ресурсы в сжатом архиве вместе с исполняемыми битами. То же самое можно увидеть в разработке под Android, где ресурсы встроены в файлы APK. Довольно распространено (за исключением двоичных файлов Windows) использовать XML для указания ресурсов.

Упаковка ресурсов в исполняемый файл имеет потенциальное преимущество в виде «однофайлового решения»; исполняемый файл содержит логику приложения и вспомогательные ресурсы, объединенные в один файл. Это может быть полезно в некоторых ситуациях, но довольно редко можно найти существенное приложение, которое можно было бы распространить в виде одного файла. В любом случае, этот метод упаковки ресурсов восходит к самым ранним дням разработки Windows; Microsoft, вероятно, использовала подходы, которые были обычными для других настольных микроплатформ того времени, которые, как я полагаю, уже давно мертвы.

Ресурсы позволяют задавать определенные вещи - обычно элементы пользовательского интерфейса - декларативно, а не в коде. Хорошая особенность - позволить этим декларативным элементам зависеть от локали. Так, например, мы можем указать главное меню для английского, одно для французского и т. Д. И выбрать нужное во время выполнения.

Проще заставить инструменты графического дизайна писать и редактировать декларативные файлы ресурсов, чем код, хотя возможно, что они будут выводить код напрямую. В любом случае обычно считается «лучше» иметь элементы пользовательского интерфейса, отделенные от логики приложения. При тщательном проектировании биты пользовательского интерфейса и другие ресурсы могут быть отредактированы после завершения самого приложения и могут поддерживаться отдельно.

Классические ресурсы Windows компилируются компилятором ресурсов в упакованный двоичный формат, вставляются в этом формате в объектные файлы (.obj, .o), а затем связываются с исполняемым файлом компоновщиком. Я подозреваю, что с современными инструментами разработки все это полностью скрыто, и вы просто видите окончательный исполняемый файл. API Windows умеют распаковывать ресурсы из исполняемого файла и превращать их в программные представления - не код как таковой, а данные в памяти, которые могут использоваться другими вызовами API.

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

Кодирование пользовательского интерфейса обычно обеспечивает большую гибкость, чем использование ресурсов; Нередко в одном приложении используются оба подхода.

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

Файлы ресурсов помещают данные в виртуальную машину в пакетной службе, но тип данных и способ их использования не являются жестко заданными. Однако существуют некоторые распространенные варианты использования.

  • Подготовка общих файлов на каждой виртуальной машине с помощью файлов ресурсов в задаче запуска.
  • Подготовка входных данных для обработки задачами.

Типы файлов ресурсов

Для создания файлов ресурсов можно использовать несколько различных вариантов. Для каждого из них используются свои методы. Процесс создания файлов ресурсов зависит от места хранения исходных данных и от того, нужно ли создавать несколько файлов.

URL-адрес контейнера хранилища

Использование URL-адреса контейнера хранилища означает, что с подходящими разрешениями вы можете получить доступ к файлам в любом контейнере хранилища в Azure.

Подписанный URL-адрес

Создайте URI подписанного URL-адреса (SAS) с подходящими разрешениями для доступа к контейнеру хранилища. Задайте срок действия и разрешения для SAS. В этом случае время начала не указано, поэтому SAS начинает действовать немедленно, а срок его действия истекает через два часа после создания.

Для доступа к контейнеру необходимо иметь разрешения Read и List , в то время как для доступа к BLOB-объектам требуется только разрешение Read .

После настройки разрешений создайте маркер SAS и отформатируйте подписанный URL-адрес для доступа к контейнеру хранилища. Используя отформатированный подписанный URL-адрес для контейнера хранилища, создайте файл ресурсов с FromStorageContainerUrl.

При необходимости можно использовать свойство blobPrefix, чтобы ограничивать загрузку только теми BLOB-объектами, имя которых начинается с указанного префикса:

Управляемое удостоверение

Создайте управляемое удостоверение, назначаемое пользователем, и назначьте ему роль Storage Blob Data Reader для вашего контейнера хранилища Azure. Затем назначьте управляемое удостоверение для своего пула, чтобы ваши виртуальные машины могли получить доступ к удостоверению. Наконец, можно получить доступ к файлам в вашем контейнере, указав идентификатор для использования пакетной службы Azure.

Открытый доступ

В качестве альтернативы созданию подписанного URL-адреса или использованию управляемого удостоверения можно включить анонимный открытый доступ с правом чтения к контейнеру и его большим двоичным объектам в хранилище BLOB-объектов Azure. Таким образом можно предоставить доступ только для чтения к этим ресурсам, не передавая ключ учетной записи и не требуя подписанного URL-адреса. Открытый доступ обычно используется для сценариев, когда определенные большие двоичные объекты нужно сделать всегда доступными для анонимного доступа на чтение. Если этот сценарий подходит для вашего решения, см. статью Настройка анонимного общего доступа с правом чтения для контейнеров и больших двоичных объектов, чтобы узнать больше об управлении доступом к данным большого двоичного объекта.

Имя контейнера хранилища (автоматическое хранение)

Вместо настройки и создания подписанного URL-адреса для доступа к данным BLOB-объектов можно использовать имя контейнера хранилища Azure. Используемый контейнер хранилища должен находиться в учетной записи хранения Azure, связанной с вашей учетной записью пакетной службы, которую еще называют учетной записью автоматического хранения. Используя контейнер автоматического хранения, можно обойти настройку и создание подписанного URL-адреса для доступа к контейнеру хранилища. Вместо этого нужно указать имя контейнера хранилища в связанной учетной записи хранения.

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

В следующем примере используется AutoStorageContainer для создания файла на основе данных в учетной записи автоматического хранения.

Как и в случае с URL-адресом контейнера хранения, можно использовать свойство blobPrefix, чтобы указать, какие BLOB-объекты будут загружены:

Один файл ресурсов из конечной точки веб-узла

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

Можно также использовать строку, определяемую в качестве URL-адреса (или сочетание строк, которые вместе создают полный URL-адрес для файла).

Если ваш файл находится в хранилище Azure, можно использовать управляемое удостоверение вместо создания подписанного URL-адреса для файла ресурсов.

Проверка подлинности управляемого удостоверения будет работать только с файлами в хранилище Azure. Для управляемого удостоверения требуется назначение роли Storage Blob Data Reader для контейнера, в котором находится файл, и она также должна быть назначена для пула пакетной службы.

Советы и рекомендации

Задачи пакетной службы Azure могут использовать файлы множеством способов, поэтому пакетная служба предоставляет различные варианты управления файлами в задачах. Приведенные ниже сценарии не являются исчерпывающими, но они охватывают несколько типичных ситуаций и содержат рекомендации.

Множество файлов ресурсов

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

И наоборот, если у каждой задачи есть много уникальных для нее файлов, скорее всего, лучшим вариантом будут файлы ресурсов. Задачи, использующие уникальные файлы, часто требуется обновлять или заменять, что нелегко выполнять с содержимым пакета приложения. Файлы ресурсов обеспечивают дополнительную гибкость при обновлении, добавлении или изменении отдельных файлов.

Количество файлов ресурсов на задачу

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

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

Прежде чем посмотреть, как применять ресурсы для локализации приложений, сначала ознакомимся с методами создания и чтения ресурсов без принятия во внимание языковых аспектов.

Создание файлов ресурсов

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

Ресурс, включающий в себя таблицу строк, может создаваться в обычном текстовом файле. В этом файле просто производится назначение строк ключам. Под ключом понимается имя, которое может использоваться в программе для получения соответствующего значения. В ключах и значениях допускается использовать пробелы.

Ниже показан пример создания простой таблицы строк:

Утилита Resgen.exe

Для создания из ***.txt файла ресурсов можно воспользоваться специальной утилитой генерации файлов ресурсов Resgen.exe. Например, ввод следующей команды:

приведет к созданию файла MyResources.resources. Сгенерированный этой утилитой файл ресурсов далее можно либо добавить в сборку как внешний файл, либо вставить в сборку DLL или ЕХЕ. Утилита Resgen также поддерживает возможность создания файлов ресурсов в формате XML с расширением .resX. Применяется она очень просто:

Выполнение этой команды приведет к созданию XML-файла ресурсов по имени MyResources.resX.

Утилита Resgen поддерживает строго типизированные ресурсы. Строго типизированный ресурс представляется в виде класса, который получает доступ к ресурсам. Для создания такого класса в утилите Resgen предусмотрена опция /str:

После опции /str должен быть указан язык, пространство имен, имя класса и имя файла исходного кода, причем именно в таком порядке.

Класс ResourceWriter

Вместо использования для создания файлов ресурсов утилиты Resgen можно написать специальную, позволяющую это делать программу. Класс ResourceWriter из пространства имен System.Resources служит для создания бинарных файлов ресурсов, а класс ResXResourceWriter — для создания файлов ресурсов на базе XML. Оба эти класса поддерживают возможность добавления изображений и любых других сериализуемых объектов. В случае применения класса ResXResourceWriter потребуется сослаться на сборку System.Windows.Forms.

В следующем примере кода демонстрируется создание объекта ResXResourceWriter по имени rw в файле Demo.resx. После создания экземпляра с помощью метода AddResource() класса ResXResourceWriter можно приступать к добавлению набора ресурсов общим объемом до 2 Гбайт. Первый аргумент в AddResource() позволяет указывать имя ресурса, а второй — значение. Ресурс изображения можно добавлять за счет применения экземпляра класса Image. Чтобы можно было использовать класс Image, необходимо сослаться на сборку System.Drawing, а также добавить директиву using для открытия пространства имен System.Drawing.

Здесь объект Image создается за счет открытия файла logo.jpg, поэтому потребуется либо скопировать этот файл изображения в каталог исполняемой программы, либо указать полный путь к нему в аргументе метода ImageToFile(). Оператор using указывает, что ресурс изображения должен автоматически уничтожаться в конце блока using.

Далее в объект ResXResourceWriter добавляются простые строковые ресурсы. В конце метод Close() класса ResXResourceWriter автоматически вызывает ResXResourceWriter.Generate() для осуществления записи ресурсов в файл Demo.resx:

Запуск этой небольшой программы приведет к созданию файла ресурсов Demo.resx с изображением logo.jpg внутри.

Использование файлов ресурсов

Добавьте в этот проект созданный ранее файл ресурсов Demo.resx, открыв в окне Solution Explorer контекстное меню и выбрав в нем пункт Add --> Add Existing Item (Добавить --> Добавить существующий элемент). По умолчанию для свойства Build Action (Действие при компоновке) этого ресурса будет установлено значение Embedded Resource (Встраиваемый ресурс), указывающее, что этот ресурс должен встраиваться в выходную сборку.

Далее в параметрах проекта (за счет выбора Application --> Assembly information (Приложение --> Информация о сборке)) следует установить в качестве значения параметра Neutral Language (Нейтральный язык) основной язык:

Установка нейтрального языка

Изменение значения этого параметра приведет к добавлению в файл assemblyinfо.cs атрибута [NeutralResourceLanguageAttribute], как показано ниже:

Установка значения для данного атрибута улучшит производительность ResourceManager, поскольку позволит ему быстрее отыскивать ресурсы для en-US, а также использовать их в качестве варианта по умолчанию. В этом атрибуте можно также указать место размещения используемого по умолчанию ресурса за счет применения второго параметра в конструкторе. С помощью перечисления UltimateResourceFallbackLocation можно указать, что он должен размещаться в главной сборке (значение MainAssembly) или же в подчиненной (значение Satellite).

После компоновки проекта можно просмотреть сгенерированную сборку утилитой ildasm и увидеть в манифесте атрибут, .mresource. Атрибут .mresource объявляет имя для ресурса в сборке. Если .mresource объявлен public (как в данном примере), это означает, что ресурс может экспортироваться из сборки и использоваться в классах других сборок. Если же .mresource объявлен private, это значит, что ресурс экспортироваться не может и доступен только в пределах данной сборки.

Для получения доступа к встроенному ресурсу используется класс ResourceManager, который находится в пространстве имен System.Resources. Конструктору этого класса в качестве аргумента можно передать имя сборки, в которой содержатся ресурсы.

В рассматриваемом примере ресурсы встроены в исполняемую сборку, поэтому во втором аргументе конструктору должен быть передан результат выполнения метода Assembly.GetExecutingAssembly(). В первом аргументе передается корневое имя ресурсов, состоящее из названия пространства имен и имени файла ресурсов, но без расширения resources. Как было показано ранее, это имя можно отобразить с помощью утилиты ildasm и просто удалить из него расширение resources. Имя можно также получить и программно с применением метода GetManifestResourceNames() класса System.Reflection.Assembly:

Для создания строго типизированного ресурса в редакторе управляемых ресурсов (Managed Resources Editor) можно изменить значение параметра Access Modifier (Модификатор доступа) с No Code Generation (Не генерировать никакой код) на Public (Общедоступный) или Internal (Внутренний). В случае установки значения Public генерируемый класс снабжается модификатором доступа public и тогда к нему возможен доступ из других сборок. При установке значения Internal генерируемый класс получает модификатор доступа internal и доступ к нему может осуществляться только изнутри сборки, в которой он находится.

Пространство имен System.Resources

Давайте кратко пройдемся по всем классам, которые содержатся в пространстве имен System.Resources и позволяют работать с ресурсами.

Класс ResourceManager

Может использоваться для получения ресурсов, относящихся к текущей культуре, из сборок или файлов ресурсов. С помощью ResourceManager можно получать сразу целый набор ресурсов для определенной культуры в виде экземпляра ResourceSet.

Класс ResourceSet

Позволяет представлять набор ресурсов для определенной культуры. При создании экземпляр ResourceSet он производит перечисление по классу, реализуя интерфейс IResourceReader, и сохраняет все ресурсы в HashTable.

Интерфейс IResourceReader

Используется в ResourceSet для перечисления ресурсов. Класс ResourceReader реализует этот интерфейс.

Класс ResourceWriter

Применяется для создания файла ресурсов и реализует интерфейс IResourceWriter.

Классы ResXResourceSet, ResXResourceReader и ResXResourceWriter

Похожи на классы ResourceSet, ResourceReader и ResourceWriter, но служат для создания не бинарного файла ресурсов, а не XML-файла .resx. Вместо того чтобы встраивать ресурс в XML-файл, они позволяют добавлять на него ссылку с помощью ResXFileRef.

Пространство имен System.Resources.Tools

Содержит класс StronglyTypedResourceBuilder, который можно использовать для создания класса из ресурса.

Но это не повод, чтобы тут же позорно сдаться, выбросив белый флаг. И я стал искать обходные пути.

Задав вопрос в форумы на нашем сайте (см. здесь), я довольно быстро получил ответ и интересную ссылку (спасибо доброму человеку с ником MaxMP ).

Вроде бы, все хорошо, но у утилиты ThemeMe оказалось два крупных недостатка:

  • она не пишет ничего, кроме манифеста
  • исходный код ее недоступен (хотя можно на него напустить Anacrino – но я не пробовал)

Описание утилиты WinRes

Эта утилита не претендует на универсальность и имеет достаточно много ограничений. Но свое дело она делает, и имхо, делает неплохо. Работает утилита только в консольном режиме, принимая параметры через командную строку – это позволяет использовать ее при построении проектов из IDE.

Командная строка

Обычный режим

Параметры командной строки:

  • appfile – путь к исполняемому файлу в формате PE
  • resfile – путь к файлу с ресурсом
  • restype – один из следующих идентификаторов, задающих тип ресурса:
  • resid – числовой идентификатор, задающий номер ресурса

Пакетный режим

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

Параметры командной строки:

  • appfile – путь к исполняемому файлу в формате PE
  • batchfile – пакетный файл

Пакетный файл представляет собой обычный текстовый файл. В каждой строке этого файла задается один ресурс в следующем виде:

  • resfile – имя файла с ресурсом, в текущей версии это имя не должно содержать пробелов
  • restype – тип ресурса, такой же как в Обычном режиме
  • resid – числовой идентификатор ресурса

Утилита понимает длинные имена файлов, в том числе содержащие пробелы. Для корректной работы утилиты такие имена надо заключать в двойные кавычки.

ПРЕДУПРЕЖДЕНИЕ

Строковые типы ресурсов с пробелами НЕ допускаются.

Использование в проектах

Для того чтобы вставлять ресурсы в исполняемый файл, необходимо иметь возможность при каждой сборке проекта выполнять необходимые действия после создания собственно исполняемого файла, то есть на этапе так называемого PostBuild.

На данный момент мне известно два способа это сделать, оба способа я узнал из документации к утилите ThemeMe .

Дополнительный проект

Все, что нужно сделать в этом случае:

Add-in

ПРИМЕЧАНИЕ

Внутреннее устройство

Структурно в программе можно выделить три основных блока:

  • блок обработки командной строки
  • блок разбора пакетного файла
  • блок записи ресурсов в файл формата PE

Первые два блока я разбирать не буду: в них нет ничего особенно интересного. Кому интересно: смотрите исходный код, он содержит довольно подробные комментарии. А вот на третьем я остановлюсь подробней.

По большому счету программа представляет собой небольшую обертку для трех вызовов функций WIN32 API, вот прототипы этих функций на языке C:

Вы можете спросить: почему используются Unicode-версии функций? Ведь это делает невозможным работу утилиты в ОС типа Windows9x? Отвечу: потому что функции xxxUpdateResource существуют только в API WindowsNT/2000/XP и недоступны в API Windows9x. А так как кодировка Unicode является родной для WindowsNT – ее использование предпочтительней.

Процесс обновления ресурсов

Процесс записи ресурсов в файл начинается с вызова BeginUpdateResource . При этом флаг bDeleteExistingResources задает режим записи: с удалением существующих ресурсов или без.

Заканчивается процесс записи вызовом EndUpdateResource . Если флаг bDiscard установлен в TRUE , то запись ресурсов отменяется, в противном случае ресурсы записываются в файл.

Между вызовами этих двух функций можно обновлять ресурсы с помощью функции UpdateResource , причем вызывать ее можно неоднократно.

Пример работы с функциями

Читайте также: