Ассемблерное программирование в Windows

22.09.2004
Новиков Максим Глебович.

Глава 6. Понятие ресурса. Меню.

Исходный файл ресурсов — это файл с расширением «rc», в котором специальным языком описаны меню, диалоговые окна, текстовые строки, графические изображения и другая информация подобного рода. При построении программы в RadASM этот файл автоматически компилируется входящим в состав пакета MASM компилятором ресурсов RC в бинарный файл ресурсов с расширением «res», который, в свою очередь, используется линковщиком для построения исполнимого exe-файла. Так что наша задача — просто написать исходный файл ресурсов.

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

Амперсант (&) перед буквой в названии пункта меню вызывает подчеркивание следующего за ним символа при выводе на экран. Windows ищет этот же символ, когда пользователь выбирает элемент меню с использованием клавиши «Alt». Если не включить амперсант в текст, то в тексте меню не будет подчеркнутых символов, и Windows будет использовать первую букву названия пункта.

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

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

Как я уже упоминал выше, для автоматизации написания файла ресурсов можно использовать специальные создатели ресурсов, например, Resource Workshop от Borland или Resource Studio от Symantec. Однако раз уж мы используем оболочку RadASM, то воспользуемся встроенным в нее создателем ресурсов, что намного удобнее.

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

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

Найдите панель «Project». Она отображается, если напротив пункта меню «View → Progect Browser» стоит галочка. По умолчанию панель отображается вдоль правой части окна оболочки RadASM. В верхней части панели мы видим несколько кнопок, сгруппированных в трех группах.

Самая последняя группа — это кнопки циклического перехода по папкам, указанным в «Option → File Brawser». По умолчанию там содержатся папка проекта, папка ассемблера, папка включаемых файлов ассемблера и папка RadASM.

Центральная группа кнопок относится к режиму просмотра самой папки проекта. Первая кнопка собственно включает режим просмотра папки проекта, вторая включает фильтр, выключающий из окна просмотра неактуальные файлы (фильтр настраивается в «Option → File Brawser»), а последняя кнопка осуществляет выход из текущей папки на 1 уровень вверх.

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

Теперь попробуем создать наше меню, используя встроенные средства RadASM. Откроем наш проект, и выберем «Project → Add New → Menu». В окне введем имя файла, в котором будет храниться вспомогательная информация для меню, и нажмем кнопку «Сохранить». Появится окно конструктора меню.

В окне конструктора разделе «Menu» из поля «Name» уберем предложенное имя меню, потому что к нашему меню мы будем обращаться по его идентификатору, который мы введем в поле «MenuID». Это сэкономит нам несколько байт. В нашем исходнике мы обращаемся к меню по идентификатору «1», поэтому впишем туда единицу. В поле «ItemStartId» укажем «0», чтобы идентификаторы пунктов меню, которые посылаются в программу при их выборе, присваивались пунктам, начиная с нуля. Этот параметр не столь важен, поскольку в данном случае мы будем вписывать идентификаторы пунктов сами.

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

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

В поле «ItemID» каждого пункта мы впишем цифру идентификатора, соответствующую той, которую мы присваивали пункту, когда писали файл вручную. Для пункта меню «Файл», имеющем подменю, идентификатор обнулим — он там не нужен. Если мы хотим вставить разделительную черту в меню, то создадим новый пункт, уберем всю информацию из его полей, а в поле «Caption» вставим дефис.

В итоге у вас должно получиться нечто подобное:

Если мы нажмем кнопку «Export», то сгенерированный исходник можно будет увидеть в выходном окне RadASM'а. Убедитесь, что он ничем не отличается от нашего, написанного вручную. Разве что в пункте «Редактировать» отсутствует идентификатор, поскольку он равен нулю. Но это нам не помешает, при компиляции компилятор корректно подставит нулевой идентификатор.

