Тюнинг функции SHBrowseForFolder

Функция 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 в нем активируется только при выборе какой-нибудь папки. По-моему тюнинг получился неплохой.

В приложении пример программы, реализующей все вышеперечисленные новые функции. Для сравнения в нее добавлена функция открытия обычного диалога выбора папки.
Теги:
SHBrowseForFolder
Добавлено: 10 Апреля 2018 20:17:38 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...