2009-12-28 3 views
2

У меня есть несколько старых приложений, которые я написал (в Delphi), которые по разным причинам используют значок в системном трее. Большинство из них используют AppControls TacTrayIcon или какой-либо другой подобный компонент.Значки в системном лотке Windows - контрольное положение

вот мой вопрос: как управлять позицией значка в лотке? (то есть, где это, скажем, относительно системного времени - 1-я позиция/«слот», 2-я позиция/«слот» и т. д.). Я помню, что видел демонстрацию (C#, если память служит), которая позволила пользователю «сдвинуть значок влево» и «сдвинуть значок вправо», но не помните, как это было сделано.

Я хотел бы разрешить пользователю выбирать, какую позицию они хотят для отображения, для Windows 2000 - Windows 7. (Я понимаю, что Windows 7 обрабатывает элементы в системном трее несколько иначе, но не проверял, что).

Спасибо за любую помощь.

+15

Каждый раз, когда вы хотите что-то сделать и не можете найти решение в Windows API, вы, вероятно, должны спросить себя, есть ли для этого веские причины, например, это просто не ваш бизнес. Это не точное совпадение, но см. Http://blogs.msdn.com/oldnewthing/archive/2006/11/01/922449.aspx для общей идеи.Возможно, вы должны пересмотреть свои различные причины, чтобы иметь значок в трее. И если причина действительно звучит, просто придумайте хорошую икону и оставьте ее в покое. – mghie

+0

@mghie - Трудно не понимать как тон вашего комментария, так и ссылку, которую вы предоставляете, что вы подразумеваете, что я пытаюсь захватить или вторгаться в некоторые настройки, контролируемые пользователем. Это не тот случай - я пытаюсь помочь пользователю контролировать положение значка, потому что он запросил * его, Windows (XP, в любом случае) не облегчает его напрямую, и я видел, как это было полезно в другом месте. – Jamo

+0

@ Джамо: Извините, я не хотел ничего подразумевать. Интерфейс Windows API огромен и часто имеет несколько способов достижения одного и того же; если нет готового способа добиться чего-то, то это чаще, чем не намеренно. И вы, конечно, не будете вторгаться в некоторые контролируемые пользователем настройки - ваш клиент требует контроля над чем-то, что не предназначено для контроля. – mghie

ответ

10

Для программ для управления позициями значков уведомлений об их оболочке нет документально подтвержденного или поддерживаемого способа. Там даже нет никаких гарантий, что они вообще появятся или если они появятся, что они появятся где-нибудь рядом с часами, так что ваши инструкции по размещению будут иметь какой-то смысл.

(Раньше я использовал программу, которая украла некоторые или все значки и, возможно, отобразила их в своем собственном окне, а не в области, близкой к часам. Это было TraySaver от Майка Лина. Источник доступен, если вы хотите посмотреть, как работает его хак.)

Позиция значка не находится под вашим контролем. Мой совет для вас - не пытаться возложить ответственность за вашу программу, особенно если никто из них не запрашивал такую ​​функциональность из вашей программы в первую очередь. Если люди хотят контролировать расположение значков вашей программы, они, вероятно, хотят контролировать расположение иконок других программ, и в этом случае проблема будет больше, чем вы.

+2

Спасибо за законный ответ, а не просто суждение/осуждение вопроса - его освежающий и оцененный. На самом деле это функция, которую запрашивают пользователи, хотя я согласен с вами: проблема больше, чем просто мое приложение. Я посмотрю код TraySaver. Еще раз спасибо. – Jamo

6

Доступ к и изменение области уведомлений оболочки является хакерским, но возможным. Сначала нужно найти окно верхнего уровня:

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; 

Вы должны быть в состоянии идентифицировать кнопку вашего значка уведомления через его имя, затем удалите эту кнопку, а затем вставьте ее в нужную позицию. Вся обработка ошибок опущена, но это должно заставить вас начать.

+0

mghie, большое спасибо за то, что нашли время, чтобы опубликовать это, а также для ваших добрых и уточняющих предложений выше - я действительно ценю оба. (Даже очень!). Кроме того, ваша точка отмечена повторно: «ваш клиент требует контроля над тем, что не предназначено для контроля». Мне кажется странным, что это уже не «контролируемо» пользователем непосредственно в Windows, даже если это не программно с помощью приложений, как упоминает Чен в статье, к которой вы привязались. – Jamo

1

Посмотрите на this article on CodeProject, надеюсь, что это поможет.

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

+0

Ах - спасибо! Это проект, который я видел раньше, но у меня проблемы с поиском. Point отметил re: «не рекомендуется вообще»; будет изучать его с осторожностью. ;) – Jamo

+0

Ок. Обратите внимание на чтение памяти других процессов и пользовательских привилегий, необходимых для этого действия. Другое дело - антивирусное программное обеспечение, которое контролирует такую ​​деятельность. – ThinkJet