Jamo
Jamo

Reputation: 3258

Windows System Tray icons - controlling position

I have a few old apps I've written (in Delphi) which for various reasons use a system tray icon. Most are using AppControls TacTrayIcon or some other similar component.

Here's my question: How does one control the position of a tray icon? (i.e. where it is, say, relative to the system time -- 1st position/"slot", 2nd position/"slot", etc). I recall seeing a demo (C#, if memory serves) that allowed the user to "shift icon to the left" and "shift icon to the right", but don't recall how it was done.

I'd like to allow the user to select what position they want to icon to appear in, for Windows 2000 - Windows 7. (I understand Windows 7 handles system tray stuff a little differently, but haven't tested that out yet).

Thanks for any and all help.

Upvotes: 2

Views: 4464

Answers (2)

mghie
mghie

Reputation: 32344

Accessing and modifying the shell notification area is hackish but possible. You first need to find the top level window:

var
  Wnd: HWND;
begin
  Wnd := FindWindow('Shell_TrayWnd', nil);
  if IsWindow(Wnd) then
    EnumChildWindows(Wnd, @FindTrayWnd, 0);
end;

then enumerate its children to find the tray notification area:

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;

then enumerate its children to find the standard Windows toolbar with the notification icons. Windows messages are used to get or set toolbar properties. Since the toolbar lives in another process you need to employ ReadProcessMemory() and WriteProcessMemory() for all messages that involve a buffer of some sort (like getting the button text or button info):

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;

You should be able to identify the button of your notification icon via its name, then delete that button, then insert it at the desired position. All error handling omitted, but this should get you started.

Upvotes: 6

Rob Kennedy
Rob Kennedy

Reputation: 163357

There is no documented or supported way for programs to control the positions of their shell notification icons. There's not even anything guaranteeing they will appear at all, or if they do appear, that they will appear anywhere near the clock, such that your positioning instructions would make any sense.

(I used to use a program that hijacked some or all of the icons and optionally displayed them in its own window instead of in the area near the clock. It was TraySaver, by Mike Lin. The source is available if you wish to see how his hack worked.)

The icon's position is not under your control. My advice to you is to not try to make it your program's responsibility, especially if nobody has actually requested such functionality from your program in the first place. If people want to control your program's icon location, they probably want to control other programs' icon locations, in which case the problem is bigger than you anyway.

Upvotes: 10

Related Questions