Blog. Just Blog

Поле EDIT для ввода шестнадцатеричных цифр

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Поле для ввода только цифр организовать несложно, для этого достаточно просто прописать в ресурсах у нужного поля флаг ES_NUMBER. После этого с клавиатуры в это поле можно будет ввести только символ из интервала "0" - "9". Впрочем этот ничуть не мешает через Ctrl+C / Ctrl+V затолкать в него все что угодно, вплоть до непечатных бинарных данных. А как сделать поле, в которое можно вводить с клавиатуры только шестнадцатеричные цифры? Ведь там могут быть не только символы с десятичными цифрами, но и символы "A" - "F", которые также являются шестнадцатеричными цифрами. Это делается через субклассирование. Мы уже рассмотрели один пример субклассирования, превратив поле EDIT в гиперссылку, и там же определили алгоритм обработчика субклассированного элемента. Как и в прошлый раз, субклассирование должно выполняться на этапе инициализации диалогового окна.
  1. ; Сегмент данных
  2. section '.data' data readable writeable  
  3. OldProc dd ?    ; Адрес предыдущего обработчика
  4.  
  5. ; Сегмент кода
  6. section '.code' code readable executable
  7.         ...
  8.         ; Субклассирование на этапе инициализации окна
  9.         invoke  GetDlgItem,[hwnddlg],ID_HEX
  10.         ; Установить наш собственный обработчик
  11.         invoke  SetWindowLong,eax,GWL_WNDPROC,EditWindowProc
  12.         ; Сохранить хэндл предыдущего обработчика
  13.         mov     [OldProc],eax
  14.         ...
Здесь все почти то же самое, разница будет лишь в обрабатываемых сообщениях. Соответственно, если будет несколько полей ввода, то надо субклассировать каждое из них отдельно.

Назначенная процедура EditWindowProc будет обрабатывать только одно сообщение: WM_CHAR, оно происходит при нажатии на любую клавишу, когда курсор находится в нашем поле EDIT. При фильтрации нажатых клавиш, кроме шестнадцатеричных цифр не забывайте про служебные комбинации Ctrl+C / Ctrl+V / Ctrl+X и клавишу Backspace. Все остальные клавиши фильтруются и блокируются. Похожий пример на MASM был показан в одном из уроков Iczelion'а, но в нем не учитывались клавиши копирования-вставки, а также функции вставки данных в поле ввода.
  1. ;------------------------------------------------
  2. ; Субклассированный обработчик
  3. ;------------------------------------------------
  4. proc EditWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
  5.         ; Нажатие клавиши?
  6.         cmp     [uMsg],WM_CHAR
  7.         je      .chk_char
  8.         ; Вставка?
  9.         cmp     [uMsg],WM_PASTE
  10.         jne     .char_ok
  11. .loc_paste:
  12.         ; Получить формат буфера обмена
  13.         invoke  IsClipboardFormatAvailable,CF_TEXT
  14.         or      eax,eax
  15.         jz      .exit_proc
  16.  
  17.         ; Получить хэндл родительского окна
  18.         invoke  GetParent,[hEdit]
  19.         ; Открыть буфер обмена
  20.         invoke  OpenClipboard,eax
  21.         invoke  GetClipboardData,CF_TEXT
  22.         mov     ebx,eax
  23.         invoke  GlobalLock,eax
  24.  
  25.         ; Поочередно передать все символы из буфера обмена
  26.         mov     esi,eax
  27. @@:
  28.         lodsb
  29.         or      al,al
  30.         jz      @f
  31.         movzx   eax,al
  32.         invoke  SendMessage,[hEdit],WM_CHAR,eax,0
  33.         jmp     @b
  34. @@:
  35.         ; Закрыть буфер обмена
  36.         invoke  GlobalUnlock,ebx
  37.         invoke  CloseClipboard
  38.         jmp     .exit_proc
  39. .char_ok:
  40.         ; Передать управление предыдущему обработчику или пропустить
  41.         ; разрешенный символ
  42.         invoke  CallWindowProc,[OldProc],[hEdit],[uMsg],[wParam],[lParam]
  43.         ret
  44. .chk_char:
  45.         ; Проверить код нажатой клавиши
  46.         mov     eax,[wParam]
  47.         cmp     al,VK_BACK      ; Backspace
  48.         je      .char_ok
  49.         cmp     al,22           ; Ctrl+V
  50.         je      .loc_paste
  51.         cmp     al,3            ; Ctrl+C
  52.         je      .char_ok
  53.         cmp     al,24           ; Ctrl+X
  54.         je      .char_ok
  55.         cmp     al,'0'          ; Hex-цифра
  56.         jb      .exit_proc
  57.         cmp     al,'9'
  58.         jbe     .char_ok
  59.         or      al,20h
  60.         cmp     al,'a'
  61.         jb      .exit_proc
  62.         cmp     al,'f'
  63.         jbe     .char_ok
  64. .exit_proc:
  65.         ; Подавить сообщение нажатия клавиши и вернуть из обработчика FALSE
  66.         xor     eax,eax
  67.         ret
  68. endp
