Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Функция API SHBrowseForFolder предназначена для выбора какой-нибудь папки из дерева каталогов всех дисков системы. Со своей задачей она, в принципе, справляется неплохо, но имеет ряд недостатков: окно выбора открывается в произвольных местах экрана, нельзя задать начальный каталог, нельзя менять заголовок окна и т.п. Если посмотреть внимательно на структуру BROWSEINFO, которую функция использует в работе, то в ней обнаруживается интересный параметр - указатель на callback-функцию BrowseCallbackProc. Эта функция получает управление при возникновении различных событий в окне выбора папок. Попробуем с ее помощью немного расширить базовый функционал SHBrowseForFolder. Сперва в сегменте данных определим необходимые значения для создания самого диалога выбора.
  1. ; Описание структуры BROWSEINFO
  2. struct  BROWSEINFO
  3.         hwndOwner            dd ?
  4.         pidlRoot             dd ?
  5.         pszDisplayName       dd ?
  6.         lpszTitle            dd ?
  7.         ulFlags              dd ?
  8.         lpfn                 dd ?
  9.         lParam               dd ?
  10.         iImage               dd ?
  11. ends
  12.  
  13. ; Структура для работы с папками
  14. bi                BROWSEINFO
  15.  
  16. ; Буфер, который получит выбранных путь
  17. szDisplayName     rb MAX_PATH
  18.  
  19. ; Текст подсказки в окне выбора папки
  20. szTitle           db 'Please select folder',0
Вызов функции обычный, за исключением того, что теперь в структуре BROWSEINFO заполнен указатель на callback-функцию:
  1.         ...
  2.         ; Заполнить структуру
  3.         mov     [bi.hwndOwner],NULL
  4.         mov     [bi.ulFlags],BIF_RETURNONLYFSDIRS+BIF_DONTGOBELOWDOMAIN
  5.         mov     [bi.pszDisplayName],szDisplayName
  6.         mov     [bi.lpszTitle],szTitle
  7.         ; Указатель на callback-функцию
  8.         mov     [bi.lpfn],BrowseCallbackProc
  9.  
  10.         ; Открыть диалог выбора папки
  11.         invoke  SHBrowseForFolder,bi
  12.         ...
Флаги диалогового окна, а также значения других параметров структуры BROWSEINFO вы можете посмотреть на сайте MSDN.

Теперь перейдем к самому обработчику функции BrowseCallbackProc. В FASM не определены константы, которые в ней используются, сделаем это самостоятельно. Также нам потребуются некоторые дополнительные данные.
  1. ; Неообходимые константы для callback-функции
  2. BFFM_INITIALIZED  = 1
  3. BFFM_SELCHANGED   = 2
  4. BFFM_ENABLEOK     = (WM_USER + 101)
  5. BFFM_SETSELECTION = (WM_USER + 102)
  6. BFFM_SETOKTEXT    = (WM_USER + 105)
  7.  
  8. ; Временный буфер для задания начального пути
  9. tmp_buff          rb MAX_PATH
  10. ; Текст заголовка окна
  11. szMainTitle       db 'FASM - Simple and powerful',0
  12. ; Текст кнопки OK (должен быть в юникоде)
  13. szOkButton        du 'Yes, my Lord',0
Для примера мы будем обрабатывать два сообщения: BFFM_INITIALIZED и BFFM_SELCHANGED, то есть открытие диалогового окна и выбор в нем какой-нибудь папки.
  1. ;---------------------------------------------
  2. ; Обработчик callback-функции
  3. ;---------------------------------------------
  4. proc    BrowseCallbackProc hwnd:DWORD,uMsg:DWORD,lParam:DWORD,lpData
  5.         cmp     [uMsg],BFFM_INITIALIZED
  6.         je      .loc_init
  7.         cmp     [uMsg],BFFM_SELCHANGED
  8.         je      .loc_change
  9. .loc_ret:
  10.         xor     eax,eax
  11.         ret
  12.  
  13. .loc_init:
  14.         ; Установить иконку окна
  15.         invoke  GetModuleHandle,0
  16.         invoke  LoadIcon,eax,1
  17.         invoke  SendMessage,[hwnd],WM_SETICON,ICON_BIG,eax
  18.  
  19.         ; Получить текущий каталог
  20.         invoke  GetCurrentDirectory,MAX_PATH,tmp_buff
  21.         ; Установить начальный каталог в окне
  22.         invoke  SendMessage,[hwnd],BFFM_SETSELECTION,TRUE,tmp_buff
  23.  
  24.         ; Изменить стиль окна
  25.         invoke  GetWindowLong,[hwnd],GWL_EXSTYLE
  26.         add     eax,WS_EX_LAYERED
  27.         invoke  SetWindowLong,[hwnd],GWL_EXSTYLE,eax
  28.         ; Установить атрибут прозрачности
  29.         invoke  SetLayeredWindowAttributes,[hwnd],0,200,LWA_ALPHA
  30.  
  31.         ; Изменить текст в заголовке окна
  32.         invoke  SetWindowText,[hwnd],szMainTitle
  33.  
  34.         ; Изменить текст на кнопке OK
  35.         invoke  SendMessage,[hwnd],BFFM_SETOKTEXT,NULL,szOkButton
  36.  
  37.         ; Заблокировать кнопку OK
  38.         invoke  SendMessage,[hwnd],BFFM_ENABLEOK,NULL,FALSE
  39.  
  40.         ; Субклассировать обработчик окна
  41.         invoke  SetWindowLong,[hwnd],GWL_WNDPROC,NewWindowProc
  42.         ; Сохранить адрес предыдущего обработчика
  43.         invoke  SetWindowLong,[hwnd],GWL_USERDATA,eax
  44.  
  45.         ; Установить окно по центру экрана
  46.         stdcall WindowToCenter,[hwnd],0
  47.         jmp     .loc_ret
  48.  
  49. .loc_change:
  50.         ; Разблокировать кнопку OK
  51.         invoke  SendMessage,[hwnd],BFFM_ENABLEOK,NULL,TRUE
  52.         jmp     .loc_ret
  53. endp
