Иконка приложения на ассемблере

Метки:  , , , ,

Затрагиваемая нами сегодня тема, пожалуй, является не такой уж и заметной в общем процессе освоения программирования на языке ассемблера под Windows. Однако возможность обеспечения разрабатываемого приложения различного рода визуальными элементами, если хотите украшательствами, заложена в саму концепцию большинства современных операционных систем с графическим интерфейсом. Сегодня мы обсудим такую несущественную деталь как иконка приложения. Согласитесь, в сущности то мелочь, а приятно посмотреть? :) В очередной раз как-то на днях увидел элегантную иконку напротив имени какой-то программы в проводнике, и.. понравилось. Эстетика, не более, никакого особенного функционала в себе иконки не несут, однако восприятие графического окна приложения как то сразу меняется. Ведь, если разобраться, в современном программировании наличие собственного значка у приложения является уже своего рода хорошим тоном, так сказать распространенной практикой. Ну, а поскольку для меня программирование под Windows является областью пока еще новой, поэтому решил разобраться, как же собственные приложения, написанные на ассемблере, оснащать иконками. Иконка приложения, о которой идет речь, видна в верхнем левом углу главного окна программы, отображается в проводнике напротив имени файла и её так же можно наблюдать в миниокне приложения на панели задач, по сути, она доступна через стандартные системные механизмы, поэтому появляется везде, где её запрашивают и выводят.

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

Как мы видим, изменений исходного шаблона немного и связаны они с модификацией кода загрузки иконки (строка 14) и добавлением секции ресурсов (строки: 90-104), описывающей данные пиктограммы выходного исполняемого файла.
Начнем изучение изменений с самого начала листинга. У нас еще с первого примера в исходном коде присутствует функция загрузки иконки LoadIcon, которую можно увидеть в строке 14. В начальном шаблоне функция LoadIcon использовалась для загрузки стандартной системной иконки. Однако, в случае с пользовательской пиктограммой (иконкой) стоит более внимательно изучить документацию по функции, дабы понять, что нам необходимо скорректировать входные параметры. В частности, параметр hInstance должен принять значение дескриптора модуля, чей исполняемый файл содержит в своей ресурсной секции байт-код иконки. Очевидно, что это значение у нас поступает в виде выходного параметра функции GetModuleHandle (строка 12) и на момент вызова LoadIcon содержится в переменной wc.hInstance (или в регистре eax). Затем, второй входной параметр lpIconName функции LoadIcon должен принимать значение идентификатора загружаемого ресурса (иконки), вот тут то и начинается самое интересное. Что такое значение 2 идентификатора ресурса и ресурс вообще? Это термины отсылают нас к понятию секции ресурсов.

Секция ресурсов

Честно говоря, когда я впервые столкнулся с описанием секции ресурсов, я мало чего из всего этого понял :) Поэтому, думаю будет более чем уместным сосредоточиться на данном материале основательнее, поскольку описание ресурсов тут является основным краеугольным камнем понимания всей стратегии.

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

Отличаются от классических данных тем, что:

  1. Представлены в виде многоуровневого двоичного дерева, отсортированного по определенным правилам с целью ускорения поиска;
  2. Размещаются в виде отдельной секции результирующего исполняемого PE-файла;
  3. Доступны через специальные идентификаторы ресурсов;
  4. Статичны, то есть не подразумевают изменения;

Для описания ресурсов используется целая секция, которая начинается со специализированной директивы section, и определяемых после неё различных опции, таких как название (.rsrc), тип (resource data), атрибуты доступа (readable).

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

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

