Blog. Just Blog

Диалог открытия файлов и юзабилити Windows

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
При всех удобствах Windows некоторые моменты меня очень сильно раздражают. Особенно поведение системы при вызове диалогов открытия файлов. Сперва немного предыстории. При работе с файлами через функцию GetOpenFileName или GetSaveFileName в структуре OPENFILENAME есть возможность указать путь, который должен открыться по умолчанию. Если это значение не задано, то система сама где-то запоминает папку, в которой последний раз был удачно открыт файл (то есть окно выбора файла было закрыто через кнопку "Ok"). Где именно хранится эта информация - я пока не выяснил, да и не особо надо. Второй вариант. Предположим, что некоторая программа самостоятельно запоминает путь к папке, в которой последний раз ею выполнялись какие-то действия с файлами. Это может быть, например, текстовый редактор, просмотрщик графики и т.п., не суть. Главное, что задумка очень хорошая и правильная. При следующем запуске или вызове диалога выбора файла в соответствующее поле OPENFILENAME будет подставлен сохраненный путь и пользователь продолжит работу с того места, где он в прошлый раз остановился. Что-то типа такого:
  1.         ...
  2.         invoke  GetModuleHandle,0
  3.         mov     [ofn.hInstance],eax
  4.         mov     [ofn.lStructSize], sizeof.OPENFILENAME
  5.         mov     [ofn.hwndOwner],0
  6.         mov     [ofn.nMaxFile],MAX_PATH
  7.         mov     [ofn.lpstrFile],buff
  8.         ; Открывать с последней сохраненной папки
  9.         mov     [ofn.lpstrInitialDir],saved_dir
  10.         mov     [ofn.Flags],OFN_EXPLORER+OFN_FILEMUSTEXIST
  11.         invoke  GetOpenFileName,ofn
  12.         ...
Неадекватное, на мой взгляд, поведение системы заключается в следующем. Вполне может возникнуть ситуация, что какая-то часть из сохраненного или запрошенного пути пропала. Например, я в просмотрщике рассортировал папку с фотографиями, в графическом редакторе подправил несколько файлов, а затем в файловом менеджере перенес всю папку с фотографиями в другое место на диске. В этом случае при попытке вернуться к просмотру в просмотрщике, повторно вызвать диалог открытия или сохранения файла в графическом редакторе, при любом раскладе в качестве дефолтного пути будет открыта какая-нибудь херня типа Библиотеки, Моих документов или вообще папки, куда установлена программа. Закономерности я тут тоже не уловил, видимо принятие решения остается за Windows и зависит от уровня осадков в Зимбабве. В итоге пользователю приходится снова топать весь путь из библиотеки до места работы.

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

D:\PICTURES\Путешествия\2011\Разобрать\Китай\NIKOND90\001

но при этом папки "\Китай" и, соответственно, вложенных в нее папок уже не существует, то должна открываться папка

D:\PICTURES\Путешествия\2011\Разобрать

