Преобразование символических ссылок в путь к файлу
"По следам наших публикаций". В одной из недавних статей я использовал код для преобразования символических ссылок на файл в привычный путь. Немного поразмыслив, я решил доработать его до полноценной универсальной функции, которая будет приводить любые "кривые" пути и символические ссылки к человекопонятному виду. Функция разворачивает переменные окружения, исправляет обратные слеши, а также обрабатывает символические ссылки вида "\SystemRoot\explorer.exe", "\Device\HarddiskVolume1\WINDOWS\win.ini", "\??\E:\asm\" и "file:///C:/Windows/explorer.exe". Поддерживаются файлы, каталоги и диски.Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Функция нормализации пути к файлу или каталогу
- ;------------------------------------------------------------
- ; Параметры:
- ; lpPath - указатель на исходный путь
- ; lpNorm - указатель на строку с нормализованным путем
- ; На выходе:
- ; EAX = 1 - путь успешно нормализован
- ; EAX = 0 - путь не найден
- ;------------------------------------------------------------
- proc normalize_path lpPath:DWORD,lpNorm:DWORD
- _OBJ_CASE_INSENSITIVE = 0x00000040
- _FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
- _FILE_READ_DATA = 1
- ; Тип запрашиваемой информации
- _ObjectNameInformation = 1
- ; Структура для получения результата
- struct _OBJECT_NAME_INFORMATION
- Length dw ?
- MaximumLength dw ?
- Buffer dd ?
- String rw MAX_PATH-1
- ends
- ; Структура для юникодной строки
- struct _UNICODE_STRING
- Length dw ?
- MaximumLength dw ?
- Buffer dd ?
- ends
- ; Структура для статуса выполнения операции
- struct _IO_STATUS_BLOCK
- Status dd ?
- Pointer dd ?
- Information dd ?
- ends
- ; Структура для атрибутов объекта
- struct _OBJECT_ATTRIBUTES
- Length dd ?
- RootDirectory dd ?
- ObjectName dd ?
- Attributes dd ?
- SecurityDescriptor dd ?
- SecurityQualityOfService dd ?
- ends
- locals
- hFile dd ?
- tmp_name rw MAX_PATH-1
- dDisk dd ?
- szDisk rw 4
- result dd ?
- szTmp rw MAX_PATH-1
- ObjectName _UNICODE_STRING
- ObjectAttributes _OBJECT_ATTRIBUTES
- IoStatusBlock _IO_STATUS_BLOCK
- dwSize dd ?
- ObjectNameInfo _OBJECT_NAME_INFORMATION
- endl
- pusha
- mov [result],0
- ; Преобразовать File URL в путь
- lea eax,[dwSize]
- mov dword [eax],MAX_PATH*2
- lea ebx,[szTmp]
- invoke PathCreateFromUrl,[lpPath],ebx,eax,0
- or eax,eax
- jz @f
- mov ebx,[lpPath]
- @@:
- ; Развернуть переменные окружения
- lea eax,[tmp_name]
- invoke ExpandEnvironmentStrings,ebx,eax,MAX_PATH*2
- ; Исправить обратные слеши
- lea ebx,[tmp_name]
- mov esi,ebx
- mov edi,ebx
- .loc_fix_slashes:
- lodsw
- cmp ax,'/'
- jne @f
- mov ax,'\'
- @@:
- stosw
- or ax,ax
- jnz .loc_fix_slashes
- ; Путь уже нормализован?
- cmp word [ebx+2],':'
- jne @f
- ; Больше ничего делать не требуется
- invoke lstrcpy,[lpNorm],ebx
- mov [result],1
- jmp .loc_ret
- @@:
- lea eax,[ObjectName]
- invoke RtlInitUnicodeString,eax,ebx
- push _FILE_SYNCHRONOUS_IO_NONALERT
- push FILE_SHARE_READ+FILE_SHARE_WRITE+FILE_SHARE_DELETE
- lea eax,[IoStatusBlock]
- push eax
- lea ebx,[ObjectAttributes]
- push ebx
- mov [ebx+_OBJECT_ATTRIBUTES.Length],sizeof._OBJECT_ATTRIBUTES
- lea eax,[ObjectName]
- mov [ebx+_OBJECT_ATTRIBUTES.ObjectName],eax
- mov [ebx+_OBJECT_ATTRIBUTES.Attributes],_OBJ_CASE_INSENSITIVE
- push _FILE_READ_DATA+SYNCHRONIZE
- lea eax,[hFile]
- push eax
- invoke NtOpenFile
- or eax,eax
- jz @f
- ; Файл открыть не удалось
- lea ebx,[tmp_name]
- invoke lstrcpy,[lpNorm],ebx
- jmp .loc_ret
- @@:
- lea ebx,[dwSize]
- ; Получить размер информации
- invoke NtQueryObject,[hFile],_ObjectNameInformation,NULL,0,ebx
- ; Получить информацию об объекте
- push ebx
- push [dwSize]
- lea eax,[ObjectNameInfo]
- push eax
- invoke NtQueryObject,[hFile],_ObjectNameInformation
- or eax,eax
- jz @f
- ; Имя объекта получить не удалось
- lea ebx,[tmp_name]
- invoke lstrcpy,[lpNorm],ebx
- invoke NtClose,[hFile]
- jmp .loc_ret
- @@:
- ; Сохранить полученное имя
- lea eax,[ObjectNameInfo]
- lea eax,[eax+_OBJECT_NAME_INFORMATION.String]
- lea ebx,[tmp_name]
- invoke lstrcpy,ebx,eax
- ; Закрыть открытый файл
- invoke NtClose,[hFile]
- ; Перебрать все диски, начиная с A:
- mov [dDisk],1
- .loc_find_drive:
- invoke GetLogicalDrives
- @@:
- test eax,[dDisk]
- jnz @f
- shl [dDisk],1
- jnz @b
- ; Диск определить не удалось
- lea ebx,[tmp_name]
- invoke lstrcpy,[lpNorm],ebx
- jmp .loc_ret
- @@:
- lea eax,[szDisk]
- mov dword [eax],0x003A0041 ; 'A:' в юникоде
- mov dword [eax+4],0
- mov ecx,[dDisk]
- bsr ecx,ecx
- add dword [eax],ecx
- ; Следующий диск
- shl [dDisk],1
- ; Преобразовать букву диска в строку типа \Device\HarddiskVolume1
- lea eax,[szDisk]
- lea ebx,[szTmp]
- invoke QueryDosDevice,eax,ebx,MAX_PATH*2
- or eax,eax
- ; Диска нет, пропускаем
- jz @f
- ; Сравнить начало пути со строкой устройства
- mov ecx,eax
- lea esi,[tmp_name]
- mov edi,ebx
- repe cmpsw
- or ecx,ecx
- jnz @f
- ; Только буква диска
- lea eax,[szDisk]
- invoke lstrcpy,[lpNorm],eax
- mov [result],1
- jmp .loc_ret
- @@:
- cmp ecx,1
- jne .loc_find_drive
- ; Дополнительная проверка корректности пути
- dec esi
- dec esi
- cmp word [esi],'\'
- jne .loc_find_drive
- ; Диск + оставшийся путь к файлу
- lea eax,[szDisk]
- invoke lstrcpy,[lpNorm],eax
- invoke lstrcat,[lpNorm],esi
- mov [result],1
- .loc_ret:
- popa
- mov eax,[result]
- ret
- endp
В современных системах, начиная с Windows Vista, вы можете встретить странные пути вида {D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}\taskmgr.exe. Первая часть пути в данном случае - это один из так называемых KNOWNFOLDERID, то есть идентификатор стандартной папки Windows. Чтобы преобразовать GUID в обычный путь, надо сперва отконвертировать строку GUID в числовой вид. Сделать это можно вручную, но лучше воспользоваться штатной функцией GUIDFromString. Она есть сразу в двух библиотеках - shell32.dll и shlwapi.dll, но импортировать ее по имени нельзя, только по ординалу. В shell32.dll это будет ординал 703 для функции GUIDFromStringA и 704 для GUIDFromStringW, в shlwapi.dll, соответственно, ординал 269 для функции GUIDFromStringA и 270 для GUIDFromStringW. Получив GUID, преобразуем его в путь при помощи функции SHGetKnownFolderPath.
Code (Assembler) : Убрать нумерацию
- ; Загрузить библиотеку shell32
- invoke LoadLibrary,shell32
- ; Получить адрес GUIDFromStringW
- invoke GetProcAddress,eax,704
- or eax,eax
- jz loc_exit
- mov [GUIDFromString],eax
- ; Получить GUID из строки
- stdcall [GUIDFromString],fname,guid
- or eax,eax
- jz loc_exit
- ; Получить имя папки из GUID
- invoke SHGetKnownFolderPath,guid,0,NULL,tmp
- or eax,eax
- jnz loc_exit
- ; Скопировать имя папки в нормализованный путь
- invoke lstrcpy,normalized,[tmp]
- ; Найти в строке путь после GUID
- mov esi,fname
- @@:
- lodsw
- cmp ax,'}'
- jne @b
- ; Добавить имя файла
- invoke lstrcat,normalized,esi
Для тех, кто любит погорячее, то же самое можно сделать при помощи COM-объектов. Ну а что, раз уж есть GUID'ы, значит где-то неподалеку должны быть и COM'ы. Первым делом пачка констант, GUID и интерфейсов, которые понадобятся для работы.
Code (Assembler) : Убрать нумерацию
- CLSCTX_INPROC_SERVER = 1
- S_OK = 0
- ; GUID {4DF0C730-DF9D-4AE3-9153-AA6B82E9795A}
- CLSID_KnownFolderManager dd 04DF0C730h
- dw 0DF9Dh
- dw 04AE3h
- db 091h, 053h, 0AAh, 06Bh, 082h, 0E9h, 079h, 05Ah
- ; GUID {8BE2D872-86AA-4D47-B776-32CCA40C7018}
- IID_IKnownFolderManager dd 08BE2D872h
- dw 086AAh
- dw 04D47h
- db 0B7h, 076h, 032h, 0CCh, 0A4h, 00Ch, 070h, 018h
- ; IID_IKnownFolderManager Interface
- struct IKnownFolderManager
- ; IUnknown
- QueryInterface dd ?
- AddRef dd ?
- Release dd ?
- ; IKnownFolderManager
- FolderIdFromCsidl dd ?
- FolderIdToCsidl dd ?
- GetFolderIds dd ?
- GetFolder dd ?
- GetFolderByName dd ?
- RegisterFolder dd ?
- UnregisterFolder dd ?
- FindFolderFromPath dd ?
- FindFolderFromIDList dd ?
- Redirect dd ?
- ends
- ; IID_IKnownFolder Interface
- struct IKnownFolder
- ; IUnknown
- QueryInterface dd ?
- AddRef dd ?
- Release dd ?
- ; IKnownFolder
- GetId dd ?
- GetCategory dd ?
- GetShellItem dd ?
- GetPath dd ?
- SetPath dd ?
- GetIDList dd ?
- GetFolderType dd ?
- GetRedirectionCapabilities dd ?
- GetFolderDefinition dd ?
- ends
Code (Assembler) : Убрать нумерацию
- invoke CoInitialize,0
- ; Загрузить библиотеку shell32
- invoke LoadLibrary,shell32
- ; Получить адрес GUIDFromStringW
- invoke GetProcAddress,eax,704
- or eax,eax
- jz loc_exit
- mov [GUIDFromString],eax
- ; Получить GUID из строки
- stdcall [GUIDFromString],fname,guid
- or eax,eax
- jz loc_exit
- ; Создать объект
- invoke CoCreateInstance,CLSID_KnownFolderManager,NULL,\
- CLSCTX_INPROC_SERVER,\
- IID_IKnownFolderManager,pKFMDisp
- cmp eax,S_OK
- jne loc_exit
- ; Получить имя папки из GUID
- mov eax, [pKFMDisp]
- mov eax, [eax]
- stdcall dword [eax+IKnownFolderManager.GetFolder],\
- [pKFMDisp],guid,pKFDisp
- or eax,eax
- jnz loc_exit
- mov eax, [pKFDisp]
- mov eax, [eax]
- stdcall dword [eax+IKnownFolder.GetPath],[pKFDisp],0,tmp
- or eax,eax
- jnz loc_exit
- ; Скопировать имя папки в нормализованный путь
- invoke lstrcpy,normalized,[tmp]
- ; Найти в строке путь после GUID
- mov esi,fname
- @@:
- lodsw
- cmp ax,'}'
- jne @b
- ; Добавить имя файла
- invoke lstrcat,normalized,esi
- mov eax, [pKFDisp]
- mov eax, [eax]
- stdcall dword [eax+IKnownFolder.Release],[pKFDisp]
- mov eax, [pKFMDisp]
- mov eax, [eax]
- stdcall dword [eax+IKnownFolderManager.Release],[pKFMDisp]
- ; Удалить объект
- invoke CoUninitialize
В приложении примеры программ с исходными текстами, в которой используются функции преобразования из статьи. Различные варианты символических ссылок закомментированы в исходнике.
Просмотров: 1854 | Комментариев: 2
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(24.04.2021 в 17:26):
Добавил информацию о преобразовании различными способами KnownFolder-путей в обычные. Архив обновлен. Можно было бы нарисовать отдельную статью, но пусть тут будет все по одной теме.
ManHunter
(13.02.2019 в 15:10):
Добавил обработку File URL типа "file:///C:/Windows/explorer.exe"
Архив обновлен.
Архив обновлен.
Добавить комментарий
Заполните форму для добавления комментария