На примере вопроса "Как скрыть окно из Панели задач" я покажу, как вы можете использовать свои голову и руки, чтобы успешно (и самостоятельно) решить эту проблему. Да это бредовый пост, но меня уже просто взбесило, что никто и не чешется что-то делать, когда им тыкаешь в подробное описание.
Когда начинающий задаёт такой вопрос на форуме, он обычно получает три вида ответов:
- (60%)RTFM (aka "читать факи").
- (39%) Код. Причём в 90% случаев код взят по принципу "бери этот код, он у меня работает, я гарантирую это!".
- (1%) Ссылку на MSDN или аналогичное описание.
Шаг 1 - найти официальную документацию
Для этого мы используем поисковик (я предпочитаю Google, чего и вам советую). Формулируем ключевые слова для своей проблемы. К примеру, в нашем случае это будет "taskbar buttons" (не знаете английский?) или что-то такое. Далее, вы делаете поиск документации. В некоторых случаях вы можете сузить область поиска, указав сайт, на котором лежит документация. Если вы ищете что-то о Delphi, то ограничивайте поиск сайтом docwiki.embarcadero.com, если по продуктам Microsoft - то msdn.microsoft.com, если ещё что-то - то используйте соответствующие поправки.Итак, поиск.
Все ссылки вида social.msdn.microsoft.com - это типа форумов. Отметаем сразу.
Итак, что мы получили? Ссылка 1 - это guideline-ы для Панели задач. Тоже весьма полезное чтиво, но сейчас интересует не это. Пропускаем.
Ссылка 2 - рассказ о Панели задач в рамках раздела "Расширение Панели задач". Во, похоже на то, что нам надо. Пробегая раздел глазами, встречаем подразделы "Managing Taskbar Buttons" и "Modifying the Contents of the Taskbar". Оно? Оно.
Шаг 2 - чтение и анализ документации
Для удобства, я приведу цитату из документации:Managing Taskbar ButtonsОткуда напрямую следуют:
The Shell creates a button on the taskbar whenever an application creates a window that isn't owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.
The Shell will remove a window's button from the taskbar only if the window's style supports visible taskbar buttons. If you want to dynamically change a window's style to one that doesn't support visible taskbar buttons, you must hide the window first (by calling ShowWindow with SW_HIDE), change the window style, and then show the window.
The window button typically contains the application icon and title. However, if the application does not contain a system menu, the window button is created without the icon.
If you want your application to get the user's attention when the window is not active, use the FlashWindow function to let the user know that a message is waiting. This function flashes the window button. Once the user clicks the window button to activate the window, your application can display the message.
Modifying the Contents of the Taskbar
Version 4.71 and later of Shell32.dll adds the capability to modify the contents of the taskbar. From an application, you can now add, remove, and activate taskbar buttons. Activating the item does not activate the window; it shows the item as pressed on the taskbar.
The taskbar modification capabilities are implemented in a Component Object Model (COM) object (CLSID_TaskbarList) that exposes the ITaskbarList interface (IID_ITaskbarList). You must call the ITaskbarList::HrInit method to initialize the object. You can then use the methods of the ITaskbarList interface to modify the contents of the taskbar.
- Правила, согласно которым окна появляются на Панели задач.
- Официальный интерфейс по управлению кнопками в Панели задач.
- Нарушить условия, при которых окно показывается на Панели задач.
- Вручную указать Панели задач скрывать наше окно.
Шаг 3 - написание или поиск заголовочников
Окей, если вы взяли первый подход, то все заголовочники у нас уже есть - это модуль Windows. Если вы взяли второй подход, то... то вам нужно их ещё найти. В частности, нам нужно определение интерфейса ITaskbarList.Где искать заголовочники:
- В Delphi. Да, иногда бывает. Запускайте поиск "ITaskbarList" в "*.pas" файлах в папке Delphi.
- В JWAPI. Аналогично, но поиск по папке JWAPI.
- В интернете. Надо понимать, что качество этих заголовочников может быть... не очень.
- В Platform SDK. Окей, владельцам новых Delphi повезло - в их состав входят более-менее свежие заголовочники. В этом случае делаем поиск "ITaskbarList" в "*.h" файлах в папке Delphi. Всем остальным? Качать и ставить Platform SDK или MSDN.
Шаг 3а - конвертация заголовочников
В файле ShObjIdl.h вы найдёте такие строки:/* interface ITaskbarList */
/* [object][uuid] */
EXTERN_C const IID IID_ITaskbarList;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("56FDF342-FD6D-11d0-958A-006097C9A090")
ITaskbarList : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE HrInit( void) = 0;
virtual HRESULT STDMETHODCALLTYPE AddTab(
/* [in] */ __RPC__in HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE DeleteTab(
/* [in] */ __RPC__in HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE ActivateTab(
/* [in] */ __RPC__in HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE SetActiveAlt(
/* [in] */ __RPC__in HWND hwnd) = 0;
};
#else /* C style interface */
...
#ifdef __cplusplus
class DECLSPEC_UUID("56FDF344-FD6D-11d0-958A-006097C9A090")
TaskbarList;
#endifПереводя это на Delphi, получаем (не знаете C?):const
CLSID_TaskbarList: TGUID = '{56FDF344-FD6D-11d0-958A-006097C9A090}';
type
ITaskbarList = interface
['{56FDF342-FD6D-11d0-958A-006097C9A090}']
procedure HrInit; safecall;
procedure AddTab(hwnd: Cardinal); safecall;
procedure DeleteTab(hwnd: Cardinal); safecall;
procedure ActivateTab(hwnd: Cardinal); safecall;
procedure SetActiveAlt(hwnd: Cardinal); safecall;
end;Обратите внимание на safecall. Я уже много раз про него рассказывал.Шаг 4 - вызов кода
Итак, если вы решили пойти способом модификации условий, то вы можете:- Сбросить WS_EX_APPWINDOW
- Установить WS_EX_TOOLWINDOW
- Установить окну владельца в терминах системы (можно невидимого)
Если вы пошли способом ручного управления, то вы создаёте ITaskbarList, инициализируете его (вызовом HrInit) и вызываете метод DeleteTab для своего окна. Достаточно прозрачно.
Шаг 5 - адаптация к Delphi
Ну и нужно учесть специфику Delphi.Пункт 1 - это запутанное управление окнами в старых Delphi, где кнопка окна на Панели задач на самом деле не была кнопкой от окна! Это была кнопка от невидимого окна Application. Это же кривое поведение может быть доступно и в новых Delphi с MainFormOnTaskbar равным False. Иными словами, вам нужно знать, какой режим активен в вашем приложении, и чем Application.Handle отличается от Form1.Handle.
Пункт 2 - ну, это опционально, но вы можете использовать CreateComObject вместо ручного вызова CoCreateInstance:
uses ComObj; ... begin FTaskbar := CreateComObject(CLSID_TaskbarList) as ITaskbarList; FTaskbar.HrInit; end;
Пункт 3 - Delphi часто автоматически выполняет за вас вызов CoInitialize(Ex), но когда она это не делает, многие просто не знают, что это нужно делать.
Шаг 6 - готовое решение
Например (вариант для MainFormOnTaskbar = True):type
TForm1 = class(TForm)
protected
procedure CreateWnd; override;
end;
...
procedure TForm1.CreateWnd;
var
Taskbar: ITaskbarList;
begin
inherited;
Taskbar := CreateComObject(CLSID_TaskbarList) as ITaskbarList;
Taskbar.HrInit;
Taskbar.DeleteTab(Handle);
end;
Вы можете использовать и другой подход. Я не хочу и не буду расписывать все варианты решения - просто используйте свою голову.Заключение
Как видите, это простая задачка на 15 минут максимум. Однако, чтобы сделать это, мне понадобилось:- Хорошее знание Delphi:
- Я знал про CreateParams (если вы выбрали путь со сменой условий).
- Я знал про пересоздание окон, поэтому повесил код с ITaskbarList на CreateWnd, а не на (более простой, но неверный вариант) FormCreate.
- Я знал про CreateComObject (хотя моё знание COM очень поверхностно, поэтому я могу где-то наврать).
- Я знал про Application.Handle <> Form1.Handle и MainFormOnTaskbar.
- Я знал про safecall в Delphi и обработку ошибок в COM.
- Знание C, чтобы переводить заголовочники.
- Знание WinAPI (Get/SetWindowLong, если вы взяли этот путь).
- Знание работы с битовой логикой (смена битовых флагов в CreateParams или SetWindowLong).
- Знание COM, чтобы успешно работать с интерфейсами и делать их перевод.
- Знание английского, чтобы читать документацию и выполнять поиск.
- Знание мест, где можно получить информацию (сайт MSDN, Wiki Embarcadero).
- Знание доступных решений (в нашем случае - JWAPI).
Кажется сложным? Ну, возьмите тогда компонент ;)
Примечание: цель этого поста - НЕ дать готовый код "бери это, он 100% рабочий, я гарантирую это". Цель поста - показать как вы можете использовать свою голову, когда скопированный код не работает. Смысл в том, чтобы попытаться понять, как работает код и что он делает. А потом увидеть, что идёт не так. Может быть, вы применяете код не к тому окну (помним Application.Handle против MainForm.Handle?), может быть, вы вызываете код в неверное время - и так далее.

8 комментарий(ев):
Обратите внимание, что при таком подходе, поскольку вы стартуете от документации, то этим вы автоматически отметаете проблемы вроде использования недокументированных возможностей.
И ещё: часто задают вопросы вида "как сделать прогресс на кнопке в Панели задач в Vista/Win7" или "как задать окно предпросмотра окна в Vista/Win7". Как нетрудно догадаться, описание этого функционала находится в двух шагах от нашего случая.
Я рекомендую вам разобраться с этим вопросом по аналогии, как самостоятельное упражнение.
Вообще, если говорить строго, то первый вариант решения со стилями - не решение проблемы "как мне убрать ЭТО окно". Потому что этот вариант не убирает ЭТО окно - он меняет окно на другое (которое не будет показываться) изменением стиля.
Не-а... Думаю предлагается просто добавить/изменить флаги...
Окно же не станет от этого "другим"...
"как сделать прогресс на кнопке в Панели задач в Vista/Win7" - для XP? о__О
Хм... Хорошая идея.....)))
Пробовал способ в шаг№6 - на панели задач программа остается видимой.
Если написать Form1.visible:=false - работает. Но некоторые антивирусы распознают это уже , как вирус. В частности AVAST.
А вот рабочий код
procedure TForm1.Button1Click(Sender: TObject);
begin
// nStyle - глобальная переменная , в которой хранится старое значение стиля окна
nStyle:=GetWindowLong(Application.Handle, GWL_EXSTYLE or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_HIDE);
// устанавливаем новый стиль
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
ShowWindow(Application.Handle, SW_SHOW);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowWindow(Application.Handle, SW_HIDE);
// возвращаем окну старый стиль и показываем кнопку на панели задач.
SetWindowLong(Application.Handle, GWL_EXSTYLE, nStyle);
ShowWindow(Application.Handle, SW_SHOW);
end;
Специально же написал: "вариант для MainFormOnTaskbar = True".
(спрашивается: для кого написано примечание?)
"95% людей - идиоты."
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.