Windows 7 Taskbar API на Ассемблере
Progress Bars и Overlay Icons
Продолжаю потихоньку осваивать внутренности Windows 7. В новой системе появилась такая приятная фича интерфейса, как отображение прогресса выполнения какого-нибудь действия прямо на кнопке приложения в панели задач. Впервые я увидел это в программе Total Commander при копировании и переносе файлов, сразу очень захотелось узнать как это делается и научиться делать самому. К сожалению, во всех доступных мне интернетах были найдены только примеры для Delphi, .NET и прочих языков высокого уровня. Пришлось лезть в отладчик и запасаться железным терпением, зато в результате получилось вполне рабочее решение. Для управления элементами панели задач в Windows 7 используется COM-интерфейс ITaskBarList3. Как обычно, никаких описаний в FASM нет, и все необходимое нужно искать самому или портировать с других языков.
Code (Assembler) : Убрать нумерацию
- ; GUID {56FDF344-FD6D-11D0-958A-006097C9A090}
- CLSID_TaskbarList dd 056FDF344h
- dw 0FD6Dh
- dw 011D0h
- db 095h, 08Ah, 000h, 060h, 097h, 0C9h, 0A0h, 090h
- ; GUID {EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF}
- IID_ITaskbarList3 dd 0EA1AFB91h
- dw 09E28h
- dw 04B86h
- db 090h, 0E9h, 09Eh, 09Fh, 08Ah, 05Eh, 0EFh, 0AFh
- ; ITaskbarList3 Interface
- SetProgressValue = 4*09
- SetProgressState = 4*10
- RegisterTab = 4*11
- UnregisterTab = 4*12
- SetTabOrder = 4*13
- SetTabActive = 4*14
- ThumbBarAddButtons = 4*15
- ThumbBarUpdateButtons = 4*16
- ThumbBarSetImageList = 4*17
- SetOverlayIcon = 4*18
- SetThumbnailTooltip = 4*19
- SetThumbnailClip = 4*20
- ; Типы прогрессбаров на панели задач
- TBPF_NOPROGRESS = 0
- TBPF_INDETERMINATE = 1
- TBPF_NORMAL = 2
- TBPF_ERROR = 4
- TBPF_PAUSED = 8
- ; Константы для работы с объектом
- CLSCTX_INPROC_SERVER = 1
- S_OK = 0
Для управления состоянием и установки значения прогресса текущего действия, отображаемого в панели задач, используются два метода интерфейса ITaskbarList3 - SetProgressState и SetProgressValue. При помощи SetProgressState устанавливается состояния прогрессбара: "обычный", "ошибка", "на паузе" или вообще не отображать прогрессбар. SetProgressValue устанавливает уже конкретные параметры прогрессбара: максимальное значение указателя и текущее значение указателя. Например, если в вашем приложении прогрессбар отображает процент выполнения какой-то задачи, то максимальное значение будет 100, а текущее положение указателя - текущий процент выполнения. Для обработки файлов может использоваться другой метод расчета: максимальное значение - размер файла, а текущее - положение указателя в файле относительно начала файла. Интересно, что даже если сперва установить состояние TBPF_NOPROGRESS ("не отображать прогрессбар"), а затем вызвать SetProgressValue с нужными значениями, то прогрессбар появится на панели задач. Можно даже вообще не вызывать SetProgressState перед SetProgressValue, индикатор все равно будет показан. Не знаю, насколько это поведение корректно, так что лучше, наверное, все-таки устанавливать тип прогрессбара перед его отображением.
Еще один интересный метод SetOverlayIcon позволяет наносить дополнительные иконки на панель задач. Ими можно обозначать какие-нибудь события, например, состояние выполняемой задачи. Обратите внимание, что этот метод работает только если в настройках панели задач установлено использование больших иконок. Пример того, как это выглядит в рабочем варианте, можете посмотреть на первом скриншоте. Удобнее всего загружать иконку из ресурсов при помощи функции LoadIcon. На этом с первой частью теории закончили, переходим к практике.
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ...
- pTaskbar dd ? ; Указатель на методы интерфейса
- ...
- ; Сегмент кода
- section '.code' code readable executable
- ...
- ; Инициализировать COM-объект
- invoke CoInitialize, NULL
- ; Создать объект
- invoke CoCreateInstance, CLSID_TaskbarList, NULL,\
- CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
- ; Если не удалось создать объект, то переход
- cmp eax,S_OK
- jnz com_create_fail
- ; Установить тип прогрессбара на панели задач
- mov eax, [pTaskbar]
- mov eax, [eax]
- stdcall dword [eax+SetProgressState], [pTaskbar],[hwnddlg], TBPF_NORMAL
- ; Установить процент заполнения прогрессбара
- ; hwnddlg - хэндл окна, для которого устанавливается прогрессбар
- ; dProc:DWORD - процент заполнения прогрессбар
- ; 100 - максимальное значение указателя
- mov eax, [pTaskbar]
- mov eax, [eax]
- stdcall dword [eax+SetProgressValue], [pTaskbar], [hwnddlg], [dProc],\
- NULL,100,NULL
- ; Загрузить из ресурсов иконку с индексом dIcon
- invoke GetModuleHandle,NULL
- invoke LoadIcon,eax,[dIcon]
- mov ebx,eax
- ; Установить иконку на прогрессбар
- mov eax, [pTaskbar]
- mov eax, [eax]
- stdcall dword [eax+SetOverlayIcon], [pTaskbar], [hwnddlg], ebx, NULL
- ; Освободить хэндл иконки
- invoke DestroyIcon,ebx
- ; Удалить объект
- invoke CoUninitialize
- com_create_fail:
- ...
Изменение текста подсказки миниатюры
Тут ничего сложного, смена текста выполняется при помощи метода SetThumbnailTooltip. Несколько тонкостей: строка подсказки должна быть в юникоде, а метод должен вызываться только после того, как приложение получит системное сообщение TaskbarButtonCreated. У этого сообщения нет постоянного идентификатора, поэтому придется воспользоваться способом, описанным ранее в статье про восстановление иконок в трее:
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ...
- szTBC db 'TaskbarButtonCreated',0 ; Сообщение
- hMsgTBC dd ? ; Идентификатор сообщения
- szTT du 'Hello, World!',0 ; Новый текст подсказки окна
- ; Сегмент кода
- section '.code' code readable executable
- ; Обработчик сообщений окна приложения
- ...
- mov eax,[msg]
- cmp eax,WM_INITDIALOG
- je wminitdialog
- cmp eax,[hMsgTBC]
- je wmTBC
- ...
- wminitdialog:
- ...
- ; Получить идентификатор сообщения TaskbarButtonCreated при
- ; инициализации окна
- invoke RegisterWindowMessage,szTBC
- mov [hMsgTBC],eax
- ...
- wmTBC:
- ; Сообщение, что кнопка и миниатюра в панели задач создана.
- ; Тут можно взвести флаг, что приложение может менять подсказку,
- ; или сразу поменять ее
- ...
- ; Инициализировать COM-объект
- invoke CoInitialize,NULL
- ; Создать объект
- invoke CoCreateInstance, CLSID_TaskbarList, NULL,\
- CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
- ; Если не удалось создать объект, то переход
- cmp eax,S_OK
- jne epic_fail
- ; Установить новый текст подсказки
- mov eax, [pTaskbar]
- mov eax, [eax]
- stdcall dword [eax+SetThumbnailTooltip],[pTaskbar],[hwnddlg],szTitle
- ; Удалить объект
- invoke CoUninitialize
- epic_fail:
- ...
Кнопки управления на миниатюре
Добавление кнопок на миниатюру выполняется при помощи метода ThumbBarAddButtons. В нем используется структура THUMBBUTTON и флаги, которые в FASM, естественно, не описаны. Поэтому перед использованием метода надо подготовить все необходимое:
Code (Assembler) : Убрать нумерацию
- ; Флаги описания кнопки
- THB_BITMAP = 1
- THB_ICON = 2
- THB_TOOLTIP = 4
- THB_FLAGS = 8
- ; Флаги состояни кнопки
- THBF_ENABLED = 0
- THBF_DISABLED = 1
- THBF_DISMISSONCLICK = 2
- THBF_NOBACKGROUND = 4
- THBF_HIDDEN = 8
- THBF_NONINTERACTIVE = 16
- ; Структура описания кнопки
- struct THUMBBUTTON
- dwMask dd ? ; Маска состояния кнопки
- iId dd ? ; Идентификатор кнопки
- iBitmap dd ? ; Порядковый номер изображения
- hIcon dd ? ; Хэндл иконки кнопки
- szTip rb 260*2 ; Строка подсказки в юникоде
- dwFlags dd ? ; Флаги кнопки
- ends
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ; Идентификаторы кнопок для обработки сообщений от них
- IDTB_BUTTON1 = 101
- IDTB_BUTTON2 = 102
- IDTB_BUTTON3 = 103
- IDTB_BUTTON4 = 104
- ; Подсказки на кнопках
- szTT1 du 'Rewind',0
- tt_len1 = $-szTT1
- szTT2 du 'Stop',0
- tt_len2 = $-szTT1
- szTT3 du 'Play',0
- tt_len3 = $-szTT1
- szTT4 du 'Forward',0
- tt_len4 = $-szTT1
- buttons: ; Массив кнопок
- butt1 THUMBBUTTON
- butt2 THUMBBUTTON
- butt3 THUMBBUTTON
- butt4 THUMBBUTTON
- ...
- ; Сегмент кода
- section '.code' code readable executable
- ...
- ; Инициализировать COM-объект
- invoke CoInitialize,NULL
- ; Создать объект
- invoke CoCreateInstance, CLSID_TaskbarList, NULL,\
- CLSCTX_INPROC_SERVER, IID_ITaskbarList3, pTaskbar
- cmp eax,S_OK
- jne epic_fail
- ; Хэндл модуля для загрузки иконок
- invoke GetModuleHandle,NULL
- mov ebx,eax
- ; Заполнить массив для первой кнопки
- ; Флаги кнопки: использовать иконку, подсказку, учитывать флаги
- mov [butt1.dwMask],THB_ICON+THB_TOOLTIP+THB_FLAGS
- ; Идентификатор кнопки
- mov [butt1.iId],IDTB_BUTTON1
- ; Порядковый номер кнопки
- mov [butt1.iBitmap],0
- ; Загрузить из ресурсов иконку
- invoke LoadIcon,ebx,2
- mov [butt1.hIcon],eax
- ; Заполнить подсказку
- mov esi,szTT1
- mov edi,butt1.szTip
- mov ecx,tt_len1
- rep movsb
- ; Флаги кнопки: активна, после нажатия миниатюра сворачивается
- mov [butt1.dwFlags],THBF_ENABLED+THBF_DISMISSONCLICK
- ; Заполнить массив для второй кнопки
- ; Флаги кнопки: использовать иконку, подсказку, учитывать флаги
- mov [butt2.dwMask],THB_ICON+THB_TOOLTIP+THB_FLAGS
- ; Идентификатор кнопки
- mov [butt2.iId],IDTB_BUTTON2
- ; Порядковый номер кнопки
- mov [butt2.iBitmap],1
- ; Загрузить из ресурсов иконку
- invoke LoadIcon,ebx,3
- mov [butt2.hIcon],eax
- ; Заполнить подсказку
- mov esi,szTT2
- mov edi,butt2.szTip
- mov ecx,tt_len2
- rep movsb
- ; Флаги кнопки: активна, без фоновой заливки
- mov [butt2.dwFlags],THBF_ENABLED+THBF_NOBACKGROUND
- ...
- ; Добавить панель кнопок к миниатюре окна
- mov eax, [pTaskbar]
- mov eax, [eax]
- ; 4 - количество кнопок в массиве
- ; buttons - указатель на массив кнопок
- stdcall dword [eax+ThumbBarAddButtons],[pTaskbar],\
- [hwnddlg],4,buttons
- ; Удалить объект
- invoke CoUninitialize
- ...
Code (Assembler) : Убрать нумерацию
- ; Обработчик сообщений окна основного приложения
- ...
- mov eax,[msg]
- cmp eax,WM_COMMAND
- je wmcommand
- ...
- wmcommand:
- ...
- ; Выделить из wparam идентификатор нажатой кнопки
- mov eax,[wparam]
- and eax,0FFFFh
- ; Нажата кнопка 1?
- cmp eax, IDTB_BUTTON1
- je wm_button1
- ; Нажата кнопка 2?
- cmp eax, IDTB_BUTTON2
- je wm_button2
- ...
Просмотров: 8092 | Комментариев: 6
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(26.01.2023 в 12:16):
Jump List: https://www.manhunter.ru/assem...zheniya.html
Чучмек
(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 !
Добавить комментарий
Заполните форму для добавления комментария