DelPhi
DelPhi

Reputation: 83

Removing NotifyIcon from the notification area

Is it possible to remove NotifyIcon from the notification area (system tray) when an app terminates abruptly?

if no, how can I remove it when the app runs for the next time?

Upvotes: 6

Views: 3406

Answers (3)

Glenn1234
Glenn1234

Reputation: 2582

FWIW, since code doesn't exist so far, I thought I'd throw this in. I don't know if it will help or not for the OP, but it should be good guidance in the right direction.

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.

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 598309

On Windows 7 and later, notify icons can be identified by a user-defined GUID. On earlier versions, they are identified by a combination of HWND and ID number instead. Since your app is not guaranteed to get the same HWND value the next time it runs, the only way you can do anything to an old icon that is identified by HWND is if you remembered the previous HWND value so you can use it to remove the old icon, before then using a new HWND to add a new icon. But with a GUID-identified icon, the GUID needs to be persistent (as it is stored in the Registry to store app settings associated with the icon), so you should be able to simply keep updating the existing icon as needed, or remove it if desired.

Upvotes: 3

Rob Kennedy
Rob Kennedy

Reputation: 163357

Abruptly? No. Your program has ceased to exist, so there's no opportunity to run any code to tell the shell that it should remove the icon.

To remove the icon, move your mouse over it. The shell will try to notify your program, realize there's nothing there anymore, and remove the icon by itself.

Upvotes: 5

Related Questions