2

我有一些我(在 Delphi 中)编写的旧应用程序,由于各种原因,它们使用系统托盘图标。大多数都在使用 AppControls TacTrayIcon 或其他一些类似的组件。

这是我的问题:如何控制托盘图标的位置?(例如,相对于系统时间的位置——第一个位置/“插槽”、第二个位置/“插槽”等)。我记得看过一个演示(C#,如果没记错的话),它允许用户“向左移动图标”和“向右移动图标”,但不记得它是如何完成的。

对于 Windows 2000 - Windows 7,我想允许用户选择他们希望图标出现的位置。(我了解 Windows 7 处理系统托盘的内容略有不同,但尚未对其进行测试)。

感谢您的任何帮助。

4

3 回答 3

10

程序没有记录或支持的方式来控制其外壳通知图标的位置。甚至没有任何东西可以保证它们会出现,或者如果它们确实出现了,它们会出现在时钟附近的任何地方,这样你的定位指令就会有意义。

(我曾经使用一个程序劫持了部分或全部图标,并有选择地将它们显示在自己的窗口中,而不是在时钟附近的区域中。它是由 Mike Lin 编写的TraySaver。如果您想了解如何使用,可以使用源代码他的 hack 奏效了。)

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

于 2009-12-28T23:22:38.143 回答
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;

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

于 2009-12-29T09:54:12.147 回答
1

查看CodeProject 上的这篇文章,希望对您有所帮助。

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

于 2009-12-29T13:36:06.560 回答