Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как известно, если элементу диалогового окна типа EDIT установить флаг ES_NUMBER, то ввести в него можно будет только символы из интервала "0" - "9". Это в теории. На практике же в это поле через Ctrl+V или контекстное меню совершенно спокойно можно вставить любые данные, о чем даже написано в официальной документации. А еще в это поле нельзя ввести отрицательное число. Таким образом, практическая ценность ES_NUMBER приближается к нулю. Давайте учтем все эти недостатки и сделаем собственное, правильное поле 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_SUB
  10.         ; Установить наш собственный обработчик
  11.         invoke  SetWindowLong,eax,GWL_WNDPROC,EditWindowProc
  12.         ; Сохранить хэндл предыдущего обработчика
  13.         mov     [OldProc],eax
  14.         ...
Для одиночного поля можно ограничиться этим решением, но если таких полей предполагается несколько, то лучше воспользоваться универсальным субклассированием.

Теперь переходим к самому обработчику. Он должен уметь следующее: фильтровать все символы, отличные от цифр; позволять вводить символ "минус", но не абы как, а только в первую позицию и только если там минуса еще нет; позволять копировать и вставлять данные с учетом упомянутых правил. Вот что у меня получилось:
  1. ;------------------------------------------------
  2. ; Субклассированный обработчик
  3. ;------------------------------------------------
  4. proc EditWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
  5.         locals
  6.                 tmp dd ?
  7.         endl
  8.  
  9.         ; Нажатие клавиши?
  10.         cmp     [uMsg],WM_CHAR
  11.         je      .chk_char
  12.         ; Вставка?
  13.         cmp     [uMsg],WM_PASTE
  14.         jne     .char_ok
  15. .loc_paste:
  16.         ; Получить формат буфера обмена
  17.         invoke  IsClipboardFormatAvailable,CF_TEXT
  18.         or      eax,eax
  19.         jz      .exit_proc
  20.  
  21.         ; Получить хэндл родительского окна
  22.         invoke  GetParent,[hEdit]
  23.         ; Открыть буфер обмена
  24.         invoke  OpenClipboard,eax
  25.         invoke  GetClipboardData,CF_TEXT
  26.         mov     ebx,eax
  27.         invoke  GlobalLock,eax
  28.  
  29.         ; Поочередно передать все символы из буфера обмена
  30.         mov     esi,eax
  31. @@:
  32.         lodsb
  33.         or      al,al
  34.         jz      @f
  35.         movzx   eax,al
  36.         invoke  SendMessage,[hEdit],WM_CHAR,eax,0
  37.         jmp     @b
  38. @@:
  39.         ; Закрыть буфер обмена
  40.         invoke  GlobalUnlock,ebx
  41.         invoke  CloseClipboard
  42.         jmp     .exit_proc
  43. .char_ok:
  44.         ; Передать управление предыдущему обработчику или пропустить
  45.         ; разрешенный символ
  46.         invoke  CallWindowProc,[OldProc],[hEdit],[uMsg],[wParam],[lParam]
  47.         ret
  48. .chk_char:
  49.         ; Проверить код нажатой клавиши
  50.         mov     eax,[wParam]
  51.         cmp     al,VK_BACK      ; Backspace
  52.         je      .char_ok
  53.         cmp     al,22           ; Ctrl+V
  54.         je      .loc_paste
  55.         cmp     al,3            ; Ctrl+C
  56.         je      .char_ok
  57.         cmp     al,24           ; Ctrl+X
  58.         je      .char_ok
  59.         cmp     al,1            ; Ctrl+A
  60.         je      .char_ok
  61.  
  62.         cmp     al,'-'
  63.         jne     @f
  64.  
  65.         ; Позиция курсора на самом начале ввода?
  66.         lea     ebx,[tmp]
  67.         invoke  SendMessage,[hEdit],EM_GETSEL,ebx,NULL
  68.         cmp     dword [ebx],0
  69.         ; Нет, тут в любом случае минуса быть не может
  70.         jnz     .exit_proc
  71.  
  72.         ; Выделен фрагмент от самого начала или все поле целиком?
  73.         invoke  SendMessage,[hEdit],EM_GETSEL,NULL,ebx
  74.         cmp     dword [ebx],0
  75.         ; Да, существующий минус в любом случае перезапишется
  76.         jne     .char_ok
  77.  
  78.         ; Минус в самом начале уже есть?
  79.         invoke  SendMessage,[hEdit],WM_GETTEXT,2,ebx
  80.         cmp     byte [ebx],'-'
  81.         ; Нет, минус можно добавить
  82.         jne     .char_ok
  83.         ; Да, подавить ввод
  84.         jmp     .exit_proc
  85. @@:
  86.         cmp     al,'0'          ; Десятичная цифра?
  87.         jb      .exit_proc
  88.         cmp     al,'9'
  89.         jbe     .char_ok
  90. .exit_proc:
  91.         ; Подавить сообщение нажатия клавиши и вернуть из обработчика FALSE
  92.         xor     eax,eax
  93.         ret
  94. endp
