Blog. Just Blog

Drag'n'Drop файлов в консольное окно

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Очередная порция странного. На этот раз я решил попробовать реализовать обработку перетаскивания файлов в консольное окно. Для обычных GUI-приложений с этим нет вообще никаких сложностей, а вот с консольными приложениями придется повозиться. Вопреки тиражируемым в этих ваших интернетах статьям, с консольными окнами не работают никакие стандартные способы перетаскивания данных типа манипулирования с расширенными стилями консольного окна WS_EX_ACCEPTFILES, с принудительным назначением ему DragAcceptFiles, с субклассированием на предмет обработки сообщения WM_DROPFILES или даже через реализацию интерфейса IDropTarget. Ни-че-го. В тоже время, если открыть системный командный интерпретатор cmd.exe и попытаться перетащить на его консольное окно какой-нибудь файл из Проводника или файлового менеджера, то в командной строке появится полный путь к этому файлу. Значит какие-то зачатки технологии Drag'n'Drop для консольных окон все-таки имеются.

С помощью обработки событий консоли удалось выяснить, что при перетаскивании файла в консольное окно, путь к нему передается посимвольно через события KEY_EVENT. При более детальном анализе я также выяснил, что четкой системы тут нет, часть символов передается с событием нажатия и отпускания клавиши, а часть только с отпусканием клавиш. Единственные закономерности заключаются в том, что такие символы передаются обязательно со счетчиком повторений равным 1 и точно с флагом отпускания клавиш.

Также не стоит забывать про различные программы-обработчики клавиатуры типа Punto Switcher, которые самостоятельно обрабатывают введенные символы и затем могут менять их раскладку и последовательность через эмуляцию ввода с клавиатуры.

Опытным путем удалось подобрать работоспособную последовательность действий с очередью сообщений консоли. Проверено на Windowx XP, Windows 7 и Windows 10 разных разрядностей, все работает.
  1.         ; Получить хэндл стандартного ввода stdout
  2.         invoke  GetStdHandle,STD_OUTPUT_HANDLE
  3.         mov     [hOut],eax
  4.  
  5.         ; Получить хэндл стандартного ввода stdin
  6.         invoke  GetStdHandle,STD_INPUT_HANDLE
  7.         mov     [hIn],eax
  8.  
  9.         ; Инициализировать начальные значения
  10.         invoke  GetTickCount
  11.         mov     [timer],eax
  12.         mov     edi,buff
  13. loc_loop:
  14.         invoke  GetTickCount
  15.         mov     ebx,eax
  16.         sub     eax,[timer]
  17.  
  18.         ; Дельта таймера
  19.         cmp     eax,200
  20.         jb      loc_next
  21.  
  22.         ; Что-то уже удалось записать в буфер?
  23.         invoke  lstrlen,buff
  24.         cmp     eax,2
  25.         jbe     @f
  26.  
  27.         ; buff -> переданное имя файла
  28.         ; выполнить с ним нужные действия
  29. @@:
  30.         ; Сбросить буфер
  31.         mov     byte [buff],0
  32.         mov     [timer],ebx
  33.         mov     edi,buff
  34. loc_next:
  35.         ; Получить количество событий в очереди
  36.         invoke  GetNumberOfConsoleInputEvents,[hIn],tmp
  37.         cmp     [tmp],0
  38.         je      loc_loop
  39.  
  40.         ; Прочитать одно событие из очереди
  41.         invoke  ReadConsoleInput,[hIn],input,1,tmp
  42.         ; Обработка в зависимости от типа события
  43.         cmp     word [input.EventType],KEY_EVENT
  44.         jne     loc_loop
  45.  
  46.         ; Защита от автоповторов
  47.         cmp     [input.KeyEvent.wRepeatCount],1
  48.         je      @f
  49.  
  50.         ; Сбросить буфер
  51.         invoke  FlushConsoleInputBuffer,[hIn]
  52.         mov     byte [buff],0
  53.         mov     edi,buff
  54. @@:
  55.         ; KEY_EVENT
  56.         cmp     [input.KeyEvent.bKeyDown],0
  57.         jne     loc_loop
  58.         movzx   eax,byte[input.KeyEvent.AsciiChar]
  59.         cmp     eax,20
  60.         jb      loc_loop
  61.  
  62.         ; Сохранить символ в буфер
  63.         stosw
  64.         dec     edi
  65.  
  66.         jmp     loc_loop
Поясню словами, что тут происходит. В цикле обрабатываются события консоли. Если они связаны с клавиатурным вводом, то проверяется выполнение обязательных условий, а затем побуквенно формируется строка из переданных консольке символов. Если интервал между событиями превышен, то строка или сбрасывается, или принимается как введенные данные, если она достаточно длинная. При перетаскивании файла в консольное окно интервал между "вводом" символов минимальный, так что такой алгоритм гарантированно защитит консольное приложение от ручного ввода. С Punto Switcher'ом немного сложнее, в случае смены раскладки он передает модифицированную строку через эмуляцию клавиатурного ввода, то есть тоже с минимальным интервалом. В этом случае, да и вообще, следуя правилам хорошего тона в программировании, придется проверять наличие файла или каталога на диске с помощью функции PathFileExists или подобных.

Ну и еще одно замечание. Описанным в статье методом можно обрабатывать только один файл. Множественная передача файлов через перетаскивание в консольное окно невозможна.

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

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

Console.Drag.n.Drop.Demo.zip (2,164 bytes)


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

Метки: Assembler

Комментарии

Отзывы посетителей сайта о статье
ManHunter (10.01.2024 в 19:07):
Это как с командной строкой, можно обрабатывать параметры целиком в кавычках, а можно кавычки разворачивать, убирая лидирующие и финальные пробелы. Зависит от ситуации. Тут то же самое.

Ошибку поправил, спасибо. Рука дрогнула.
Petya (10.01.2024 в 18:58):
Можно пояснить, почему на усмотрение?
И, кстати, ещё одна мелкая ошибка - в слове Received буквы местами поменялись, стало Recieved.
ManHunter (10.01.2024 в 18:38):
И не должна обрабатывать, это на усмотрение разработчика.
Petya (10.01.2024 в 18:32):
Тут есть ещё одна особенность, программа её не обрабатывает. Если в полном пути есть пробелы, то он закавычивается и перед передачей его API-функциям кавычки надо удалить.
Сделано, очевидно, ради удобства командной строки.

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

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

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