и никак иначе! По-моему, это единственно правильное поведение системы. Почему разработчики Windows до сих пор открывают непонятно что вместо ПОСЛЕДНЕЙ ДОСТУПНОЙ папки из запрошенного пути - непонятно. Какая-то дефолтная папка может открываться только в одном единственном случае - когда ВЕСЬ запрошенный путь, включая букву диска, недоступен.
  1. ;------------------------------------------------------------
  2. ; Функция проверки доступности пути в файловой системе
  3. ; (C) ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;------------------------------------------------------------
  6. ; Параметры:
  7. ; lpRaw - указатель на буфер размером MAX_PATH, в который
  8. ; записан проверяемый путь
  9. ; lpGood - указатель на буфер размером MAX_PATH, в который
  10. ; будет записан максимально доступный путь
  11. ;
  12. ; На выходе:
  13. ; EAX=0 - ни один из составляющих пути, включая носитель, не
  14. ; доступен
  15. ; EAX=1 - по крайней мере один из составляющих пути доступен,
  16. ; результат без финального слеша записан в буфер lpGood
  17. ;------------------------------------------------------------
  18. proc    GetLastValidFolder lpRaw:DWORD, lpGood:DWORD
  19.         locals
  20.                 result  dd ?
  21.                 old_dir rb MAX_PATH
  22.                 new_dir rb MAX_PATH
  23.         endl
  24.  
  25.         pusha
  26.  
  27.         ; Сохранить текущую директорию
  28.         lea     eax,[old_dir]
  29.         invoke  GetCurrentDirectory,MAX_PATH,eax
  30.  
  31.         ; Скопировать поверяемый путь
  32.         lea     esi,[new_dir]
  33.         invoke  lstrcpy,esi,[lpRaw]
  34.         mov     edi,esi
  35.         invoke  lstrlen,esi
  36.         or      eax,eax
  37.         jz      .loc_bad
  38.         dec     eax
  39.         add     edi,eax
  40.  
  41.         ; Исправить слеши
  42. .loc_fix_slash:
  43.         cmp     byte [esi+eax],'/'
  44.         jne     @f
  45.         mov     byte [esi+eax],'\'
  46. @@:
  47.         dec     eax
  48.         or      eax,eax
  49.         jnz     .loc_fix_slash
  50.  
  51. .loc_chk:
  52.         ; Попробовать установить текущую директорию
  53.         invoke  SetCurrentDirectory,esi
  54.         or      eax,eax
  55.         jne     .loc_ok
  56. .loc_scan:
  57.         mov     byte [edi],0
  58.         dec     edi
  59.  
  60.         ; Сканируем с конца до ближайшего слеша
  61.         cmp     byte [edi],'\'
  62.         je      .loc_chk
  63.  
  64.         ; Добрались до начала строки?
  65.         cmp     edi,esi
  66.         jne     .loc_scan
  67. .loc_bad:
  68.         ; Результат - ошибка
  69.         mov     [result],0
  70.         ; Обнулить строку
  71.         mov     eax,[lpGood]
  72.         mov     byte [eax],0
  73.         jmp     .loc_ret
  74. .loc_ok:
  75.         ; Убрать финальный слеш
  76.         cmp     byte [edi],'\'
  77.         jne     @f
  78.         mov     byte [edi],0
  79. @@:
  80.         ; Скопировать последний правильный путь
  81.         invoke  lstrcpy,[lpGood],esi
  82.         ; Результат - успешно
  83.         mov     [result],1
  84. .loc_ret:
  85.         ; Вернуть на место текущую директорию
  86.         lea     eax,[old_dir]
  87.         invoke  SetCurrentDirectory,eax
  88.  
  89.         popa
  90.         ; Записать результат в EAX
  91.         mov     eax,[result]
  92.         ret
  93. endp
На входе передаются два указателя: lpRaw - указатель на исходную строку пути, lpGood - указатель на буфер-приемник, куда будет записан последний максимально доступный путь. Исходный путь может содержать не только папки, но и имя файла, в этом случае функция вернет только папки. Также функция исправляет слеши, приводя их к принятому в Windows виду "\". Результат выполнения возвращается в регистре EAX, если он равен 0, то не доступен ни один элемент проверяемого пути, включая диск. Если EAX=1, то доступный путь найден. Функция самодостаточная и не требует наличия каких-либо дополнительных переменных для своей работы.

Я прекрасно понимаю, что ждать подобной милости от разработчиков Windows бесполезно, поэтому предлагаю программистам взять на себя заботу об удобстве пользователей. Ведь именно из таких, казалось бы, незначительных мелочей складывается впечатление о программном продукте и разработчике в целом.

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

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

Last.Valid.Folder.Demo.zip (3,143 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (08.11.2011 в 10:55):
Matsist, ну для правильных пацанов могли бы где-нибудь сделать секретную галочку, чтобы эта опция работала как надо :)
Matsist (06.11.2011 в 22:53):
Думаю, Microsoft сознательно сбрасывает несуществующий путь в МоиДокументы и т.п., чтобы сделать систему дуракозащищенной в этой части. Средняя секретарша испугается, если ей внезапно откроется папка сокрытая где-нибудь в недрах системы, а средняя школота еще и попробует навести в ней порядок.

Но вот реализовать твой алгоритм хотя бы в рамках папок типа "Мои документы" с их стороны было бы просто отлично!
ManHunter (03.11.2011 в 11:20):
Compiller, спасибо, буду знать.
Compiller (03.11.2011 в 10:06):
Windows хранит данные последних папок и много чего ещё в реестре в разделах со словом MRU в названии (Most Recently Used).
ManHunter (01.11.2011 в 14:41):
Тут все на чистом WinAPI, так что можно портировать куда угодно.
Dimas (01.11.2011 в 14:39):
Согласен, как меня вымораживал microangelo explorer когда не находил последний открытый файл - выдавал веселое сообщение, что файл не найден и выпинывал в мой компьютер...
На ассемблере не пишу, но саму идею возьму на вооружение, спасибо

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

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

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