Обработчик легко модифицировать под свои нужды, например, в своих программах я частенько использую клик колесиком мыши на поле ввода в качестве команды "вставить".

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

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

Dec.EDIT.Demo.zip (6,735 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (04.09.2020 в 11:32):
GetDlgItemInt
Андрей (04.09.2020 в 11:29):
Доброго дня. Вопросы не иссякли. Помимо ограничения по разрядности, решил прикрутить ограничение по максимально возможной вводимой цифре. К примеру мне нужно ввести байт в десятичном формате. Это три разряда в десятичной системе. Но по понятным причинам больше 255 вводится не должно. Так вот попытался воспользоваться функцией  invoke GetDlgItemText,[hEdit],ID_SUB,buff_mes,32
Но она опять же не работает-) Хотя по логике вещей должна.
Ведь  invoke  SendMessage,[hEdit],WM_GETTEXTLENGTH,0,0 выдает наличие знаков которыми владеет окно с текущим хеделом. В чем моя ошибка?
Андрей (03.09.2020 в 16:03):
Спасибо огромное!!!
Андрей (03.09.2020 в 15:48):
Я понимаю что задолбал своей простотой. Но если найдется время на ответ.

Соорудил в начале субобработчика следующую конструкцию.

invoke   SendMessage, [hEdit], WM_GETTEXTLENGTH,0,0
          cmp eax,6                                                                               
          ja .exit_proc ; 7 и более на выход

Но программа вообще перестала окно выводить. Судя по всему ей строчка с SendMessage в принципе не нравится. В чем моя ошибка?
Андрей (03.09.2020 в 11:52):
Пробовал таким образом в начале обработчика число символов добывать. Но почему то не взлетело.

;------------------------------------------------
; Субклассированный обработчик
;------------------------------------------------
proc EditWindowProcRun hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD                                                                               invoke  SetDlgItemText,[hwin],ID_SUB,buff_mes                                                                        cmp eax,6                                                                               ja .exit_proc

Спасибо, буду пробовать.
ManHunter (03.09.2020 в 11:34):
EM_SETLIMITTEXT, WM_GETTEXTLENGTH
Андрей (03.09.2020 в 11:27):
И снова здравствуйте-)

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

Однако это не спасает от того, что простым нажатием кнопки можно вставить 100500 знаков. Равно как и копированием, можно накидать столько же за несколько раз "вставить"
Андрей (28.07.2020 в 18:33):
Понял. Спасибо. Теперь думаю разберусь.
ManHunter (28.07.2020 в 13:42):
Вот где код "Поочередно передать все символы из буфера обмена", там просто добавить счетчик вставляемых символов. Уже взяли из буфера, например, 10-15 или сколько там надо символов, значит все, стоп, остальное содержимое буфера обмена не обрабатываем.

А исходники я не выкладываю, если чего-то сразу нет в статьях в открытом доступе, значит этого нигде никогда и не появится.
Андрей (28.07.2020 в 13:32):
Доброго дня.
Очень полезный пример. Сам я в winasm поскольку постольку, больше по микроконтроллерам кнопаю, на хобийном уровне. Но иногда приходится и в это дело вникать. Но изучение API дается туго. Из за отсутствия постоянной практики быстро всё улетает. Собственно данный пример мне очень подходит своей "защитой от дурака" в том плане что бы из поля ввода на компе загонять десятичные значения через com порт на микроконтроллер. Радует именн защита от дурака при копировании. Всяких hex и прочего по невнимательности.

Однако остался неосвещенным такой пунктик как ограничение числа вводимых знаков. Мне нужно с ограничением. И под это дело как нельзя лучше подходит Dec2Hex лежащий в исходниках. Нельзя ли исходный код оного увидеть. Или хотя бы кусок где реализован контроль числа вводимых знаков. Можно на мыло andrey_777@bk.ru

Если уж совсем нет, то в какую сторону смотреть реализацию. там API или ещё как.  invoke  SendDlgItemMessage,[hwnddlg],ID_SPD,EM_LIMITTEXT,3,0 я нашел. Но от вставки копированием 100500 знаков она увы не спасает.
ManHunter (01.03.2020 в 16:34):
Василий, доработал и под такую ситуевину, спасибо! Архив и пример в статье обновлены.
Василий (01.03.2020 в 15:37):
Небольшой баг - если выделить число в edit'e и вставить отрицательное, а потом опять повторить, то минус теряется через раз

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

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

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