Пользовательская фильтрация в функции SHBrowseForFolder
Функцию SHBrowseForFolder я оттюнинговал уже давно, казалось бы уже все, дальше некуда. Ан нет, есть куда. Сегодня разберем еще одну замечательную возможность этой функции, а именно пользовательскую фильтрацию. С ее помощью вы можете выводить в древовидный список только те папки и файлы, которые считаете нужным. Это будет очень полезно, когда надо дополнительно подстраховать пользователя от возможной ошибки.Для реализации фильтрации используется технология COM, а это значит, что нам потребуется некоторое количество GUID'ов, структур и констант, которые не знает FASM.
Code (Assembler) : Убрать нумерацию
- ; GUID {00000000-0000-0000-C000-000000000046}
- IID_IUnknown \
- dd 000000000h
- dw 00000h
- dw 00000h
- db 0C0h, 000h, 000h, 000h, 000h, 000h, 000h, 046h
- ; GUID {C0A651F5-B48B-11D2-B5ED-006097C686F6}
- IID_IFolderFilterSite \
- dd 0C0A651F5h
- dw 0B48Bh
- dw 011D2h
- db 0B5h, 0EDh, 000h, 060h, 097h, 0C6h, 086h, 0F6h
- ; IID_IFolderFilterSite Interface
- struct IFolderFilterSite
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IFolderFilterSite
- SetFilter dd ? ; 00Ch
- ends
- ; GUID {9CC22886-DC8E-11D2-B1D0-00C04F8EEB3E}
- IID_IFolderFilter \
- dd 09CC22886h
- dw 0DC8Eh
- dw 011D2h
- db 0B1h, 0D0h, 000h, 0C0h, 04Fh, 08Eh, 0EBh, 03Eh
- ; IID_IFolderFilter Interface
- struct IFolderFilter
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IFolderFilter
- ShouldShow dd ? ; 00Ch
- GetEnumFlags dd ? ; 010h
- refcount dd ?
- ends
- ; IID_IShellFolder Interface
- struct IShellFolder
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IShellFolder
- ParseDisplayName dd ? ; 00Ch
- EnumObjects dd ? ; 010h
- BindToObject dd ? ; 014h
- BindToStorage dd ? ; 018h
- CompareIDs dd ? ; 01Ch
- CreateViewObject dd ? ; 020h
- GetAttributesOf dd ? ; 024h
- GetUIObjectOf dd ? ; 028h
- GetDisplayNameOf dd ? ; 02Ch
- SetNameOf dd ? ; 030h
- ends
- struct STRRET
- uType dd ?
- cStr rb 260
- ends
- SHCONTF_FOLDERS = 0x00000020
- SHCONTF_NONFOLDERS = 0x00000040
- SFGAO_FOLDER = 0x20000000
- SHGDN_FORPARSING = 0x8000
- BFFM_IUNKNOWN = 5
- ; Флаги открытия диалога
- BIF_RETURNONLYFSDIRS = 1
- BIF_DONTGOBELOWDOMAIN = 2
- BIF_BROWSEINCLUDEFILES = 0x00004000
- BIF_NEWDIALOGSTYLE = 0x00000040
Важное замечание. Для того, чтобы callback-функция получила сообщение BFFM_IUNKNOWN, окно выбора обязательно должно быть создано с флагом BIF_NEWDIALOGSTYLE. А объект IFolderFilter к этому моменту уже должен быть заполнен.
Code (Assembler) : Убрать нумерацию
- ; Настроить структуру IFolderFilter
- mov [FolderFilter.QueryInterface],IFolderFilter_QueryInterface
- mov [FolderFilter.AddRef],IFolderFilter_AddRef
- mov [FolderFilter.Release],IFolderFilter_Release
- mov [FolderFilter.ShouldShow],IFolderFilter_ShouldShow
- mov [FolderFilter.GetEnumFlags],IFolderFilter_GetEnumFlags
- ; Заполнить указатель на структуру
- mov [pFolderFilter],FolderFilter
- ; Заполнить структуру
- mov eax,[hwnddlg]
- mov [bi.hwndOwner],eax
- mov [bi.ulFlags],BIF_NEWDIALOGSTYLE+BIF_BROWSEINCLUDEFILES+\
- BIF_DONTGOBELOWDOMAIN+BIF_RETURNONLYFSDIRS
- mov [bi.pszDisplayName],szDisplayName
- mov [bi.lpszTitle],szTitle2
- ; Указатель на callback-функцию
- mov [bi.lpfn],BrowseCallbackProc
- ; Открыть диалог выбора папки
- invoke SHBrowseForFolder,bi
- ...
- ...
- ...
- ;---------------------------------------------
- ; Обработчик callback-функции
- ;---------------------------------------------
- proc BrowseCallbackProc hwnd:DWORD,uMsg:DWORD,lParam:DWORD,lpData
- cmp [uMsg],BFFM_IUNKNOWN
- jne .loc_ret
- .loc_interface:
- cmp [lParam],0
- je .loc_ret
- ; Получить адрес интерфейса IFolderFilterSite
- mov eax,[lParam]
- mov eax,[eax]
- stdcall dword [eax+IFolderFilterSite.QueryInterface],[lParam],\
- IID_IFolderFilterSite,pFilter
- ; Установить пользовательский фильтр
- mov eax,[pFilter]
- mov eax,[eax]
- stdcall dword [eax+IFolderFilterSite.SetFilter],[pFilter],\
- pFolderFilter
- ; Объект IFolderFilterSite больше не нужен
- mov eax,[pFilter]
- mov eax,[eax]
- stdcall dword [eax+IFolderFilterSite.Release],[pFilter]
- .loc_ret:
- xor eax,eax
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------------------
- ; Обработчик метода QueryInterface
- ;----------------------------------------------------------------------------
- proc IFolderFilter_QueryInterface pthis:DWORD, iid:DWORD, ppvObject:DWORD
- pusha
- mov eax,[ppvObject]
- cmp eax,0
- jne @f
- ; E_FAIL
- mov eax,0x80004005
- jmp .loc_ret
- @@:
- ; Это интерфейс IFolderFilter?
- push 4
- pop ecx
- mov esi,[iid]
- mov edi,IID_IFolderFilter
- xor eax,eax
- repe cmpsd
- jz .loc_call
- ; Это интерфейс IUnknown?
- push 4
- pop ecx
- mov esi,[iid]
- mov edi,IID_IUnknown
- xor eax,eax
- repe cmpsd
- jz .loc_call
- ; E_NOINTERFACE
- mov eax,0x80004002
- jmp .loc_ret
- .loc_call:
- mov eax,[pthis]
- ; Установить интерфейс
- mov ecx,[ppvObject]
- mov [ecx],eax
- mov ecx,[eax]
- stdcall dword [ecx+IFolderFilter.AddRef],eax
- .loc_ok:
- ; S_OK
- xor eax,eax
- .loc_ret:
- mov [esp+28],eax
- popa
- ret
- endp
- ;----------------------------------------------------------------------------
- ; Обработчик метода AddRef
- ;----------------------------------------------------------------------------
- proc IFolderFilter_AddRef pthis:DWORD
- mov eax,[pthis]
- inc [eax+IFolderFilter.refcount]
- mov eax,[eax+IFolderFilter.refcount]
- ret
- endp
- ;----------------------------------------------------------------------------
- ; Обработчик метода Release
- ;----------------------------------------------------------------------------
- proc IFolderFilter_Release pthis:DWORD
- push ecx
- mov eax,[pthis]
- mov ecx,[eax+IFolderFilter.refcount]
- or ecx,ecx
- jz @f
- dec [eax+IFolderFilter.refcount]
- dec ecx
- @@:
- mov eax,ecx
- pop ecx
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------------------
- ; Обработчик метода GetEnumFlags
- ;----------------------------------------------------------------------------
- proc IFolderFilter_GetEnumFlags pthis:DWORD, pIShellFolder:DWORD,\
- pidlFolder:DWORD, phwnd:DWORD, pgrfFlags:DWORD
- ; Установить значение флагов
- mov eax,[pgrfFlags]
- mov dword [eax],SHCONTF_FOLDERS or SHCONTF_NONFOLDERS
- ; S_OK
- xor eax,eax
- ret
- endp
Кроме глобальных правил, вы можете устанавливать флаги для каждого отдельного элемента списка, по правилам которых будут обрабатываться его вложенные элементы. В параметре pIShellFolder обработчика приходит указатель на интерфейс IShellFolder текущего элемента. Через методы этого интерфейса можно узнать имя элемента, его свойства, а затем принять какое-то решение. Вряд ли вам когда-либо потребуется настолько детальная обработка, но знать о такой возможности будет полезно.
И вот теперь плавно подходим к самому интересному - реализации метода ShouldShow. Он вызывается системой на каждый элемент списка без исключения и должен вернуть результат S_OK - элемент можно показывать или S_FALSE - элемент показывать нельзя. Все правила проверки вы придумываете сами.
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------------------
- ; Обработчик метода ShouldShow
- ;----------------------------------------------------------------------------
- proc IFolderFilter_ShouldShow pthis:DWORD, pIShellFolder:DWORD,\
- pidlFolder:DWORD, pidlItem:DWORD
- locals
- pidl dd ?
- dAttr dd ?
- StrRet STRRET
- endl
- ; Получить атрибуты проверяемого элемента
- mov [dAttr],SFGAO_FOLDER
- lea eax,[dAttr]
- push eax
- mov eax,[pidlItem]
- mov [pidl],eax
- lea eax,[pidl]
- push eax
- mov eax,[pIShellFolder]
- mov eax,[eax]
- stdcall dword [eax+IShellFolder.GetAttributesOf],[pIShellFolder],\
- 1
- ; Атрибуты получить не удалось, пропустить
- or eax,eax
- jnz .loc_no
- ; Это каталог?
- mov eax,[dAttr]
- and eax,SFGAO_FOLDER
- cmp eax,SFGAO_FOLDER
- je .loc_yes
- ; Очистить структуру STRRET
- lea edi,[StrRet]
- xor eax,eax
- mov ecx,sizeof.STRRET
- rep stosb
- ; Получить имя элемента
- lea eax,[StrRet]
- push eax
- push SHGDN_FORPARSING
- mov eax,[pidlItem]
- push eax
- mov eax,[pIShellFolder]
- mov eax,[eax]
- stdcall dword [eax+IShellFolder.GetDisplayNameOf],[pIShellFolder]
- or eax,eax
- jnz .loc_no
- ; Указатель на имя элемента
- lea eax,[StrRet]
- mov eax,[eax+4]
- ; Файл соответствует разрешенным типам?
- invoke PathMatchSpec,eax,szMatch
- cmp eax,TRUE
- ; Не отображать в списке
- jnz .loc_no
- .loc_yes:
- ; S_OK
- xor eax,eax
- jmp .loc_ret
- .loc_no:
- ; S_FALSE
- xor eax,eax
- inc eax
- .loc_ret:
- ret
- endp
В приложении пример программы с исходным текстом, которая открывает диалог выбора каталога с применением пользовательского фильтра.
Просмотров: 461 | Комментариев: 2
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
zenon
(19.07.2023 в 22:52):
Красота. Спасибо!
morgot
(19.07.2023 в 17:58):
Спасибо, пригодится.
Добавить комментарий
Заполните форму для добавления комментария