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

Jam*_*amo 2 delphi components system-tray

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

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

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

感谢您的帮助.

Rob*_*edy 10

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

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

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

  • 感谢一个合理的答案,而不仅仅是对这个问题的判断/谴责 - 令人耳目一新和赞赏.这实际上是用户要求的功能,但我同意你的意思:问题比我的应用更大.我将看一下TraySaver代码.再次感谢. (2认同)

mgh*_*hie 6

访问和修改shell通知区域是一种破坏性但可能.您首先需要找到顶级窗口:

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

然后枚举其子项以查找托盘通知区域:

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;
Run Code Online (Sandbox Code Playgroud)

然后枚举其子项以查找带有通知图标的标准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;
Run Code Online (Sandbox Code Playgroud)

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