Reputation: 3258
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
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
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