Функция API
SHBrowseForFolder предназначена для выбора какой-нибудь папки из дерева каталогов всех дисков системы. Со своей задачей она, в принципе, справляется неплохо, но имеет ряд недостатков: окно выбора открывается в произвольных местах экрана, нельзя задать начальный каталог, нельзя менять заголовок окна и т.п. Если посмотреть внимательно на структуру BROWSEINFO, которую функция использует в работе, то в ней обнаруживается интересный параметр - указатель на callback-функцию
BrowseCallbackProc. Эта функция получает управление при возникновении различных событий в окне выбора папок. Попробуем с ее помощью немного расширить базовый функционал SHBrowseForFolder. Сперва в сегменте данных определим необходимые значения для создания самого диалога выбора.
; Описание структуры BROWSEINFO
struct BROWSEINFO
hwndOwner dd ?
pidlRoot dd ?
pszDisplayName dd ?
lpszTitle dd ?
ulFlags dd ?
lpfn dd ?
lParam dd ?
iImage dd ?
ends
; Структура для работы с папками
bi BROWSEINFO
; Буфер, который получит выбранных путь
szDisplayName rb MAX_PATH
; Текст подсказки в окне выбора папки
szTitle db 'Please select folder',0
Вызов функции обычный, за исключением того, что теперь в структуре BROWSEINFO заполнен указатель на callback-функцию:
...
; Заполнить структуру
mov [bi.hwndOwner],NULL
mov [bi.ulFlags],BIF_RETURNONLYFSDIRS+BIF_DONTGOBELOWDOMAIN
mov [bi.pszDisplayName],szDisplayName
mov [bi.lpszTitle],szTitle
; Указатель на callback-функцию
mov [bi.lpfn],BrowseCallbackProc
; Открыть диалог выбора папки
invoke SHBrowseForFolder,bi
...
Флаги диалогового окна, а также значения другие параметров структуры BROWSEINFO вы можете посмотреть на сайте MSDN.
Теперь перейдем к самому обработчику функции BrowseCallbackProc. В FASM не определены константы, которые в ней используются, сделаем это самостоятельно. Также нам потребуются некоторые дополнительные данные.
; Неообходимые константы для callback-функции
BFFM_INITIALIZED = 1
BFFM_SELCHANGED = 2
BFFM_ENABLEOK = (WM_USER + 101)
BFFM_SETSELECTION = (WM_USER + 102)
BFFM_SETOKTEXT = (WM_USER + 105)
; Временный буфер для задания начального пути
tmp_buff rb MAX_PATH
; Текст заголовка окна
szMainTitle db 'FASM - Simple and powerful',0
; Текст кнопки OK (должен быть в юникоде)
szOkButton du 'Yes, my Lord',0
Для примера мы будем обрабатывать два сообщения: BFFM_INITIALIZED и BFFM_SELCHANGED, то есть открытие диалогового окна и выбор в нем какой-нибудь папки.
;---------------------------------------------
; Обработчик callback-функции
;---------------------------------------------
proc BrowseCallbackProc hwnd:DWORD,uMsg:DWORD,lParam:DWORD,lpData
cmp [uMsg],BFFM_INITIALIZED
je .loc_init
cmp [uMsg],BFFM_SELCHANGED
je .loc_change
.loc_ret:
xor eax,eax
ret
.loc_init:
; Установить иконку окна
invoke GetModuleHandle,0
invoke LoadIcon,eax,1
invoke SendMessage,[hwnd],WM_SETICON,ICON_BIG,eax
; Получить текущий каталог
invoke GetCurrentDirectory,MAX_PATH,tmp_buff
; Установить начальный каталог в окне
invoke SendMessage,[hwnd],BFFM_SETSELECTION,TRUE,tmp_buff
; Изменить стиль окна
invoke GetWindowLong,[hwnd],GWL_EXSTYLE
add eax,WS_EX_LAYERED
invoke SetWindowLong,[hwnd],GWL_EXSTYLE,eax
; Установить атрибут прозрачности
invoke SetLayeredWindowAttributes,[hwnd],0,200,LWA_ALPHA
; Изменить текст в заголовке окна
invoke SetWindowText,[hwnd],szMainTitle
; Изменить текст на кнопке OK
invoke SendMessage,[hwnd],BFFM_SETOKTEXT,NULL,szOkButton
; Заблокировать кнопку OK
invoke SendMessage,[hwnd],BFFM_ENABLEOK,NULL,FALSE
; Субклассировать обработчик окна
invoke SetWindowLong,[hwnd],GWL_WNDPROC,NewWindowProc
; Сохранить адрес предыдущего обработчика
invoke SetWindowLong,[hwnd],GWL_USERDATA,eax
; Установить окно по центру экрана
stdcall WindowToCenter,[hwnd],0
jmp .loc_ret
.loc_change:
; Разблокировать кнопку OK
invoke SendMessage,[hwnd],BFFM_ENABLEOK,NULL,TRUE
jmp .loc_ret
endp
Для установки окна по центру экрана используется описанная ранее функция
WindowToCenter. Субклассированный обработчик применяется для перетаскивания окна за любое место.
proc NewWindowProc hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
; Нажатие левой клавиши мыши?
cmp [uMsg],WM_LBUTTONDOWN
je .loc_drag
; Передать управление предыдущему обработчику
invoke GetWindowLong,[hwnd],GWL_USERDATA
invoke CallWindowProc,eax,[hwnd],[uMsg],[wParam],[lParam]
ret
.loc_drag:
; Перетаскивание окна
invoke ReleaseCapture
invoke SendMessage,[hwnd],WM_SYSCOMMAND,61458,0
.exit_proc:
; Подавить сообщение и вернуть из обработчика FALSE
xor eax,eax
ret
endp
Теперь у нас получается окно выбора папки, установленное по центру экрана, полупрозрачное, с иконкой, с установленным по умолчанию текущим каталогом, с собственным текстом на кнопке OK и в заголовке окна, да еще и перетаскиваемое за любое место. А кнопка OK в нем активируется только при выборе какой-нибудь папки. По-моему тюнинг получился неплохой.
В приложении пример программы, реализующей все вышеперечисленные новые функции. Для сравнения в нее добавлена функция открытия обычного диалога выбора папки.