
Устраняем косяки функций PathRemoveExtension и PathRenameExtension
В комментариях к статьям иногда проскакивают мысли, мол, зачем изобретать велосипед, если уже существуют готовые WinAPI. Ну так вот, в библиотеке shlwapi помимо кучи других полезных функций есть две функции для работы с расширениями файлов. PathRemoveExtension служит для удаления расширения из строки с путем файла, а PathRenameExtension для замены имеющегося расширения на другое. Это очень удобно в случае, когда, например, надо сгенерировать имя файла данных или файла с настройками по имени исполняемого файла. Но, к сожалению, у них есть фатальные косяки в реализации. Это проще показать на примерах. Вот удаление расширения:c:\test\file1.txt -> PathRemoveExtension -> c:\test\file1
c:\test\file2 -> PathRemoveExtension -> c:\test\file2
c:\test\.htaccess -> PathRemoveExtension -> c:\test\
c:\test\.htaccess.bak -> PathRemoveExtension -> c:\test\.htaccess
И аналогично для замены расширения, например, на ".ini":
c:\test\file1.txt -> PathRenameExtension -> c:\test\file1.ini
c:\test\file2 -> PathRenameExtension -> c:\test\file2.ini
c:\test\.htaccess -> PathRenameExtension -> c:\test\.ini
c:\test\ -> PathRenameExtension -> c:\test\.ini
c:\test\.htaccess.bak -> PathRenameExtension -> c:\test\.htaccess.ini
Как видите, если имя файла начинается с точки и при этом файл не имеет расширения, то обе функции работают неправильно. Кроме этого, функция замены расширения дает неправильный с точки зрения логики результат, если имя файла в строке вообще отсутствует.
Когда я столкнулся с таким поведением штатных функций, пришлось написать свои реализации, которые дают ожидаемый результат.
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Удаление расширения файла из строки
- ;----------------------------------------------------------
- proc PathRemoveExtensionTrue lpszPath:DWORD
- pusha
- mov esi,[lpszPath]
- invoke lstrlen,esi
- add esi,eax
- inc esi
- @@:
- dec esi
- cmp esi,[lpszPath]
- je .loc_ret
- cmp byte [esi],'\'
- je .loc_ret
- cmp byte [esi],'/'
- je .loc_ret
- cmp byte [esi],' '
- je .loc_ret
- cmp byte [esi],':'
- je .loc_ret
- cmp byte [esi],'.'
- jne @b
- dec esi
- cmp esi,[lpszPath]
- je .loc_ret
- cmp byte [esi],'\'
- je .loc_ret
- cmp byte [esi],'/'
- je .loc_ret
- mov byte [esi+1],0
- .loc_ret:
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- proc PathRenameExtensionTrue lpszPath:DWORD,lpszExt:DWORD
- pusha
- mov esi,[lpszPath]
- invoke lstrlen,esi
- or eax,eax
- jz .loc_ret
- add esi,eax
- cmp byte [esi-1],'\'
- je .loc_ret
- cmp byte [esi-1],'/'
- je .loc_ret
- @@:
- dec esi
- cmp byte [esi],'\'
- je @f
- cmp byte [esi],'/'
- je @f
- cmp byte [esi],':'
- je @f
- cmp byte [esi],' '
- je @f
- cmp esi,[lpszPath]
- je @f
- cmp byte [esi],'.'
- jne @b
- dec esi
- cmp esi,[lpszPath]
- je @f
- cmp byte [esi],'\'
- je @f
- cmp byte [esi],'/'
- je @f
- cmp byte [esi],':'
- je @f
- mov byte [esi+1],0
- @@:
- invoke lstrcat,[lpszPath],[lpszExt]
- .loc_ret:
- popa
- ret
- endp
c:\test\file1.txt -> PathRemoveExtensionTrue -> c:\test\file1
c:\test\file2 -> PathRemoveExtensionTrue -> c:\test\file2
c:\test\.htaccess -> PathRemoveExtensionTrue -> c:\test\.htaccess
c:\test\.htaccess.bak -> PathRemoveExtensionTrue -> c:\test\.htaccess
c:\test\file1.txt -> PathRenameExtensionTrue -> c:\test\file1.ini
c:\test\file2 -> PathRenameExtensionTrue -> c:\test\file2.ini
c:\test\.htaccess -> PathRenameExtensionTrue -> c:\test\.htaccess.ini
c:\test\ -> PathRenameExtensionTrue -> c:\test\
c:\test\.htaccess.bak -> PathRenameExtensionTrue -> c:\test\.htaccess.ini
Хочешь сделать хорошо - сделай это сам. При необходимости функции можно дополнительно усилить, например, автоматически исправлять слеши или добавить проверку на их "косой" вариант.
Просмотров: 510 | Комментариев: 3
Метки: Assembler

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

ГостьY
(03.01.2025 в 16:31):
и еще: строчка может быть MBCS

ГостьY
(03.01.2025 в 15:58):
У этих функций еще есть проблема в том, что они не понимают прямой слэш в путях: '/'.
Однако, в стандартных функциях есть то, что не реализовано в ваших:
расширение файла не может содержать пробел.
В этом случае оно считается частью имени.
Если назвать файл "aaa.bb bb", то на него нельзя ассоциировать программу по расширению.
* можно посмотреть код XP/03 в утекших исходниках Win2K3\NT\shell\shlwapi\path.c
Однако, в стандартных функциях есть то, что не реализовано в ваших:
расширение файла не может содержать пробел.
В этом случае оно считается частью имени.
Если назвать файл "aaa.bb bb", то на него нельзя ассоциировать программу по расширению.
* можно посмотреть код XP/03 в утекших исходниках Win2K3\NT\shell\shlwapi\path.c

Добавить комментарий
Заполните форму для добавления комментария

Спасибо за правильную наводку, обе функции в статье дополнил.