Наименование Определение
Макроопределение directory Каталог ресурсов. Основная директория, в которой перечисляются (описываются) все ресурсы, содержащиеся в секции ресурсов. Макрос directory необходимо указывать в самом начале секции ресурсов. После макроса directory следуют пары, разделенные запятыми: первое в паре — идентификатор (тип) ресурса, второе — имя поддиректории (метка) ресурса, содержащей непосредственно уже сам ресурс. Константы RT_ICON и RT_GROUP_ICON являются предопределенными системными константами, определяющими тип ресурса (в данном случае - иконка).
Макроопределение resource Точка входа каталога. Поддиректория, содержащая уже непосредственно сам ресурс. Первым параметром указывается имя поддиректории (оно было перечислено выше в парах параметров макроса directory), затем следуют группы из трех параметров ресурса: идентификатор ресурса (выбирается разработчиком, используется для доступа к ресурсу в коде приложения), язык ресурса, имя (метка) ресурса. К одному имени поддиректории может быть привязано несколько трехпараметральных групп. В нашем случае используется константа LANG_NEUTRAL, потому как используемые нами ресурсы (иконка) не нуждаются в языковом определении.
Макроопределение icon Данные ресурсов. Завершает у нас секцию ресурсов в строке 103 макрос icon, который, по логике вещей, призван организовать размещение выбранного типа ресурса (иконки приложения) в секции ресурсов. В параметрах макроса указаны: имя ресурса группы иконок (main_icon), имя ресурса отдельной иконки (icon_data), имя файла с иконкой (icon.ico). Понятное дело, что при запуске исполняемого файла нам сам файл иконки уже будет не нужен, однако на стадии сборки исполняемого файла компилятор должен откуда то получить данные, дабы сформировать соответствующую поддиректорию секции ресурсов.
Тем не менее, объяснение определений по отдельности не дает общего понимания того, почему именно директивы и макроопределения следуют именно этим, а не каким-либо иным порядком, почему именно в такой последовательности описывается секция ресурсов? Думаю, за разобраться в вопросе поможет изучение структуры PE-файла.

Именование макроса icon порождает дополнительные вопросы. Получается, для объявления разнообразных по типу ресурсов существуют соответствующие макроинструкции, которые призваны обеспечить размещение того или иного типа ресурса в секции ресурсов. Именно они должны использоваться в секции в каждом конкретном случае, при использовании того или иного типа ресурса. Эти макроопределения, поддерживаемые FASM в секции ресурсов, можно найти в файле \INCLUDE\MACRO\resource.inc. Это такие макросы, как: bitmap, icon, cursor, menu, menuitem, menuseparator, dialog, dialogitem, enddialog и другие.
В нашем примере подразумевается, что файл icon.ico размещается в той директории, что и исходный код программы, однако нет никаких проблем с выбором произвольного местоположения внешних данных для секции ресурсов, поэтому, если у Вас вдруг возникло желание положить иконки в отдельную поддиректорию, то необходимо будет соответствующим образом подкорректировать параметр.
Но, вернемся к нашему примеру. В итоге всех наших манипуляций, подключенную нами иконку можно разглядеть в левом верхнем углу основного окна приложения:

application icon

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

app explorer icon

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

Формат и размерность иконки

Тут возникает резонный вопрос, а почему в качестве формата иконок выбран .ico? Честно говоря, за время, проведенное в работе с операционными системами Windows, у меня сложилась устойчивая ассоциация, что стандартом де-факто для иконок (пиктограмм) в этой системе непременно должен являться указанный формат. И действительно, как свидетельствует нам Wikipedia:

ICO (Windows icon) — формат хранения файлов значков в Microsoft Windows.

Однако, развитие видов представления в различных областях ОС способствовало увеличению необходимых разрешений иконок. Теоретически, размер иконки приложения может быть любым, однако разработчиком рекомендованы следующие стандартные размеры: 16×16, 24×24, 32×32, 48×48, 256×256. В принципе, в данном примере я нашел в Сети первый попавшийся .ico-файл размером 16х16 и использовал его в примере.

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

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

Размер файла иконки

Потом я заметил, что моя иконка занимает на диске 2.5 Кб, а сам скомпилированный исполняемый файл тестовой программы - всего-лишь 2 Кб. Вот это да, оказывается что иконка приложения у меня "весит" больше нежели сама программа! Реалии современного программирования под Windows таковы, что подобные размеры никого уже не удивляют, ведь если сам инсталлятор уже занимает несколько сотен мегабайт, то какое там дело до размера иконки? Но я с детства привык к экономному использованию ресурсов системы, поэтому у меня возникло желание оптимизировать иконку по размеру.
В этом случае можно попробовать:

  • Поделиться:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *