Шаг 43.
Примеры программ с ресурсами. Управление списками

    На этом шаге мы рассмотрим работу со списками.

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

    Средства управления списком можно разделить на сообщения и свойства. Свойства задаются в файле ресурсов. Например, свойство LBS_SORT приводит к тому, что содержимое списка будет автоматически сортироваться при добавлении туда элемента. Очень важным является свойство LBS_WANTKEYBOARDINPUT. При наличии такого свойства приложение получает сообщение WM_VKEYTOITEM, которое посылается приложению, когда нажимается какая-либо клавиша при наличии фокуса на данном списке. Вы можете выбрать самостоятельную обработку - клавиша PgUp или оставить стандартную обработку. В том случае, если стандартная обработка не нужна, следует возвратить из функции диалогового окна отрицательное значение.


    Ресурсный файл diallst.rc.
//Файл diallst.rc.
//Определение констант.
//Стили окна.
#define WS_SYSMENU      0x00080000L
#define WS_MINIMIZEBOX  0x00020000L
#define WS_MAXIMIZEBOX  0x00010000L
//Элементы на окне должны быть изначально видимы.
#define WS_VISIBLE      0x10000000L
//При помощи TAB можно по очереди активизировать элементы.
#define WS_TABSTOP      0x00010000L

#define WS_VSCROLL      0x00200000L
#define WS_THICKFRAME   0x00040000L
#define LBS_NOTIFY      0x0001L
#define LBS_SORT	0x0002L
#define LBS_WANTKEYBOARDINPUT   0x0400L
//Идентификаторы.
#define LIST1   101
#define LIST2   102
#define IDI_ICON1 3
//Определили пиктограмму.
IDI_ICON1  ICON  "help.ico"
//Определение диалогового окна.
DIAL1 DIALOG 0, 0, 210, 110
STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION "Пример диалогового окна"
FONT 8, "Arial"
{
CONTROL "ListBox1", LIST1, "listbox", WS_VISIBLE | WS_TABSTOP | 
  WS_VSCROLL | WS_THICKFRAME | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT, 16, 16, 70, 75 
CONTROL "ListBox2", LIST2, "listbox", WS_VISIBLE | WS_TABSTOP | 
  WS_VSCROLL | WS_THICKFRAME | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT, 116, 16, 70, 75 
}


    Заголовочный файл для приложения, содержащий определения констант, внешних процедур и структур (его имя pr43_1.asm).
;Константы.
;Сообщение приходит при закрытии окна.
WM_CLOSE      equ 10h
WM_INITDIALOG equ 110h
WM_SETICON    equ 80h
WM_COMMAND    equ 111h
WM_VKEYTOITEM equ 2Eh
LB_ADDSTRING  equ 180h
LBN_DBLCLK    equ 2
LB_GETCURSEL  equ 188h
LB_GETTEXT    equ 189h
LB_FINDSTRING equ 18Fh
VK_INSERT     equ 2Dh
;Прототипы внешних процедур.
EXTERN	ExitProcess@4:NEAR
EXTERN	GetModuleHandleA@4:NEAR
EXTERN	DialogBoxParamA@20:NEAR
EXTERN	EndDialog@8:NEAR
EXTERN	LoadIconA@8:NEAR
EXTERN	SendMessageA@16:NEAR
EXTERN	SendDlgItemMessageA@20:NEAR
EXTERN	MessageBoxA@16:NEAR
;Структуры.
;Структура сообщения.
MSGSTRUCT  STRUC	
           MSHWND     DD ? ;Идентификатор окна, получающего сообщение.
           MSMESSAGE  DD ? ;Идентификатор сообщения.
           MSWPARAM   DD ? ;Доп. информация о сообщении.
           MSLPARAM   DD ? ;Доп. информация о сообщении.
           MSTIME     DD ? ;Время посылки сообщения.
           MSPT       DD ? ;Положение курсора во время посылки сообщения.
