Blog. Just Blog

Windows 7 Taskbar API на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Progress Bars и Overlay Icons
Progress Bars и Overlay Icons

Продолжаю потихоньку осваивать внутренности Windows 7. В новой системе появилась такая приятная фича интерфейса, как отображение прогресса выполнения какого-нибудь действия прямо на кнопке приложения в панели задач. Впервые я увидел это в программе Total Commander при копировании и переносе файлов, сразу очень захотелось узнать как это делается и научиться делать самому. К сожалению, во всех доступных мне интернетах были найдены только примеры для Delphi, .NET и прочих языков высокого уровня. Пришлось лезть в отладчик и запасаться железным терпением, зато в результате получилось вполне рабочее решение. Для управления элементами панели задач в Windows 7 используется COM-интерфейс ITaskBarList3. Как обычно, никаких описаний в FASM нет, и все необходимое нужно искать самому или портировать с других языков.
  1. ; GUID {56FDF344-FD6D-11D0-958A-006097C9A090}
  2. CLSID_TaskbarList       dd 056FDF344h
  3.                         dw 0FD6Dh
  4.                         dw 011D0h
  5.                         db 095h, 08Ah, 000h, 060h, 097h, 0C9h, 0A0h, 090h
  6.  
  7. ; GUID {EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF}
  8. IID_ITaskbarList3       dd 0EA1AFB91h
  9.                         dw 09E28h
  10.                         dw 04B86h
  11.                         db 090h, 0E9h, 09Eh, 09Fh, 08Ah, 05Eh, 0EFh, 0AFh
  12.  
  13. ; ITaskbarList3 Interface
  14. SetProgressValue        = 4*09
  15. SetProgressState        = 4*10
  16. RegisterTab             = 4*11
  17. UnregisterTab           = 4*12
  18. SetTabOrder             = 4*13
  19. SetTabActive            = 4*14
  20. ThumbBarAddButtons      = 4*15
  21. ThumbBarUpdateButtons   = 4*16
  22. ThumbBarSetImageList    = 4*17
  23. SetOverlayIcon          = 4*18
  24. SetThumbnailTooltip     = 4*19
  25. SetThumbnailClip        = 4*20
  26.  
  27. ; Типы прогрессбаров на панели задач
  28. TBPF_NOPROGRESS         = 0
  29. TBPF_INDETERMINATE      = 1
  30. TBPF_NORMAL             = 2
  31. TBPF_ERROR              = 4
  32. TBPF_PAUSED             = 8
  33.  
  34. ; Константы для работы с объектом
  35. CLSCTX_INPROC_SERVER    = 1
  36. S_OK                    = 0
Хотя сейчас будут использоваться только три функции, я привел полное описание методов COM-интерфейса ITaskbarList3, оно пригодится чуть позже.

Для управления состоянием и установки значения прогресса текущего действия, отображаемого в панели задач, используются два метода интерфейса ITaskbarList3 - SetProgressState и SetProgressValue. При помощи SetProgressState устанавливается состояния прогрессбара: "обычный", "ошибка", "на паузе" или вообще не отображать прогрессбар. SetProgressValue устанавливает уже конкретные параметры прогрессбара: максимальное значение указателя и текущее значение указателя. Например, если в вашем приложении прогрессбар отображает процент выполнения какой-то задачи, то максимальное значение будет 100, а текущее положение указателя - текущий процент выполнения. Для обработки файлов может использоваться другой метод расчета: максимальное значение - размер файла, а текущее - положение указателя в файле относительно начала файла. Интересно, что даже если сперва установить состояние TBPF_NOPROGRESS ("не отображать прогрессбар"), а затем вызвать SetProgressValue с нужными значениями, то прогрессбар появится на панели задач. Можно даже вообще не вызывать SetProgressState перед SetProgressValue, индикатор все равно будет показан. Не знаю, насколько это поведение корректно, так что лучше, наверное, все-таки устанавливать тип прогрессбара перед его отображением.

