Blog. Just Blog

Устраняем косяки функций PathRemoveExtension и PathRenameExtension

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
В комментариях к статьям иногда проскакивают мысли, мол, зачем изобретать велосипед, если уже существуют готовые 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

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

Когда я столкнулся с таким поведением штатных функций, пришлось написать свои реализации, которые дают ожидаемый результат.
  1. ;----------------------------------------------------------
  2. ; Удаление расширения файла из строки
  3. ;----------------------------------------------------------
  4. proc PathRemoveExtensionTrue lpszPath:DWORD
  5.         pusha
  6.  
  7.         mov     esi,[lpszPath]
  8.         invoke  lstrlen,esi
  9.         add     esi,eax
  10.         inc     esi
  11. @@:
  12.         dec     esi
  13.         cmp     esi,[lpszPath]
  14.         je      .loc_ret
  15.         cmp     byte [esi],'\'
  16.         je      .loc_ret
  17.         cmp     byte [esi],'/'
  18.         je      .loc_ret
  19.         cmp     byte [esi],' '
  20.         je      .loc_ret
  21.         cmp     byte [esi],':'
  22.         je      .loc_ret
  23.         cmp     byte [esi],'.'
  24.         jne     @b
  25.         dec     esi
  26.         cmp     esi,[lpszPath]
  27.         je      .loc_ret
  28.         cmp     byte [esi],'\'
  29.         je      .loc_ret
  30.         cmp     byte [esi],'/'
  31.         je      .loc_ret
  32.         mov     byte [esi+1],0
  33. .loc_ret:
  34.         popa
  35.         ret
  36. endp
  1. proc PathRenameExtensionTrue lpszPath:DWORD,lpszExt:DWORD
  2.         pusha
  3.  
  4.         mov     esi,[lpszPath]
  5.         invoke  lstrlen,esi
  6.         or      eax,eax
  7.         jz      .loc_ret
  8.         add     esi,eax
  9.         cmp     byte [esi-1],'\'
  10.         je      .loc_ret
  11.         cmp     byte [esi-1],'/'
  12.         je      .loc_ret
  13. @@:
  14.         dec     esi
  15.         cmp     byte [esi],'\'
  16.         je      @f
  17.         cmp     byte [esi],'/'
  18.         je      @f
  19.         cmp     byte [esi],':'
  20.         je      @f
  21.         cmp     byte [esi],' '
  22.         je      @f
  23.         cmp     esi,[lpszPath]
  24.         je      @f
  25.         cmp     byte [esi],'.'
  26.         jne     @b
  27.         dec     esi
  28.         cmp     esi,[lpszPath]
  29.         je      @f
  30.         cmp     byte [esi],'\'
  31.         je      @f
  32.         cmp     byte [esi],'/'
  33.         je      @f
  34.         cmp     byte [esi],':'
  35.         je      @f
  36.         mov     byte [esi+1],0
  37. @@:
  38.         invoke  lstrcat,[lpszPath],[lpszExt]
  39.  
  40. .loc_ret:
  41.         popa
  42.         ret
  43. 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
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (03.01.2025 в 17:15):
Добавил обработку слешей и пробела в расширении. Про слеши, кстати, в статье было упомянуто. С поддержкой юникода тоже проблем нет, достаточно сменить разрядность операндов с byte на word и этого, в основном, хватит. А китайцы со своими кодовыми страницами пусть сами разбираются, у них и без меня специалистов хватает.

Спасибо за правильную наводку, обе функции в статье дополнил.
ГостьY (03.01.2025 в 16:31):
и еще: строчка может быть MBCS
ГостьY (03.01.2025 в 15:58):
У этих функций еще есть проблема в том, что они не понимают прямой слэш в путях: '/'.
Однако, в стандартных функциях есть то, что не реализовано в ваших:
расширение файла не может содержать пробел.
В этом случае оно считается частью имени.
Если назвать файл "aaa.bb bb", то на него нельзя ассоциировать программу по расширению.

* можно посмотреть код XP/03 в утекших исходниках Win2K3\NT\shell\shlwapi\path.c

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

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

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