MSGSTRUCT ENDS	
Текст этого модуля, а также исходный файл ресурсов и файл с пиктограммой можно взять здесь.


    Основной файл приложения, содержит подключение файла pr43_1.asm (его имя pr43_2.asm).
.386P
;Плоская модель.
.MODEL FLAT, STDCALL
include pr43_1.asm
;Директивы компоновщику для подключения библиотек.
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib
;------------------------------------------------
;Сегмент данных. 
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
     MSG        MSGSTRUCT  <;?>
     HINST      DD 0 ;Дескриптор приложения.
     PA         DB 'DIAL1',0
     BUFER      DB 100 DUP(0)
     STR1       DB 'Первый',0
     STR2       DB "Второй",0
     STR3       DB "Третий",0 
     STR4       DB "Четвертый",0 
     STR5       DB "Пятый",0 
     STR6       DB "Шестой",0 
     STR7       DB "Седьмой",0 
     STR8       DB "Восьмой",0 
     STR9       DB "Девятый",0 
     STR10      DB "Десятый",0 
     STR11      DB "Одиннадцатый",0 
     STR12      DB "Двенадцатый",0 
     STR13      DB "Тринадцатый",0 
     STR14      DB "Четырнадцатый",0 
     STR15      DB "Пятнадцатый",0 
     INDEX      DD OFFSET STR1 
                DD OFFSET STR2 
                DD OFFSET STR3 
                DD OFFSET STR4 
                DD OFFSET STR5 
                DD OFFSET STR6 
                DD OFFSET STR7 
                DD OFFSET STR8 
                DD OFFSET STR9 
                DD OFFSET STR10 
                DD OFFSET STR11 
                DD OFFSET STR12 
                DD OFFSET STR13 
                DD OFFSET STR14 
                DD OFFSET STR15 
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START: 
;Получить дескриптор приложения.
     PUSH  0
     CALL  GetModuleHandleA@4
     MOV   [HINST], EAX
;-------------------------------------
     PUSH  0
     PUSH  OFFSET WNDPROC
     PUSH  0
     PUSH  OFFSET PA
     PUSH  [HINST] 
     CALL  DialogBoxParamA@20 
     CMP   EAX,-1 
     JNE   KOL 
;Здесь можно разместить сообщение об ошибке.
KOL:
;-------------------------------------
     PUSH  0
     CALL  ExitProcess@4
;-------------------------------------
;-------------------------------------
;Процедура окна.
;Расположение параметров в стеке: 
;[ЕВР+014Н] LPARAM 
;[ЕВР+10Н] WAPARAM 
;[ЕВР+0СН] MES 
;[ЕВР+8] HWND 
WNDPROC PROC
        PUSH EBP
        MOV  EBP, ESP
        PUSH EBX
        PUSH ESI
        PUSH EDI
;-------------------------------------
        CMP  DWORD PTR [EBP+0CH],WM_CLOSE
        JNE  L1
        PUSH 0
        PUSH DWORD PTR [EBP+08H]
        CALL EndDialog@8
        JMP  FINISH 
L1:
        CMP  DWORD PTR [EBP+0CH],WM_INITDIALOG
        JNE  L2 
;Загрузить пиктограмму.
        PUSH 3         ;Идентификатор пиктограммы.
        PUSH [HINST]   ;Идентификатор процесса.
        CALL LoadIconA@8 
;Установить пиктограмму.
        PUSH EAX
        PUSH 0          ;Тип пиктограммы (маленькая).
        PUSH WM_SETICON
        PUSH DWORD PTR [EBP+08H]
        CALL SendMessageA@16 
;Заполнить левый список.
        MOV  ECX,15
        MOV  ESI,0 
LO1:
        PUSH ECX        ;Сохранить параметр цикла.
        PUSH INDEX[ESI]
        PUSH 0
        PUSH LB_ADDSTRING
        PUSH 101
        PUSH DWORD PTR [EBP+08H]
        CALL SendDlgItemMessageA@20
        ADD  ESI,4
        POP  ECX
        LOOP LO1
        JMP  FINISH 
L2:
        CMP  DWORD PTR [EBP+0CH],WM_COMMAND
        JNE  L3 
