Значки в системном трее Windows - контроль положения
У меня есть несколько старых приложений, которые я написал (на Delphi), которые по разным причинам используют значок в системном трее. Большинство из них используют AppControls TacTrayIcon или какой-либо другой аналогичный компонент.
Вот мой вопрос: как можно контролировать положение иконки в трее? (т.е. где это, скажем, относительно системного времени - 1-я позиция /"слот", 2-я позиция /"слот" и т. д.). Я помню, как видел демонстрацию (C#, если память не изменяет), которая позволяла пользователю "перемещать значок влево" и "перемещать значок вправо", но не помню, как это было сделано.
Я бы хотел, чтобы пользователь мог выбрать, в какой позиции он хочет, чтобы иконка отображалась, для Windows 2000 - Windows 7. (Я понимаю, что Windows 7 обрабатывает содержимое панели задач немного по-другому, но еще не проверял это).
Спасибо за любую помощь.
3 ответа
Для программ нет документированного или поддерживаемого способа управления позициями значков уведомлений оболочки. Нет даже ничего, что гарантировало бы, что они появятся вообще, или, если они действительно появятся, что они появятся где-нибудь около часов, так что ваши инструкции по размещению будут иметь какой-то смысл.
(Раньше я использовал программу, которая захватила некоторые или все значки и при желании отображала их в своем собственном окне, а не в области рядом с часами. Это был TraySaver, автор Mike Lin. Источник доступен, если вы хотите увидеть, как его взлом сработал.)
Положение значка не находится под вашим контролем. Мой вам совет: не пытайтесь сделать это обязанностью вашей программы, особенно если никто не запрашивал такую функцию у вашей программы. Если люди хотят контролировать расположение значков вашей программы, они, вероятно, хотят контролировать расположение значков других программ, и в этом случае проблема в любом случае больше, чем вы.
Доступ к области уведомлений оболочки и ее изменение являются хакерскими, но возможными. Сначала вам нужно найти окно верхнего уровня:
var
Wnd: HWND;
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
if IsWindow(Wnd) then
EnumChildWindows(Wnd, @FindTrayWnd, 0);
end;
затем перечислите его дочерние элементы, чтобы найти область уведомлений в области уведомлений:
function FindTrayWnd(AWnd: HWND; AParam: LPARAM): BOOL; stdcall;
var
ClassName: string;
begin
SetLength(ClassName, 64);
SetLength(ClassName, GetClassName(AWnd, PChar(ClassName), 64));
Result := True;
if AnsiCompareText(ClassName, 'TrayNotifyWnd') = 0 then begin
EnumChildWindows(AWnd, @FindToolbar, 0);
Result := False;
end;
end;
затем перечислите его дочерние элементы, чтобы найти стандартную панель инструментов Windows со значками уведомлений. Сообщения Windows используются для получения или установки свойств панели инструментов. Поскольку панель инструментов живет в другом процессе, вам нужно использовать ReadProcessMemory()
а также WriteProcessMemory()
для всех сообщений, которые содержат некоторый буфер (например, получение текста кнопки или информации о кнопке):
function FindToolbar(AWnd: HWND; AParam: LPARAM): BOOL; stdcall;
const
VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE;
var
ClassName: string;
i, ButtonCount: integer;
ProcessId, BytesRead: Cardinal;
ProcessHandle: THandle;
ExplorerButtonInfo: PTBButton;
ButtonInfo: array of TTBButton;
begin
SetLength(ClassName, 64);
SetLength(ClassName, GetClassName(AWnd, PChar(ClassName), 64));
if AnsiCompareText(ClassName, 'ToolbarWindow32') = 0 then begin
GetWindowThreadProcessId(AWnd, @ProcessId);
ProcessHandle := OpenProcess(VMFLAGS, FALSE, ProcessId);
ExplorerButtonInfo := VirtualAllocEx(ProcessHandle, nil, SizeOf(TTBButton),
MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
if ExplorerButtonInfo <> nil then try
ButtonCount := SendMessage(AWnd, TB_BUTTONCOUNT, 0, 0);
SetLength(ButtonInfo, ButtonCount);
for i := 0 to ButtonCount - 1 do begin
SendMessage(AWnd, TB_GETBUTTON, i, LPARAM(ExplorerButtonInfo));
ReadProcessMemory(ProcessHandle, ExplorerButtonInfo, @ButtonInfo[i],
SizeOf(TTBButton), BytesRead);
end;
// manipulate the button info, use WriteProcessMemory() and SendMessage()
// to repopulate the toolbar
finally
VirtualFreeEx(ProcessId, ExplorerButtonInfo, SizeOf(TTBButton),
MEM_RELEASE);
end;
Result := False;
end else
Result := True;
end;
Вы должны быть в состоянии идентифицировать кнопку вашего значка уведомления по ее имени, затем удалить эту кнопку, а затем вставить ее в нужное место. Вся обработка ошибок опущена, но с этого следует начать.
Посмотрите на эту статью на CodeProject, надеюсь, это поможет.
Это решение имеет очень ограниченную совместимость и не рекомендуется вообще.