Среда, 16.07.2025, 18:10 Приветствую Вас Гость

On-line: Книги, учебники, статьи

Главная | Регистрация | Вход | RSS

Глава 3. Понятие ресурса. Редакторы и трансляторы ресурсов(3)

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

Это очень удобно и быстро. Таблица акселераторов является ресурсом, имя которого должно совпадать с именем того меню (ресурса), пункты которого она определяет.

Вот пример такой таблицы. Определяется один акселератор на пункт меню MENUP, имеющий идентификатор 4.

MENUP ACCELERATORS
{
VK_F5, 4, VIRTKEY
}

А вот общий вид таблицы акселераторов.

Имя ACCELERATORS
{
Клавиша 1, Идентификатор пункта меню (1) [,тип] [,параметр]
Клавиша 2, Идентификатор пункта меню (2) [,тип] [,параметр]
Клавиша 3, Идентификатор пункта меню (3) [,тип] [,параметр]
...
Клавиша N, Идентификатор пункта меню (N) [,тип] [,параметр]
}

Рассмотрим представленную схему. Клавиша - это либо символ в кавычках, либо код ASCII символа, либо виртуальная клавиша. Если вначале стоит код символа, то тип задается как ASCII. Если используется виртуальная клавиша, то тип определяется как VIRTUAL. Все названия (макроимена) виртуальных клавиш можно найти в include- файлах (windows.h). Мы, как обычно, будем определять все макроимена непосредственно в программе.

Параметр может принимать одно из следующих значений: NOINVERT, ALT, CONTROL, SHIFT. Значение NOINVERT означает, что не подсвечивается выбранный при помощи акселератора пункт меню. Значения ALT, SHIFT, CONTROL означают, что, кроме клавиши, определенной в акселераторе, должна быть нажата одна из управляющих клавиш. Кроме этого, если клавиша определяется в кавычках, то нажатие при этом клавиши CONTROL определяется знаком "^": "^А".

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

  1. Должна быть загружена таблица акселераторов. Для этого используется функция LoadAccelerators.
  2. Сообщения, пришедшие от акселератора, следует преобразовать в сообщение WM_COMMAND. Здесь нам пригодится функция TranslateAccelerator.

Остановимся подробнее на втором пункте. Функция TranslateAccelerator преобразует сообщения WM_KEYDOWN и WM_SYSKEYDOWN в сообщения WM_COMMAND и WM_SYSCOMMAND соответственно. При этом в старшем слове параметра WPARAM помещается 1, как отличие для акселератора. В младшем слове, как Вы помните, содержится идентификатор пункта меню. Возникает вопрос: для чего необходимы два сообщения WM_COMMAND и WM_SYSCOMMAND? Здесь все закономерно: сообщение WM_SYSCOMMAND генерируется для пунктов системного меню или меню окна (см. Рис. 2.3.4).

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

MSG_LOOP:
 PUSH 0
 PUSH 0
 PUSH 0
 PUSH OFFSET MSG
 CALL GetMessageA@16
 CMP EAX, 0
 JE END_LOOP
 PUSH OFFSET MSG
 PUSH [ACC]
 PUSH [NEWHWND]
 CALL TranslateAcceleratorA@12
 CMP EAX ,0
 JNE MSG_LOOP
 PUSH OFFSET MSG
 CALL TranslateMessage@4
 PUSH OFFSET MSG
 CALL DispatchMessageA@4
 JMP MSG_LOOP
END_LOOP:

Рис. 2.3.4. Меню окна.

Фрагмент Вам знаком, но в него вставлена функция TranslateAccelerator. Первым параметром этой функции идет дескриптор приложения, вторым параметром идет дескриптор таблицы акселераторов ([ACC]), получаемый при загрузке таблицы с помощью функции LoadAccelerators. Третий параметр - адрес, где содержится сообщение, полученное функцией GetMessage.

И вот тут начинается самое интересное, потому что Вы можете сказать, что кольцо обработки сообщений используется, когда основное окно создается обычным способом, посредством регистрации класса окна и последующего вызова функции CreateWindow. В данной же главе мы рассматриваем диалоговые окна. Конечно, диалоговые окна могут порождаться обычным окном, и здесь все нормально. Но как быть в простейшем случае одного диалогового окна. Первое, что приходит в голову — это поместить функцию TranslateAccelerator в функцию окна и там, на месте, осуществлять преобразования. Но сообщения от акселератора до этой функции не доходят. И здесь мы подходим к новому материалу: модальные и немодальные окна.

До сих пор мы работали с модальными диалоговыми окнами. Эти окна таковы, что при их вызове программа должна дожидаться, когда окно будет закрыто. Существуют и немодальные диалоговые окна. После их вызова программа продолжает свою работу. При этом немодальное окно позволяет переключаться на другие окна. Каковы особенности создания немодального диалога?

  1. Немодальный диалог создается при помощи функции CreateDialog.
  2. Уничтожается немодальный диалог функцией DestroyWindow.
  3. Для того чтобы немодальный диалог появился на экране, нужно либо указать у него свойство WS_VISIBLE, либо после создания диалога выполнить команду ShowWindow.

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

// файл menu1.rc
// определение констант
#define WS_SYSMENU 0x00080000L
#define WS_MINIMIZEBOX 0x00020000L
#define WS_MAXIMIZEBOX 0x00010000L
#define WS_POPUP 0x80000000L
#define VK_F5 0х74
#define st WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

MENUP MENU
{
POPUP "&Первый пункт"
{
MENUITEM "&Первый",1
MENUITEM "В&торой",2,HELP
MENUITEM "Что-то?",8
}

POPUP "&Второй пункт"
{
MENUITEM "Трети&й",3
MENUITEM "Четверт&ый",4
MENUITEM SEPARATOR
POPUP "Еще подмен&ю"
{
MENUITEM "Десятый пунк&т",6
}
}
MENUITEM "Вы&ход",5
}

// идентификаторы
#define IDI_ICON1 100

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

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

MENUP ACCELERATORS
{
 VK_F5, 4, VIRTKEY, ALT
}

; файл menu1.inc
; константы
; сообщение приходит при закрытии окна
WM_CLOSE equ 10h
WM_INITDIALOG equ 110h
WM_SETICON equ 80h
WM_COMMAND equ 111h

; прототипы внешних процедур
EXTERN ShowWindow@8:NEAR
EXTERN MessageBoxA@16:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN LoadMenuA@8:NEAR
EXTERN SendMessageA@16:NEAR
EXTERN SetMenu@8:NEAR
EXTERN LoadAcceleratorsA@8:NEAR
EXTERN TranslateAcceleratorA@12:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN CreateDialogParamA@20:NEAR
EXTERN DestroyWindow@4:NEAR
EXTERN TranslateMessage@4:NEAR

; структуры
; структура сообщения
MSGSTRUCT STRUC
 MSHWND DD ?
 MSMESSAGE DD ?
 MSWPARAM DD ?
 MSLPARAM DD ?
 MSTIME DD ?
 MSPT DD ?
MSGSTRUCT ENDS

;файл menu1.asm
.386P

; плоская модель
.MODEL FLAT, stdcall
include menu1.inc
; директивы компоновщику для подключения библиотек
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
 NEWHWND DD 0
 MSG MSGSTRUCT <?>
 HINST DD 0 ; дескриптор приложения
 PA DB "DIAL1",0
 PMENU DB "MENUP",0
 STR1 DB "Выход из программы",0
 STR2 DB "Сообщение",0
 STR3 DB "Выбран четвертый", О
 АСС DWORD ?
_DATA ENDS

; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
; получить дескриптор приложения
 PUSH 0
 CALL GetModuleHandleA@4
 MOV [HINST], EAX
; загрузить акселераторы
 PUSH OFFSET PMENU
 PUSH [HINST]
 CALL LoadAcceleratorsA@8
 MOV АСС, EAX ; запомнить дескриптор таблицы
; создать немодальный диалог
 PUSH 0
 PUSH OFFSET WNDPROC
 PUSH 0
 PUSH OFFSET PA
 PUSH [HINST]
 CALL CreateDialogParamA@20
; визуализировать немодальный диалог
 MOV NEWHWND, EAX
 PUSH 1 ; SW_SHOWNORMAL
 PUSH [NEWHWND]
 CALL ShowWindow@8 ; показать созданное окно
; кольцо обработки сообщений
MSG_LOOP:
 PUSH 0
 PUSH 0
 PUSH 0
 PUSH OFFSET MSG
 CALL GetMessageA@l6
 CMP EAX, 0
 JE END_LOOP
; транслировать сообщение акселератора
 PUSH OFFSET MSG
 PUSH [АСС]
 PUSH [NEWHWND]
 CALL TranslateAcceleratorA@12
 CMP EAX,0
 JNE MSG_LOOP
 PUSH OFFSET MSG
 CALL TranslateMessage@4
 PUSH OFFSET MSG
 CALL DispatchMessageA@4
 JMP MSG_LOOP
END_LOOP:
 PUSH 0
 CALL ExitProcess@4

; процедура окна
; расположение параметров в стеке
; [EBP+014Н] LPARAM
; [EBP+10H] WAPARAM
; [EBP+0CH] MES
; [EBP+8] HWND
WNDPROC PROC
 PUSH EBP
 MOV EBP,ESP
 PUSH EBX
 PUSH ESI
 PUSH EDI
;-------------------------
 CMP DWORD PTR [EBP+0CH],WM_CLOSE
 JNE L1
; закрыть диалоговое окно
 JMP L5
L1:
 CMP DWORD PTR [EBP+0CH],WM_INITDIALOG
 JNE L3
; загрузить иконку
 PUSH 100 ; идентификатор иконки
 PUSH [HINST] ; идентификатор процесса
 CALL LoadIconA@8
; установить иконку
 PUSH EAX
 PUSH 0 ; тип иконки (маленькая)
 PUSH WM_SETICON
 PUSH DWORD PTR [EBP+08H]
 CALL SendMessageA@16
; загрузить меню
 PUSH OFFSET PMENU
 PUSH [HINST]
 CALL LoadMenuA@8
; установить меню
 PUSH EAX
 PUSH DWORD PTR [EBP+08H]
 CALL SetMenu@8
;-----------------------------
 MOV EAX, 1 ; возвратить не нулевое значение
 JMP FIN
; проверяем, не случилось ли чего с управляющими
; элементами на диалоговом окне
L3:
 CMP DWORD PTR [EBP+0CH],WM_COMMAND
 JE L6
 JMP FINISH
; здесь определяем идентификатор, в данном случае
; это идентификатор пункта меню сообщение
L6:
 CMP WORD PTR [EBP+10H], 4
 JNE L4
 PUSH 0 ; MB_OK
 PUSH OFFSET STR2
 PUSH OFFSET STR3
 PUSH 0
 CALL MessageBoxA@16
 JMP FINISH
L4:
 CMP WORD PTR [EBP+10H], 5
 JNE FINISH
; сообщение
 PUSH 0 ; MB_OK
 PUSH OFFSET STR2
 PUSH OFFSET STR1
 PUSH 0
 CALL MessageBoxA@16
; закрыть диалоговое немодальное окно
L5:
 PUSH DWORD PTR [EBP+08H]
 CALL DestroyWindow@4
; послать сообщение для выхода из кольца
; обработки сообщений
 PUSH 0
 CALL PostQuitMessage@4 ; сообщение WM_QUIT
FINISH:
 MOV EAX, 0
FIN:
 POP EDI
 POP ESI
 POP EBX
 POP EBP
 RET 16
WNDPROC ENDP
_TEXT ENDS
END START

Рис. 2.3.5. Пример диалогового немодального окна сменю и обработкой сообщений акселераторов.

Несколько комментариев по поводу программы на Рис. 2.3.5.

  1. Немодальный диалог задается в файле ресурсов так же, как и модальный. Поскольку в свойствах окна нами не было указано свойство WS_VISIBLE, в программе, для того чтобы окно было видимым, мы используем функцию ShowWindow.
  2. Выход из программы в нашем случае предполагает не только удаление из памяти диалогового окна, что достигается посредством функции DestroyWindow, но и выход из цикла обработки сообщений. Последнее осуществляется через вызов функции PostQuitMessage.

В заключение отмечу, что остались неосвещенными также следующие виды ресурсов:

1. Ресурс, содержащий неструктурированные данные.

имя RCDATA
BEGIN
 raw-data
 . . .
END

2. Ресурс VERSIONINFO.

ID VERSIONINFO
BEGIN
 block-statement
 . . .
END

Оба этих ресурса используются значительно реже остальных, и мы на них останавливаться не будем.

Трансляция при помощи пакета TASM32

При работе с ресурсами в пакете TASM32 следует учитывать некоторые особенности. И дело здесь не только в том, что компиляторы ресурсов могут иметь свои конструкции языка, которые не понимает другой компилятор. Об этом мы уже говорили и больше касаться этого не будем. Есть и другое отличие. Пусть программа называется DIAL.ASM, а файл ресурсов DIAL.RC. Тогда полная трансляция в пакете TASM32 будет выглядеть следующим образом.

TASM32 /ml DIAL.ASM
BRCC32 DIAL.RC
TLINK32 DIAL.OBJ,,,,, DIAL.RES

В результате получится программа DIAL.EXE. Если программа представляет на экран диалоговое окно (именно диалоговое, а не обычное), то, скорее всего (возможно и нет). Вы обнаружите, что стиль его соответствует стилю окон Windows 3.1, чего не было в случае трансляции в MASM32. Проблема разрешится, если добавить в стиль окна константу DS_3DLOOK, равную 0x0004L. В файле помощи можно найти утверждение, что стиль DS_3DLOOK должен автоматически устанавливаться у диалоговых окон. Возможно, суть здесь заключается в особенности работы TLINK32.EXE. Других существенных отличий при работе с ресурсами в пакетах MASM32 и TASM32 я не усматриваю.






Вход на сайт
Поиск
Календарь
«  Июль 2025  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
28293031
Архив записей
Наш опрос
Как Вам удобнее??
Всего ответов: 341
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0