;Не сообщение ли от левого списка?
        CMP  WORD PTR [EBP+10H],101
        JNE  FINISH 
;Не было ли двойного щелчка?
        CMP  WORD PTR [EBP+12H], LBN_DBLCLK
        JNE  FINISH
;Был двойной щелчок, теперь определим элемент. 
;Получить индекс выбранного элемента. 
L4:
        PUSH 0
        PUSH 0
        PUSH LB_GETCURSEL 
        PUSH 101
        PUSH DWORD PTR [EBP+08H] 
        CALL SendDlgItemMessageA@20 
;Скопировать элемент списка в буфер. 
        PUSH OFFSET BUFER 
        PUSH EAX       ;Индекс записи. 
        PUSH LB_GETTEXT 
        PUSH 101
        PUSH DWORD PTR [EBP+08H] 
        CALL SendDlgItemMessageA@20 
;Определить, нет ли элемента в правом списке. 
        PUSH OFFSET BUFER 
        PUSH -1        ;Искать во всем списке. 
        PUSH LB_FINDSTRING 
        PUSH 102
        PUSH DWORD PTR [EBP+08H] 
        CALL SendDlgItemMessageA@20 
        CMP  EAX,-1
        JNE  FINISH    ;Элемент нашли. 
;Не нашли, можно добавлять. 
        PUSH OFFSET BUFER 
        PUSH 0
        PUSH LB_ADDSTRING 
        PUSH 102
        PUSH DWORD PTR [EBP+08H] 
        CALL SendDlgItemMessageA@20 
        MOV  EAX,-1 
        JMP  FIN
L3: 
;Здесь проверка, не нажата ли клавиша.
        CMP  DWORD PTR [EBP+0CH],WM_VKEYTOITEM
        JNE  FINISH
        CMP  WORD PTR [EBP+10H],VK_INSERT
        JE   L4
        MOV  EAX,-1
        JMP  FIN
FINISH:
        MOV  EAX,0
FIN:	
        POP  EDI
        POP  ESI
        POP  EBX
        POP  EBP
        RET  16
WNDPROC ENDP
_TEXT   ENDS	
        END START
Текст этой программы можно взять здесь.

    Результат работы приложения можнео увидеть на рисунке 1:


Рис.1. Результат работы приложения

    Прокомментируем приведенную программу.

    В первую очередь обратите внимание на функцию SendDlgItemMessage. Для посылки сообщения элементам диалогового окна эта функция более удобна, чем SendMessage, так как элемент в ней идентифицируется не дескриптором (который еще надо узнать), а номером, определенным в файле ресурсов.

    Взглянув на файл ресурсов, вы увидите, что второму (правому) списку присвоено свойство LBS_SORT. Если такое свойство присвоено списку, то при добавлении в него элемента (сообщение LB_ADDSTRING) этот элемент помещается в список так, что список остается упорядоченным. Свойство LBS_SORT стоит системе Windows довольно большой работы. Посредством сообщения WM_COMPAREITEM она определяет нужное положение нового элемента в списке, а затем вставляет его при помощи сообщения LB_INSERTSTRING.

    Хотелось бы также обратить внимание на цикл заполнения левого списка. Нам приходится хранить регистр ЕСХ в стеке. Вы скажете, что это обычное дело при организации цикла с использованием команды LOOP. Отметим, что это совсем не очевидно. К сожалению, в документации по функциям API и сообщениям не указывается, какие регистры микропроцессора сохраняются, а какие - нет. Все это придется устанавливать экспериментально. Что и было сделано в данном примере.

    Сообщение WM_VKEYTOITEM приходит при нажатии какой-либо клавиши, при наличии фокуса на списке. При этом список должен иметь свойство LBS_WANTKEYBOARDINPUT. Именно потому, что данное свойство установлено только у левого списка, у нас нет необходимости проверять, от какого списка пришло сообщение.

    Со следующего шага мы начнем рассматривать работу с файлами.




Предыдущий шаг Содержание Следующий шаг