Для установки окна по центру экрана используется описанная ранее функция WindowToCenter. Субклассированный обработчик применяется для перетаскивания окна за любое место.
  1. proc NewWindowProc hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
  2.         ; Нажатие левой клавиши мыши?
  3.         cmp     [uMsg],WM_LBUTTONDOWN
  4.         je      .loc_drag
  5.  
  6.         ; Передать управление предыдущему обработчику
  7.         invoke  GetWindowLong,[hwnd],GWL_USERDATA
  8.         invoke  CallWindowProc,eax,[hwnd],[uMsg],[wParam],[lParam]
  9.         ret
  10.  
  11. .loc_drag:
  12.         ; Перетаскивание окна
  13.         invoke  ReleaseCapture
  14.         invoke  SendMessage,[hwnd],WM_SYSCOMMAND,61458,0
  15.  
  16. .exit_proc:
  17.         ; Подавить сообщение и вернуть из обработчика FALSE
  18.         xor     eax,eax
  19.         ret
  20. endp
Теперь у нас получается окно выбора папки, установленное по центру экрана, полупрозрачное, с иконкой, с установленным по умолчанию текущим каталогом, с собственным текстом на кнопке OK и в заголовке окна, да еще и перетаскиваемое за любое место. А кнопка OK в нем активируется только при выборе какой-нибудь папки. По-моему тюнинг получился неплохой.

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Advanced.SHBrowseForFolder.Demo.zip (5,549 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 9131 | Комментариев: 5

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
user (09.10.2015 в 16:47):
)) Ну вот, снова наткнулся на этот блог.

Добавлю, szDisplayName должна быть статично определена в сегменте данных.
Иначе функция SHBrowseForFolder(..) начинает вести себя непредсказуемо и очень неприятно. Что и стоило пары часов нехороших ощущений.

Ну, например, если определить строку в стеке локально, то первый же вызов SHBrowseForFolder(..) заканчивался записью по невалидному адресу в shell32.dll (а это должна быть ссылка на сегмент данных shell32.dll в нормальном случае).

При этом если перед вызовом этой функции в диалоге генерируется хотя бы один вызов MessageBox'а или одна операция с ListBox'ом, тогда дальше SHBrowseForFolder(..) уже работает нормально даже при локальном определении этой строки. Почему-то.

В общем, такие вот дела..
semiono (02.10.2010 в 22:10):
"окно выбора открывается в произвольных местах экрана,"
Самое противное что есть, это беспорядочные окна! Всегда борюсь за порядок на рабочем столе...
Puk (02.05.2010 в 17:15):
Так и писали бы что рефералы нужны! Я их отключаю в браузере, разрешаю только в экстренных случаях, когда без них сайт неадекватен.
Zummenix (10.02.2010 в 19:53):
Отличный пример :)
в обработчик .loc_change добавил проверку
.loc_change:
        ; Заблокировать кнопку OK
        invoke  SendMessage,[hwnd],BFFM_ENABLEOK,NULL,FALSE

        ; Проверить выбранный путь
        invoke  SHGetPathFromIDList,[lParam],szDisplayName
        cmp     byte [szDisplayName],0
        je      .loc_ret

        ; Если все в порядке, то разблокировать кнопку OK
        invoke  SendMessage,[hwnd],BFFM_ENABLEOK,NULL,TRUE
        jmp     .loc_ret
SAY (09.02.2010 в 08:10):
Здорово!

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

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

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