Обработчик можно легко модифицировать для других правил фильтрации ввода, например, если требуется ввести только латинские символы или число в восьмеричной системе счисления. Какой бы тщательной ни была фильтрация, в любом случае обязательно выполняйте дополнительные проверки при чтении и дальнейшем использовании введенных данных!

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Hex.EDIT.Demo.zip (3,019 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 6157 | Комментариев: 9

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (27.02.2020 в 18:00):
Учел все недостатки, полностью перекроил код обработчика. Теперь вставка обрабатывается корректно. Архив и статья обновлены.
brute (04.01.2012 в 10:53):
у меня теория - сплошной пробел! (пытаюсь писать на PB - это тот же fasm)
1.Посмотрел примеров 20 с wasma, и ничего не понял! Где-то при сабклассинге правят значения wParam/lParam; где-то значения eax/ebx - это зависит от типа сообщений?  (например, при наезде мышкой на button WM_MOUSEMOVE в ebx - хендл buttona?) Что почитать?
2. Обычное окно (созданное через Wldclass) обрабатыает msg=WM_CTLCOLOREDIT в основном цикле сообщений - без сабклассинга, меняя цвет вводимого через SetTextColor. А диалоговое окно не реагирует на это сообщение!!! (контол EDIT создавал и в ресурсах и через CreateWinowEX).
3. Где должна быть CallWindowProc - в новой процедуре или в цикле старой? С WM_SETCURSOR  и сменой формы курсора работает по любому.  (с другими сообщениями не работает никак)
4. Зачем нужен сабклассинг? Чтобы научить контрол обрабатывать сообщения, которые он не обрабатывает по умолчанию? Где должна происходить смена свойств контрола (в основной процедуре окна или в новой?)
5. как (правильно и вообще) работать с нотификационными сообщениями, когда они посылаются?
brute (02.01.2012 в 22:12):
-файл закрывается при нажатии ENTER (почему?)
-нет на форме кнопки ОК с IDOK (в коде есть)
-"никакое субклассирование не спасает от копирования и вставки данных в поле EDIT" - если не обрабатывать Ctrl+C/Ctrl+V, то спасает (см. аттач)
-в уроке 20Iczelion'а, в CallWindowProc возвращают eax вместо wParam. Почему здесь не так?
- не получается сабклассировать гиперссылку (её цвет) - переписываю для разнообразия и обучения примеры на PB (hiperlink.pb - в аттаче)- может, советом поможешь?:^);
- во всех примерах трудно разобраться из-за кучи джампов (с if-elseif обработка сообщений была бы нагляднее) приходится две страницы постоянно скролить вверх-вниз
 
http://www.fayloobmennik.net/1360934
ManHunter (29.12.2010 в 08:53):
Кстати, было бы еще неплохо проверить на юникод. По идее проблем быть не должно, но все-таки многобайтные символы передаются по одному байту.
Pretorian (28.12.2010 в 23:10):
Хотя лучше добавить еще проверку на Lower Case (так как иногда некорректно срабатывает при вставке текста), т.е.

    cmp al,61H          ; 'a'
    jb .upper
    cmp al,7AH          ; 'z'
    ja loc_exit_proc
    xor al,20H          ; Символ в нижний регистр
