Ассемблер для Windows

       

В данном разделе мы займемся изучением языка ресурсов



I

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

Начнем с перечисления наиболее употребляемых ресурсов.

  1. Иконки.
  2. Курсоры.
  3. Битовая картинка.
  4. Строка.
  5. Диалоговое окно.
  6. Меню.
  7. Акселераторы.
  8. Вот наиболее распространенные ресурсы. Надо только иметь в виду, что такой ресурс, как диалоговое окно, может содержать в себе управляющие элементы, которые также должны быть описаны, но в рамках описания окна. Но об этом поговорим несколько позже.

    1. Иконки. Могут быть описаны в самом файле ресурсов, либо храниться в отдельном файле *.ico. Рассмотрим последний случай. Вот файл ресурсов resu.rc:

    #define IDI_ICON1 1

    IDI_ICON1 ICON "Cdrom01.ico"

    Как видите, файл содержит всего две значимых строки. Одна строка определяет идентификатор иконки, вторая - ассоциирует идентификатор с файлом "Cdrom01.ico". Оператор define является Си-оператором препроцессора. Как вы увидите в дальнейшем, язык ресурсов очень напоминает язык Си. Откомпилируем текстовый файл resu.rc: RC resu.rc. На диске появляется объектный файл resu.res. При компоновке укажем этот файл в командной строке:

    LINK /subsystem:windows resu.obj resu.res

    У читателя возникает вопрос: как использовать данный ресурс в программе? Здесь все просто: предположим, что мы хотим установить новую иконку для окна. Вот фрагмент программы, который устанавливает стандартную иконку для главного окна.

    PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX



    А вот фрагмент программы для установки иконки, указанной в файле ресурсов:




    PUSH 1 ; идентификатор иконки (см. файл resu.rc) PUSH [HINST] ; идентификатор процесса CALL LoadIconA@8 MOV [WC.CLSHICON], EAX

    Компилятор ресурсов brcc32.exe ( из пакета TASM32) допускает включение иконки в текст проекта. В этом случае проект будет иметь следующий вид (Рисунок 2.3.1):

    #define IDI_ICON1 1

    IDI_ICON1 ICON { '00 00 01 00 02 00 20 20 10 00 00 00 00 00 E8 02' '00 00 26 00 00 00 10 10 10 00 00 00 00 00 28 01' '00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00' '00 00 01 00 04 00 00 00 00 00 80 02 00 00 00 00' '00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00' '00 00 00 00 BF 00 00 BF 00 00 00 BF BF 00 BF 00' '00 00 BF 00 BF 00 BF BF 00 00 C0 C0 C0 00 80 80' '80 00 00 00 FF 00 00 FF 00 00 00 FF FF 00 FF 00' '00 00 FF 00 FF 00 FF FF 00 00 FF FF FF 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 77 78 33 AA 00 00 00 00 00 00 00 00' '00 00 07 7F 77 78 33 AA 77 80 00 00 00 00 00 00' '00 0F F7 F7 77 78 33 AA 77 C8 60 00 00 00 00 00' '00 FF FF 7F 77 78 33 AA 78 C6 66 00 00 00 00 00' '0F FF FF F7 77 78 38 A7 7C 86 66 60 00 00 00 00' '77 FF FF 7F 77 78 37 A7 8C 66 66 77 00 00 00 07' '87 7F FF F7 F7 78 37 A7 C8 66 67 77 70 00 00 08' '78 77 FF FF 77 78 3A A8 C6 66 77 77 E0 00 00 87' '87 87 7F FF F7 78 3A AC 86 67 77 EE EE 00 00 78' '78 78 77 FF 7F 78 3A 8C 66 77 EE EE BB 00 07 87' '87 87 87 7F F7 78 3A C8 67 7E EB BB BA A0 08 78' '78 78 78 77 F8 88 88 C6 7E BB BB AA AA A0 07 87' '87 87 87 87 88 00 00 88 BB BA AA A3 33 30 08 78' '78 78 78 78 80 8F F8 08 33 33 33 DD DD D0 08 88' '88 88 88 88 80 FF FF 08 5D 5D 5D 5D 5D 50 05 D5' 'D5 D5 D5 D5 80 FF FF 08 88 88 88 88 88 80 0D DD' 'DD 33 33 33 80 8F F8 08 87 87 87 87 87 80 03 33' '3A AA AB BB 88 00 00 88 78 78 78 78 78 70 0A AA' 'AA BB BB E7 6C 88 88 8F 77 87 87 87 87 80 0A AB' 'BB BE E7 76 8C A3 87 7F F7 78 78 78 78 70 00 BB' 'EE EE 77 66 C8 A3 87 F7 FF 77 87 87 87 00 00 EE' 'EE 77 76 68 CA A3 87 7F FF F7 78 78 78 00 00 0E' '77 77 66 6C 8A A3 87 77 FF FF 77 87 80 00 00 07' '77 76 66 8C 7A 73 87 7F 7F FF F7 78 70 00 00 00' '77 66 66 C8 7A 73 87 77 F7 FF FF 77 00 00 00 00' '06 66 68 C7 7A 83 87 77 7F FF FF F0 00 00 00 00' '00 66 6C 87 AA 33 87 77 F7 FF FF 00 00 00 00 00' '00 06 8C 77 AA 33 87 77 7F 7F F0 00 00 00 00 00' '00 00 08 77 AA 33 87 77 F7 70 00 00 00 00 00 00' '00 00 00 00 AA 33 87 77 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF F0' '0F FF FF 80 01 FF FE 00 00 7F FC 00 00 3F F8 00' '00 1F F0 00 00 0F E0 00 00 07 C0 00 00 03 C0 00' '00 03 80 00 00 01 80 00 00 01 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 80 00 00 01 80 00 00 01 C0 00 00 03 C0 00' '00 03 E0 00 00 07 F0 00 00 0F F8 00 00 1F FC 00' '00 3F FE 00 00 7F FF 80 01 FF FF F0 0F FF 28 00' '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00' '00 00 C0 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80' '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80' '00 00 80 80 80 00 C0 C0 C0 00 00 00 FF 00 00 FF' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF' '00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00' '08 87 3A 80 00 00 00 0F F8 87 32 CC 60 00 00 08' 'F8 87 32 C6 68 00 00 87 8F 87 2C 66 86 00 08 78' '78 87 2C 68 AA A0 07 87 87 70 08 2A A2 20 08 78' '78 0F F0 II 15 50 05 51 II 0F F0 87 87 80 02 2A' 'A2 80 08 78 78 70 0A AA 86 C2 78 87 87 80 00 68' '66 C2 78 F8 78 00 00 86 6C 23 78 8F 88 00 00 06' 'CC 23 78 8F F0 00 00 00 08 A3 78 80 00 00 00 00' '00 00 00 00 00 00 F8 IF 00 00 E0 07 00 00 C0 03' '00 00 80 01 00 00 80 01 00 00 00 00 00 00 00 00' '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' '00 00 80 01 00 00 80 01 00 00 C0 03 00 00 E0 07' '00 00 F8 1F 00 00' }



    Рисунок 2.3.1. Пример файла ресурсов с включенным туда кодом иконки.

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

    #define IDI_ICON1 1 #define IDI_CUR1 2

    IDI_ICON1 ICON "Cdrom01.ico" IDI_CUR1 CURSOR "4way01.cur"

    А вот фрагмент программы, вызывающей иконку и курсор.

    ;----------иконка окна PUSH 1 ; идентификатор иконки PUSH [HINST] CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ; ----------курсор окна PUSH 2 ; идентификатор курсора PUSH [HINST] CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX

    Как и для иконки, программа brcc32.exe обрабатывает определение курсора в тексте файла ресурсов.

    3. Битовые картинки (*.BMP). Здесь ситуация аналогична двум предыдущим. Вот пример файла ресурсов с битовой картинкой.

    #define ВIТ1 1

    BIT1 BITMAP "PIR2.BMP"

    4. Строки. Чтобы задать строку или несколько строк используется ключевое слово STRINGTABLE. Ниже представлен текст ресурса, задающий две строки. Для загрузки строки в программу используется функция LoadString (см. ниже). Строки, задаваемые в файле ресурсов, могут играть роль констант.

    #define STR1 1 #define STR2 2

    STRINGTABLE { STR1, "Сообщение" STR2, "Версия 1.01" }

    5. Диалоговые окна. Диалоговые окна являются наиболее сложными элементами ресурсов. В отличие от ресурсов, которые мы до сих пор рассматривали, для диалога не задается идентификатор. Обращение к диалогу происходит по его имени (строке).

    #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L

    DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX CAPTION "Пример диалогового окна" FONT 8, "Arial" { }

    Как видим, определение диалога начинается со строки, содержащей ключевое слово DIALOG. В этой же строке далее указывается положение и размер диалогового окна. Далее идут строки, содержащие другие свойства окна. Наконец идут фигурные скобки. В данном случае они пусты. Это означает, что на окне нет никаких управляющих элементов. Тип окна, а также других элементов определяется константами, которые мы поместили в начале файла. Эти константы стандартны, и для языка Си хранятся в файле RESOURCE.H. Мы, как и раньше, все константы будем определять непосредственно в файле ресурсов. Обращаю ваше внимание, что константы определяются согласно нотации языка Си.



    Прежде чем разбирать пример на Рисунок 2.3.2, рассмотрим особенности работы с диалоговыми окнами. Диалоговое окно очень похоже на обычное окно. Так же как обычное окно, оно имеет свою процедуру. Процедура диалогового окна имеет те же параметры, что и процедура обычного окна. Сообщений, которые приходят на процедуру диалогового окна, гораздо меньше. Но те, которые у диалогового окна имеются, в основном совпадают с аналогичными сообщениями для обычного окна. Только вместо сообщения WM_CREATE приходит сообщение WM_INITDIALOG. Процедура диалогового окна может возвращать либо нулевое, либо ненулевое значение. Ненулевое значение должно возвращаться в том случае, если процедура обрабатывает (берет на себя обработку) данное сообщение, и ноль - если предоставляет обработку системе.

    Отличия в поведении диалогового окна от обычного окна легко объяснить. Действительно, если Вы создаете обычное окно, то все его свойства определяются тремя факторами: свойствами класса, свойствами, определяемыми при создании окна, реакцией процедуры окна на определенные сообщения. При создании диалогового окна все свойства заданы в ресурсах. Часть этих свойств задается, когда при вызове функции создания диалогового окна (DialogBox, DialogBoxParam и др.) неявно вызывается функция CreateWindow. Остальная же часть свойств определяется поведением внутренней функции, которую создает система при создании диалогового окна. Если с диалоговым окном что-то происходит, то сообщение сначала приходит на внутреннюю процедуру, а затем вызывается процедура диалогового окна, которую мы создаем в программе. Если процедура возвращает 0, то внутренняя процедура продолжает обработку данного сообщения, если же возвращается ненулевое значение, внутренняя процедура не обрабатывает сообщение. Вот, вкратце, как работают механизмы, регулирующие работу диалогового окна. Рассмотрите теперь программу на Рисунок 2.3.2, ниже будет дано ее разъяснение.

    // файл dial.rc // определение констант #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L



    // идентификаторы #define STR1 1 #define STR2 2 #define IDI_ICON1 3

    // определили иконку IDI_ICON1 ICON "ico1.ico"

    // определение диалогового окна DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX CAPTION "Пример диалогового окна" FONT 8, "Arial" { }

    // определение строк STRINGTABLE { STR1, "Сообщение" STR2, "Версия программы 1.00" }

    ; файл dial.inc ; константы

    ; сообщение приходит при закрытии окна WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SETICON equ 80h ; прототипы внешних процедур EXTERN MessageBoxA@16:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN LoadStringA@16:NEAR EXTERN LoadIconA@8:NEAR EXTERN SendMessageA@16:NEAR ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS

    ;файл dial.asm .386P

    ; плоская модель .MODEL FLAT, stdcall include dial.inc

    ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;--------------------------------------------------

    ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 BUF1 DB 40 dup(0) BUF2 DB 40 dup(0) _DATA ENDS

    ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST],EAX ;------ ; загрузить строку PUSH 40 PUSH OFFSET BUF1 PUSH 1 PUSH [HINST] CALL LoadStringA@16 ; загрузить строку PUSH 40 PUSH OFFSET BUF2 PUSH 2 PUSH [HINST] CALL LoadStringA@16 ;------------------------------------------------------------ PUSH 0 ; MB_OK PUSH OFFSET BUF1 PUSH OFFSET BUF2 PUSH 0 CALL MessageBoxA@16 ; создать диалоговое окно PUSH 0 PUSH OFFSET WNDPROC ; процедура окна PUSH 0 PUSH OFFSET PA ; название ресурса (DIAL1) PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL KOL: PUSH 0 CALL ExitProcess@4 ;--------------------------



    ; процедура диалогового окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CН] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI ;----- CMP DWORD PTR [EBP+ОСН], WM_CLOSE JNE L1 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+ОСН], WM_INITDIALOG JNE FINISH ; загрузить иконку PUSH 3 ; идентификатор иконки PUSH [HINST] ; идентификатор процесса CALL LoadIconA@8 ; установить иконку PUSH EAX PUSH 0 ; тип иконки (маленькая) PUSH WM_SETICON PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX, 0 RET 16 WNDPROC ENDP _TEXT ENDS END START

    Рисунок 2.3.2. Демонстрация использования простых ресурсов.

    Рассмотрим теперь, как работает эта программа.



    1. Файл ресурсов должен быть Вам понятен, так как все используемые там ресурсы были подробно рассмотрены ранее. Замечу только, что файл ресурсов содержит сразу несколько элементов. При этом все ресурсы, кроме диалогового окна, должны иметь идентификатор. Для диалогового окна определяющим является его название, в нашем случае это DIAL1.
    2. Перед тем как вызвать диалоговое окно, демонстрируется то, как нужно работать с таким ресурсом, как строка. Как видите, это достаточно просто. При помощи функции LoadString строка загружается в буфер, после чего с ней можно работать, как с обычной строкой.
    3. Вызов диалогового окна достаточно очевиден, так что перейдем сразу к процедуре диалогового окна. Начнем с сообщения WM_INITDIALOG. Это сообщение, как и сообщение WM_CREATE для обычного окна, приходит один раз при создании окна. Это весьма удобно для проведения какой-то начальной инициализации. Мы используем это для определения иконки диалогового окна. В начале загружаем иконку, а далее посылаем сообщение установить иконку для данного окна (WM_SETICON). Вторым сообщением, которое мы обрабатываем, является WM_CLOSE. Это сообщение приходит, когда происходит щелчок мышью по крестику в правом верхнем углу экрана. По получении этого сообщения выполняется функция EndDialog, что приводит к удалению диалогового окна из памяти, выходу из функции DialogBoxParamA и в конечном итоге - к выходу из программы.


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

      31

      Лично я предпочитаю использовать редактор ресурсов из пакета Borland C++ 5.00, либо простой текстовый редактор.


      Содержание раздела