6

NotifyIcon当应用程序突然终止时,是否可以从通知区域(系统托盘)中删除 ?

如果没有,下次运行应用程序时如何将其删除?

4

3 回答 3

5

突然?不,您的程序已不复存在,因此没有机会运行任何代码来告诉外壳程序它应该删除图标。

要删除图标,请将鼠标移到它上面。shell 将尝试通知您的程序,意识到那里没有任何东西,然后自行删除图标。

于 2013-10-04T13:10:09.343 回答
3

在 Windows 7 及更高版本上,通知图标可以通过用户定义的 GUID 来识别。在早期版本中,它们由 HWND 和 ID 号的组合来标识。由于您的应用程序无法保证在下次运行时获得相同的 HWND 值,因此您可以对由 HWND 标识的旧图标执行任何操作的唯一方法是,如果您记住了以前的 HWND 值,那么您可以使用它来删除旧图标,然后使用新的 HWND 添加新图标。但是对于 GUID 标识的图标,GUID 需要是持久的(因为它存储在注册表中以存储与图标关联的应用程序设置),因此您应该能够根据需要简单地继续更新现有图标,或者将其删除如果需要。

于 2013-10-04T15:54:29.530 回答
0

FWIW,由于到目前为止代码不存在,我想我会把它扔进去。我不知道它是否对 OP 有帮助,但它应该是正确方向的良好指导。

unit csystray;
  { removes dead system tray icons, by Glenn1234 @ stackoverflow.com
    since this uses "less than supported by Microsoft" means, it may
    not work on all operating system.  It was tested on Windows XP }
interface
  uses commCtrl, shellapi, windows;
type
  TTrayInfo = packed record
    hWnd: HWnd;
    uID: UINT;
    uCallBackMessage: UINT;
    Reserved1: array[0..1] of longint;
    Reserved2: array[0..2] of longint;
    hIcon: HICON;
  end;
  PTBButton = ^TTBButton;
  _TBBUTTON = packed record
    iBitmap: Integer;
    idCommand: Integer;
    fsState: Byte;
    fsStyle: Byte;
    bReserved: array[1..2] of Byte;
    dwData: Longint;
    iString: Integer;
  end;
  TTBButton = _TBBUTTON;

procedure RemoveStaleTrayIcons;

implementation

procedure RemoveStaleTrayIcons;
const
  VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ OR PROCESS_VM_WRITE;
var
  ProcessID: THandle;
  ProcessHandle: THandle;
  trayhandle: HWnd;
  ExplorerButtonInfo: Pointer;
  i: integer;
  ButtonCount: Longint;
  BytesRead: Longint;
  ButtonInfo: TTBButton;
  TrayInfo: TTrayInfo;
  ClassNameA: Array[0..255] of char;
  outlen: integer;
  TrayIconData: TNotifyIconData;
begin
  // walk down the window hierarchy to find the notification area window
  trayhandle := FindWindow('Shell_TrayWnd', '');
  trayhandle := FindWindowEx(trayhandle, 0, 'TrayNotifyWnd', nil);
  trayhandle := FindWindowEx(trayhandle, 0, 'SysPager', nil);
  trayhandle := FindWindowEx(trayhandle, 0, 'ToolbarWindow32', nil);
  if trayhandle = 0 then exit;
  // find the notification area process and open it up for reading.
  GetWindowThreadProcessId(trayhandle, @ProcessID);
  ProcessHandle := OpenProcess(VMFLAGS, false, ProcessID);
  ExplorerButtonInfo := VirtualAllocEx(ProcessHandle, nil, Sizeof(TTBButton),
       MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
  // the notification area is a tool bar.  Get the number of buttons.
  ButtonCount := SendMessage(trayhandle, TB_BUTTONCOUNT, 0, 0);
  if ExplorerButtonInfo <> nil then
    try
      // iterate the buttons & check.
      for i := (ButtonCount - 1) downto 0 do
        begin
          // get button information.
          SendMessage(trayhandle, TB_GETBUTTON, i, LParam(ExplorerButtonInfo));
          ReadProcessMemory(ProcessHandle, ExplorerButtonInfo, @ButtonInfo,
             Sizeof(TTBButton), BytesRead);
          // if there's tray data, read and process
          if Buttoninfo.dwData <> 0 then
            begin
              ReadProcessMemory(ProcessHandle, PChar(ButtonInfo.dwData),
                                @TrayInfo, Sizeof(TTrayInfo), BytesRead);
              // here's the validation test, this fails if the master window is invalid
              outlen := GetClassName(TrayInfo.hWnd, ClassNameA, 256);
              if outlen < 1 then
                begin
                  // duplicate the shell icon removal, i.e. my component's DeleteTray
                  TrayIconData.cbSize := sizeof(TrayIconData);
                  TrayIconData.Wnd := TrayInfo.hWnd;
                  TrayiconData.uID := TrayInfo.uID;
                  TrayIconData.uCallbackMessage := TrayInfo.uCallBackMessage;
                  Shell_NotifyIcon(NIM_DELETE, @TrayIconData);
                end;
            end;
        end;
    finally
      VirtualFreeEx(ProcessID, ExplorerButtonInfo, Sizeof(TTBButton), MEM_RELEASE);
    end;
end;

end.
于 2013-10-05T00:26:59.407 回答