Windows系统托盘图标 - 控制位置

时间:2009-12-28 22:00:24

标签: delphi components system-tray

我有几个旧的应用程序(在Delphi中),由于各种原因使用系统托盘图标。大多数人正在使用AppControls TacTrayIcon或其他类似的组件。

以下是我的问题:如何控制托盘图标的位置? (即,相对于系统时间的位置 - 第一位置/“槽”,第二位置/“槽”等)。我记得看到一个演示(C#,如果内存服务),允许用户“向左移动图标”和“向右移动图标”,但不记得它是如何完成的。

我想让用户选择他们想要显示图标的位置,对于Windows 2000 - Windows 7.(我知道Windows 7处理系统托盘的东西有点不同,但没有测试出来尚未)。

感谢您的帮助。

3 个答案:

答案 0 :(得分:10)

程序无法记录或支持其控制shell通知图标位置的方法。甚至没有什么能保证它们会出现,或者它们确实出现,它们会出现在任何时间点附近,这样你的定位指令就没有任何意义。

(我以前使用的程序劫持了部分或全部图标,并可选择将它们显示在自己的窗口中,而不是显示在时钟附近的区域。由Mike Lin提供TraySaver。来源是如果你想看看他的黑客如何工作,你可以使用。)

图标的位置不在您的控制之下。我给你的建议是不要试图让你的程序负起责任,特别是如果没有人真正从你的程序中请求过这样的功能。如果人们想要控制你的程序的图标位置,他们可能想要控制其他程序的图标位置,在这种情况下,问题比你还要大。

答案 1 :(得分:6)

访问和修改shell通知区域是hackish但可能。您首先需要找到顶级窗口:

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;

您应该能够通过其名称识别通知图标的按钮,然后删除该按钮,然后将其插入所需位置。省略了所有错误处理,但这应该让你开始。

答案 2 :(得分:1)

看看this article on CodeProject,希望它有所帮助。

此解决方案的兼容性非常有限,根本不推荐。