Теперь нажимаем «OK». В окне проекта появится новый файл c расширением «mnu». При его запуске у нас будет открываться окно конструктора меню. Теперь посмотрим в наш старый файл «rc» . Мы увидим, что в его начало дописана строка:

#include "Res/ххххххMnu.rc

где хххххх — имя файла меню.

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

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

В итоге мы получим тот же самый исполнимый файл.

Глава 7. Акселераторы (горячие клавиши).

Теперь давайте добавим еще один ресурс, тесно связанный с меню — акселераторы (горячие клавиши), сочетания которых мы прописали в меню. Для этого потребуются минимальные добавления в наш проект. Прежде всего, создайте новый ресурс — таблицу акселераторов. Для этого выбираем в меню RadASM'a «Project → Accelerator».

В открывшемся окне очищаем поле «Name», в поле «ID» вводим идентификатор таблицы акселераторов — «2», и создаем 3 акселератора для трех, спрятанных внутри нашего меню, пунктов. Так же, как и везде, впишем только идентификаторы, поля имен оставим пустыми. Выберем латинскую букву, соответствующую кнопке с указанной нами русской буквой, и двойным щелчком поставим галочку напротив клавишей, в сочетании с которыми каждый акселератор будет работать. Нажимаем «ОК». Файл с новым ресурсом создается в папке ресурсов, а в наш файл ресурсов включается на него ссылка.

Дополним файл «user32.inc» двумя новыми функциями:

extrn __imp__LoadAcceleratorsA@8:dword
extrn __imp__TranslateAcceleratorA@12:dword

LoadAccelerators     equ __imp__LoadAcceleratorsA@8
TranslateAccelerator equ __imp__TranslateAcceleratorA@12

Теперь дополним код программы. В сегмент неинициализированных данных после определения структуры MSG впишем:

h_accel dd ? ;Идентификатор массива акселераторов.

В сегменте кода после создания окна оператором «call CreateWindowEx» добавим строчку сохранения его идентификатора в стеке:

push eax ;Сохраним идентификатор окна

а перед циклом обработки сообщений перед оператором «mov edi, offset msg_» вставим код подключения таблицы акселераторов:

    push 2                ;Идентификатор таблицы акселераторов
    push esi              ;Идентификатор нашей программы
    call LoadAccelerators ;Загрузить таблицу акселераторов
    mov h_accel,eax       ;Сохранить идентификатор загруженного 
                          ;массива акселераторов 

и строчку восстановления из стека ранее сохраненного идентификатора нашего окна:

pop esi ;Восстановим идентификатор окна

Теперь дополним цикл обработки сообщений. Между функцией принятия сообщения и проверки его на ноль

    call GetMessage
    test eax,eax
    jz exit_msg_loop

и функцией преобразования сообщения

    push edi
    call TranslateMessage

Вставим проверку сообщения на сообщение от акселератора:

push edi                  ;Арес структуры с сообщением
push h_accel              ;Идентификатор массива акселераторов
push esi                  ;Идентификатор нашего окна
call TranslateAccelerator ;Преобразовать сообщение от акселератора в вид
                          ;сообщения от меню и послать его.
test eax,eax              ;Сообщение от акселератора приходило?
jnz message_loop          ;Если да, то на начало цикла обработки сообщений
                          ;и ждем прихода преобразованного сообщения.

Теперь нам осталось дополнить только оконную процедуру. После команды забора идентификатора пункта меню

mov eax,wp_wParam

вставляем команду:

cwde ;Преобразовать слово в двойное слово (обнулим старшее слово,
     ;где сидит мешающий нам идентификатор сообщения от акселератора).

Всё. Теперь можем компилировать программу, и проверить ее работоспособность. Не открывая меню, мы можем теперь выполнять прописанные в нем пункты, нажимая клавиатурные комбинации.

[Вернуться в начало]
Глава 5
Глава 8 [Оставить отзыв в гостевой]
Hosted by uCoz