.upper:
ManHunter (27.12.2010 в 17:17):
Надо будет проверить и этот способ, спасибо.
Pretorian (27.12.2010 в 17:15):
Это вариант еще эффективнее:
...
buffer  db 256 dup(?)
buflen  dd ?
...
proc EditWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    cmp [uMsg],WM_CHAR
    jnz loc_paste
    mov eax,[wParam]
    cmp al,1            ; Ctrl+A
    je loc_char_ok
    cmp al,8            ; Backspace
    je loc_char_ok
    cmp al,16h          ; Ctrl+V
    je loc_char_ok
    cmp al,18h          ; Ctrl+X
    je loc_char_ok
    cmp al,30h          ; '0'
    jb loc_exit_proc
    cmp al,39h          ; '9'
    jbe loc_char_ok
    xor al,20h          ; Символ в нижний регистр
    cmp al,41h          ; 'A'
    jb loc_exit_proc
    cmp al,46h          ; 'F'
    jbe loc_char_ok
loc_exit_proc:
    xor eax,eax
    ret
loc_char_ok:
    mov [wParam],eax
loc_paste:
    invoke CallWindowProc,[OldProc],[hEdit],[uMsg],[wParam],[lParam]
    cmp [uMsg],WM_PASTE
    je loc_chk_paste
    ret
loc_chk_paste:
    invoke GetWindowText,[hEdit],buffer,255
    invoke SetWindowText,[hEdit],NULL
    mov [buflen],0
    push esi
    mov esi,buffer
loc_loop:
    cmp [buflen],255
    je loc_exit_loop
    lodsb
    or al,al
    je loc_exit_loop
    invoke SendMessage,[hEdit],WM_CHAR,eax,0
    inc [buflen]
    jmp loc_loop
loc_exit_loop:
    pop esi
    ret
endp
ManHunter (26.08.2009 в 10:52):
Да, есть такое дело.
Борис Александрович (26.08.2009 в 08:25):
"прочем этот ничуть не мешает через Ctrl+C / Ctrl+V затолкать в него все что угодно"
а ваш вариант в этом плане не лучше нискольно - туда тоже можно что угодно засунуть. Правда, его можно слегка улучшить:

proc EditWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
        cmp     [uMsg],WM_CHAR
        je      .chk_char
        cmp     eax,WM_PASTE
        je      .paste
.char_ok:
        invoke  CallWindowProc,[OldProc],[hEdit],[uMsg],[wParam],[lParam]
        ret
.chk_char:
        invoke  GetAsyncKeyState,VK_CONTROL
        mov     ecx,[wParam]
        test    eax,eax
        jne     .ctrl
        cmp     cl,VK_BACK
        je      .char_ok
        cmp     cl,'0'
        jb      .exit_proc
        cmp     cl,'9'
        jbe     .char_ok
        or      cl,20h
        cmp     cl,'a'
        jb      .exit_proc
        cmp     cl,'f'
        jbe     .char_ok
.exit_proc:
        xor     eax,eax
        ret

.ctrl:  cmp     cl,3
        je      .char_ok
        cmp     cl,22
        jne     .exit_proc
.paste: invoke  IsClipboardFormatAvailable,CF_TEXT
        test    eax,eax
        je      .exit_proc
        invoke  OpenClipboard,ebx
        test    eax,eax
        je      .exit_proc
        invoke  GetClipboardData,CF_TEXT
        test    eax,eax
        je      .exit_proc
        push    eax
        invoke  GlobalLock,eax
        push    ebx
        sub     ebx,ebx
.l00p:  mov     cl,byte[eax]
        cmp     cl,'0'
        jb      .e_l00p
        cmp     cl,'9'
        jbe     .inc
        or      cl,20h
        cmp     cl,'a'
        jb      .e_l00p
        cmp     cl,'f'
        ja      .e_l00p
.inc:   inc     eax
        cmp     [eax],bl
    jne     .l00p
    inc     ebx
.e_l00p:call    [GlobalUnlock]
.close: call    [CloseClipboard]
        test    ebx,ebx
        pop    ebx
        jne    .char_ok
        jmp    .exit_proc
endp

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0074 сек.) / Память: 4.5 Mb
Наверх