Еще один интересный метод SetOverlayIcon позволяет наносить дополнительные иконки на панель задач. Ими можно обозначать какие-нибудь события, например, состояние выполняемой задачи. Обратите внимание, что этот метод работает только если в настройках панели задач установлено использование больших иконок. Пример того, как это выглядит в рабочем варианте, можете посмотреть на первом скриншоте. Удобнее всего загружать иконку из ресурсов при помощи функции LoadIcon. На этом с первой частью теории закончили, переходим к практике.
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.         ...
  4. pTaskbar dd ?    ; Указатель на методы интерфейса
  5.         ...
  6.  
  7. ; Сегмент кода
  8. section '.code' code readable executable
  9.         ...
  10.         ; Инициализировать COM-объект
  11.         invoke  CoInitialize, NULL
  12.         ; Создать объект
  13.         invoke  CoCreateInstance, CLSID_TaskbarList, NULL,\
  14.                 CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
  15.  
  16.         ; Если не удалось создать объект, то переход
  17.         cmp     eax,S_OK
  18.         jnz     com_create_fail
  19.  
  20.         ; Установить тип прогрессбара на панели задач
  21.         mov     eax, [pTaskbar]
  22.         mov     eax, [eax]
  23.         stdcall dword [eax+SetProgressState], [pTaskbar],[hwnddlg], TBPF_NORMAL
  24.  
  25.         ; Установить процент заполнения прогрессбара
  26.         ; hwnddlg - хэндл окна, для которого устанавливается прогрессбар
  27.         ; dProc:DWORD - процент заполнения прогрессбар
  28.         ; 100 - максимальное значение указателя
  29.         mov     eax, [pTaskbar]
  30.         mov     eax, [eax]
  31.         stdcall dword [eax+SetProgressValue], [pTaskbar], [hwnddlg], [dProc],\
  32.                 NULL,100,NULL
  33.  
  34.         ; Загрузить из ресурсов иконку с индексом dIcon
  35.         invoke  GetModuleHandle,NULL
  36.         invoke  LoadIcon,eax,[dIcon]
  37.         mov     ebx,eax
  38.  
  39.         ; Установить иконку на прогрессбар
  40.         mov     eax, [pTaskbar]
  41.         mov     eax, [eax]
  42.         stdcall dword [eax+SetOverlayIcon], [pTaskbar], [hwnddlg], ebx, NULL
  43.  
  44.         ; Освободить хэндл иконки
  45.         invoke  DestroyIcon,ebx
  46.  
  47.         ; Удалить объект
  48.         invoke  CoUninitialize
  49.  
  50. com_create_fail:
  51.         ...
От панели задач перейдем к панели миниатюр. Это небольшие превьюшки окон запущенных программ. В Windows XP такие миниатюры позволяли делать сторонние программы, например, VisualTaskTips или оболочка Aston. В Windows 7 миниатюры стали частью интерфейса, и более того, появилась возможность самостоятельного управления их свойствами. Начнем с простого примера: замена текста всплывающей подсказки над миниатюрой.

Изменение текста подсказки миниатюры
Изменение текста подсказки миниатюры

Тут ничего сложного, смена текста выполняется при помощи метода SetThumbnailTooltip. Несколько тонкостей: строка подсказки должна быть в юникоде, а метод должен вызываться только после того, как приложение получит системное сообщение TaskbarButtonCreated. У этого сообщения нет постоянного идентификатора, поэтому придется воспользоваться способом, описанным ранее в статье про восстановление иконок в трее:
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.         ...
  4. szTBC   db      'TaskbarButtonCreated',0 ; Сообщение
  5. hMsgTBC dd      ?                        ; Идентификатор сообщения
  6. szTT    du      'Hello, World!',0        ; Новый текст подсказки окна
  7.  
  8. ; Сегмент кода
  9. section '.code' code readable executable
  10.  
  11.         ; Обработчик сообщений окна приложения
  12.         ...
  13.         mov     eax,[msg]
  14.         cmp     eax,WM_INITDIALOG
  15.         je      wminitdialog
  16.         cmp     eax,[hMsgTBC]
  17.         je      wmTBC
  18.         ...
  19.  
  20. wminitdialog:
  21.         ...
  22.         ; Получить идентификатор сообщения TaskbarButtonCreated при
  23.         ; инициализации окна
  24.         invoke  RegisterWindowMessage,szTBC
  25.         mov     [hMsgTBC],eax
  26.         ...
  27. wmTBC:
  28.         ; Сообщение, что кнопка и миниатюра в панели задач создана.
  29.         ; Тут можно взвести флаг, что приложение может менять подсказку,
  30.         ; или сразу поменять ее
  31.         ...
  32.         ; Инициализировать COM-объект
  33.         invoke  CoInitialize,NULL
  34.         ; Создать объект
  35.         invoke  CoCreateInstance, CLSID_TaskbarList, NULL,\
  36.                 CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
  37.  
  38.         ; Если не удалось создать объект, то переход
  39.         cmp     eax,S_OK
  40.         jne     epic_fail
  41.  
  42.         ; Установить новый текст подсказки
  43.         mov     eax, [pTaskbar]
  44.         mov     eax, [eax]
  45.         stdcall dword [eax+SetThumbnailTooltip],[pTaskbar],[hwnddlg],szTitle
  46.         ; Удалить объект
  47.         invoke  CoUninitialize
  48. epic_fail:
  49.         ...
