Значки в системном трее 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, надеюсь, это поможет.

Это решение имеет очень ограниченную совместимость и не рекомендуется вообще.

Другие вопросы по тегам