При всех удобствах Windows некоторые моменты меня очень сильно раздражают. Особенно поведение системы при вызове диалогов открытия файлов. Сперва немного предыстории. При работе с файлами через функцию
GetOpenFileName или
GetSaveFileName в структуре
OPENFILENAME есть возможность указать путь, который должен открыться по умолчанию. Если это значение не задано, то система сама где-то запоминает папку, в которой последний раз был удачно открыт файл (то есть окно выбора файла было закрыто через кнопку "Ok"). Где именно хранится эта информация - я пока не выяснил, да и не особо надо. Второй вариант. Предположим, что некоторая программа самостоятельно запоминает путь к папке, в которой последний раз ею выполнялись какие-то действия с файлами. Это может быть, например, текстовый редактор, просмотрщик графики и т.п., не суть. Главное, что задумка очень хорошая и правильная. При следующем запуске или вызове диалога выбора файла в соответствующее поле OPENFILENAME будет подставлен сохраненный путь и пользователь продолжит работу с того места, где он в прошлый раз остановился. Что-то типа такого:
...
invoke GetModuleHandle,0
mov [ofn.hInstance],eax
mov [ofn.lStructSize], sizeof.OPENFILENAME
mov [ofn.hwndOwner],0
mov [ofn.nMaxFile],MAX_PATH
mov [ofn.lpstrFile],buff
; Открывать с последней сохраненной папки
mov [ofn.lpstrInitialDir],saved_dir
mov [ofn.Flags],OFN_EXPLORER+OFN_FILEMUSTEXIST
invoke GetOpenFileName,ofn
...
Неадекватное, на мой взгляд, поведение системы заключается в следующем. Вполне может возникнуть ситуация, что какая-то часть из сохраненного или запрошенного пути пропала. Например, я в просмотрщике рассортировал папку с фотографиями, в графическом редакторе подправил несколько файлов, а затем в файловом менеджере перенес всю папку с фотографиями в другое место на диске. В этом случае при попытке вернуться к просмотру в просмотрщике, повторно вызвать диалог открытия или сохранения файла в графическом редакторе, при любом раскладе в качестве дефолтного пути будет открыта какая-нибудь херня типа Библиотеки, Моих документов или вообще папки, куда установлена программа. Закономерности я тут тоже не уловил, видимо принятие решения остается за Windows и зависит от уровня осадков в Зимбабве. В итоге пользователю приходится снова топать весь путь из библиотеки до места работы.
После очередной серии чудесатых чудес я решил сделать для себя небольшую вспомогательную функцию. Она проверяет сохраненный путь и возвращает последнюю папку максимального уровня вложенности, которая существует на диске в текущий момент. Например, если ваша программа в последнем сеансе работы сохранила, а затем пытается открыть путь
D:\PICTURES\Путешествия\2011\Разобрать\Китай\NIKOND90\001
но при этом папки "\Китай" и, соответственно, вложенных в нее папок уже не существует, то должна открываться папка
D:\PICTURES\Путешествия\2011\Разобрать
и никак иначе! По-моему, это единственно правильное поведение системы. Почему разработчики Windows до сих пор открывают непонятно что вместо ПОСЛЕДНЕЙ ДОСТУПНОЙ папки из запрошенного пути - непонятно. Какая-то дефолтная папка может открываться только в одном единственном случае - когда ВЕСЬ запрошенный путь, включая букву диска, недоступен.
;------------------------------------------------------------
; Функция проверки доступности пути в файловой системе
; (C) ManHunter / PCL
; http://www.manhunter.ru
;------------------------------------------------------------
; Параметры:
; lpRaw - указатель на буфер размером MAX_PATH, в который
; записан проверяемый путь
; lpGood - указатель на буфер размером MAX_PATH, в который
; будет записан максимально доступный путь
;
; На выходе:
; EAX=0 - ни один из составляющих пути, включая носитель, не
; доступен
; EAX=1 - по крайней мере один из составляющих пути доступен,
; результат без финального слеша записан в буфер lpGood
;------------------------------------------------------------
proc GetLastValidFolder lpRaw:DWORD, lpGood:DWORD
locals
result dd ?
old_dir rb MAX_PATH
new_dir rb MAX_PATH
endl
pusha
; Сохранить текущую директорию
lea eax,[old_dir]
invoke GetCurrentDirectory,MAX_PATH,eax
; Скопировать поверяемый путь
lea esi,[new_dir]
invoke lstrcpy,esi,[lpRaw]
mov edi,esi
invoke lstrlen,esi
or eax,eax
jz .loc_bad
dec eax
add edi,eax
; Исправить слеши
.loc_fix_slash:
cmp byte [esi+eax],'/'
jne @f
mov byte [esi+eax],'\'
@@:
dec eax
or eax,eax
jnz .loc_fix_slash
.loc_chk:
; Попробовать установить текущую директорию
invoke SetCurrentDirectory,esi
or eax,eax
jne .loc_ok
.loc_scan:
mov byte [edi],0
dec edi
; Сканируем с конца до ближайшего слеша
cmp byte [edi],'\'
je .loc_chk
; Добрались до начала строки?
cmp edi,esi
jne .loc_scan
.loc_bad:
; Результат - ошибка
mov [result],0
; Обнулить строку
mov eax,[lpGood]
mov byte [eax],0
jmp .loc_ret
.loc_ok:
; Убрать финальный слеш
cmp byte [edi],'\'
jne @f
mov byte [edi],0
@@:
; Скопировать последний правильный путь
invoke lstrcpy,[lpGood],esi
; Результат - успешно
mov [result],1
.loc_ret:
; Вернуть на место текущую директорию
lea eax,[old_dir]
invoke SetCurrentDirectory,eax
popa
; Записать результат в EAX
mov eax,[result]
ret
endp
На входе передаются два указателя: lpRaw - указатель на исходную строку пути, lpGood - указатель на буфер-приемник, куда будет записан последний максимально доступный путь. Исходный путь может содержать не только папки, но и имя файла, в этом случае функция вернет только папки. Также функция исправляет слеши, приводя их к принятому в Windows виду "\". Результат выполнения возвращается в регистре EAX, если он равен 0, то не доступен ни один элемент проверяемого пути, включая диск. Если EAX=1, то доступный путь найден. Функция самодостаточная и не требует наличия каких-либо дополнительных переменных для своей работы.
Я прекрасно понимаю, что ждать подобной милости от разработчиков Windows бесполезно, поэтому предлагаю программистам взять на себя заботу об удобстве пользователей. Ведь именно из таких, казалось бы, незначительных мелочей складывается впечатление о программном продукте и разработчике в целом.
В приложении пример программы с исходным текстом, в которой используется описанная выше функция для проверки доступности пути.