Более интересный и сложный пример. На миниатюру можно добавлять собственные элементы управления. Это позволяет использовать функции приложения даже в тех случаях, когда оно свернуто. В частности, с помощью таких кнопок можно получить доступ к приложению, не делая его активным. Хороший пример - Windows Media Player. Если подвести указатель мыши к иконке Media Player, когда приложение запущено, вы увидите три кнопки на дисплее: Play/Pause, Next и Previous. Нажатие на каждую кнопку выполнит соответствующую команду. Попробуем сделать что-нибудь подобное на Ассемблере.

Кнопки управления на миниатюре
Кнопки управления на миниатюре

Добавление кнопок на миниатюру выполняется при помощи метода ThumbBarAddButtons. В нем используется структура THUMBBUTTON и флаги, которые в FASM, естественно, не описаны. Поэтому перед использованием метода надо подготовить все необходимое:
  1. ; Флаги описания кнопки
  2. THB_BITMAP    = 1
  3. THB_ICON      = 2
  4. THB_TOOLTIP   = 4
  5. THB_FLAGS     = 8
  6.  
  7. ; Флаги состояни кнопки
  8. THBF_ENABLED          = 0
  9. THBF_DISABLED         = 1
  10. THBF_DISMISSONCLICK   = 2
  11. THBF_NOBACKGROUND     = 4
  12. THBF_HIDDEN           = 8
  13. THBF_NONINTERACTIVE   = 16
  14.  
  15. ; Структура описания кнопки
  16. struct THUMBBUTTON
  17.         dwMask  dd ?       ; Маска состояния кнопки
  18.         iId     dd ?       ; Идентификатор кнопки
  19.         iBitmap dd ?       ; Порядковый номер изображения
  20.         hIcon   dd ?       ; Хэндл иконки кнопки
  21.         szTip   rb 260*2   ; Строка подсказки в юникоде
  22.         dwFlags dd ?       ; Флаги кнопки
  23. ends
Значения флагов подробно описаны в MSDN. Как и в предыдущем примере, все действия с миниатюрой можно выполнять только после того, как ваше приложение получит системное сообщение TaskbarButtonCreated. Переходим к заполнению структур в массиве кнопок. Здесь также есть свои особенности: текст подсказок обязательно должен быть в юникоде, а всего на миниатюру можно добавить не более 7 элементов управления.
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. ; Идентификаторы кнопок для обработки сообщений от них
  5. IDTB_BUTTON1 = 101
  6. IDTB_BUTTON2 = 102
  7. IDTB_BUTTON3 = 103
  8. IDTB_BUTTON4 = 104
  9.  
  10. ; Подсказки на кнопках
  11. szTT1   du 'Rewind',0
  12. tt_len1 = $-szTT1
  13. szTT2   du 'Stop',0
  14. tt_len2 = $-szTT1
  15. szTT3   du 'Play',0
  16. tt_len3 = $-szTT1
  17. szTT4   du 'Forward',0
  18. tt_len4 = $-szTT1
  19.  
  20. buttons:                ; Массив кнопок
  21. butt1 THUMBBUTTON
  22. butt2 THUMBBUTTON
  23. butt3 THUMBBUTTON
  24. butt4 THUMBBUTTON
  25. ...
  26.  
  27. ; Сегмент кода
  28. section '.code' code readable executable
  29.         ...
  30.         ; Инициализировать COM-объект
  31.         invoke  CoInitialize,NULL
  32.         ; Создать объект
  33.         invoke  CoCreateInstance, CLSID_TaskbarList, NULL,\
  34.                 CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
  35.         cmp     eax,S_OK
  36.         jne     epic_fail
  37.  
  38.         ; Хэндл модуля для загрузки иконок
  39.         invoke  GetModuleHandle,NULL
  40.         mov     ebx,eax
  41.  
  42.         ; Заполнить массив для первой кнопки
  43.         ; Флаги кнопки: использовать иконку, подсказку, учитывать флаги
  44.         mov     [butt1.dwMask],THB_ICON+THB_TOOLTIP+THB_FLAGS
  45.         ; Идентификатор кнопки
  46.         mov     [butt1.iId],IDTB_BUTTON1
  47.         ; Порядковый номер кнопки
  48.         mov     [butt1.iBitmap],0
  49.         ; Загрузить из ресурсов иконку
  50.         invoke  LoadIcon,ebx,2
  51.         mov     [butt1.hIcon],eax
  52.         ; Заполнить подсказку
  53.         mov     esi,szTT1
  54.         mov     edi,butt1.szTip
  55.         mov     ecx,tt_len1
  56.         rep     movsb
  57.         ; Флаги кнопки: активна, после нажатия миниатюра сворачивается
  58.         mov     [butt1.dwFlags],THBF_ENABLED+THBF_DISMISSONCLICK
  59.  
  60.         ; Заполнить массив для второй кнопки
  61.         ; Флаги кнопки: использовать иконку, подсказку, учитывать флаги
  62.         mov     [butt2.dwMask],THB_ICON+THB_TOOLTIP+THB_FLAGS
  63.         ; Идентификатор кнопки
  64.         mov     [butt2.iId],IDTB_BUTTON2
  65.         ; Порядковый номер кнопки
  66.         mov     [butt2.iBitmap],1
  67.         ; Загрузить из ресурсов иконку
  68.         invoke  LoadIcon,ebx,3
  69.         mov     [butt2.hIcon],eax
  70.         ; Заполнить подсказку
  71.         mov     esi,szTT2
  72.         mov     edi,butt2.szTip
  73.         mov     ecx,tt_len2
  74.         rep     movsb
  75.         ; Флаги кнопки: активна, без фоновой заливки 
  76.         mov     [butt2.dwFlags],THBF_ENABLED+THBF_NOBACKGROUND
  77.  
  78.         ...
  79.  
  80.         ; Добавить панель кнопок к миниатюре окна
  81.         mov     eax, [pTaskbar]
  82.         mov     eax, [eax]
  83.         ; 4 - количество кнопок в массиве
  84.         ; buttons - указатель на массив кнопок
  85.         stdcall dword [eax+ThumbBarAddButtons],[pTaskbar],\
  86.                 [hwnddlg],4,buttons
  87.  
  88.         ; Удалить объект
  89.         invoke  CoUninitialize
  90.         ...
Кнопки на миниатюре центрируются автоматически, а их состояние, тексты подсказок и картинки меняются при помощи метода ThumbBarUpdateButtons. Его параметры точно такие же, как и у метода ThumbBarAddButtons. Но мало нарисовать кнопки, надо, чтобы они взаимодействовали с основным приложением. Для этого и используется параметр iId в структуре THUMBBUTTON. При нажатии кнопки на миниатюре родительскому окну приложения посылается сообщение WM_COMMAND, а идентификатор кнопки передается в младшем слове wParam:
  1. ; Обработчик сообщений окна основного приложения
  2.         ...
  3.         mov     eax,[msg]
  4.         cmp     eax,WM_COMMAND
  5.         je      wmcommand
  6.         ...
  7. wmcommand:
  8.         ...
  9.         ; Выделить из wparam идентификатор нажатой кнопки
  10.         mov     eax,[wparam]
  11.         and     eax,0FFFFh
  12.  
  13.         ; Нажата кнопка 1?
  14.         cmp     eax, IDTB_BUTTON1
  15.         je      wm_button1
  16.  
  17.         ; Нажата кнопка 2?
  18.         cmp     eax, IDTB_BUTTON2
  19.         je      wm_button2
  20.         ...
Как видите, новые элементы интерфейса Windows 7 открывают перед разработчиками очень широкие возможности. В приложении примеры программ с исходниками, реализующие все три описанных метода. Естественно, они будут работать только под Windows 7.

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

Windows.7.Taskbar.API.Demo.zip (26,006 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
ManHunter (26.01.2023 в 12:16):
Чучмек (17.10.2012 в 10:26):
Пишу на Delphi. Нашел пример на ASM. Ассемблер мне родней англицкого. Спасибо.
ManHunter (09.02.2011 в 13:18):
Что мешает динамически переключать их через SetProgressState?
Dima (09.02.2011 в 13:13):
А как сделать в одном таскбаре 3 цвета - перехода ? Например при установки программы сначала красный потом желтый и в конце зеленый
Gar|k (19.12.2010 в 00:17):
Захотелось вдруг реализовать эту возможность прогресс бара :) Порылся в интернетах и тоже столкнулся с переводами статей про .NET, ну подумал всё буду реверсить! Потом нагуглил про ITaskbarList3 и код в форуме RSDN, как заюзать ITaskbarList, обрадовался уже начал искать константы ITaskbarList3 и наткнулся на эту статью! Очень радует, что не один я такой ;) Жаль правда, что не первый, но на Си вроде пока еще не писали такого (может быть)
пРОХОЖИЙ (08.10.2010 в 19:19):
Спасибо за любовь к ASM !

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

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

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