Можно ли удалить NotifyIcon
из области уведомлений (в системном трее), когда приложение завершается внезапно?Удаление NotifyIcon из области уведомлений
Если нет, как его можно удалить, когда приложение запускается в следующий раз?
Можно ли удалить NotifyIcon
из области уведомлений (в системном трее), когда приложение завершается внезапно?Удаление NotifyIcon из области уведомлений
Если нет, как его можно удалить, когда приложение запускается в следующий раз?
Abruttly? Нет. Ваша программа перестала существовать, поэтому нет возможности запускать какой-либо код, чтобы сообщить оболочке, что он должен удалить значок.
Чтобы удалить значок, наведите указатель мыши на него. Оболочка попытается уведомить вашу программу, понять, что там ничего нет, и удалить значок самостоятельно.
Я знаю, что это просто так, как ведет себя Windows, когда приложение с иконкой в трее падает. Но я хочу удалить его, когда приложение будет работать в следующий раз. – DelPhi
@DelPhi, вы можете записать предыдущие параметры значка и попробовать «NIM_DELETE» при запуске. –
Я использую try/finally и эту команду {Shell_NotifyIcon (NIM_DELETE, @IconData);}. – DelPhi
В Windows 7 и более поздних версиях значки уведомлений могут быть идентифицированы с помощью определяемого пользователем GUID. В более ранних версиях они идентифицируются комбинацией HWND и идентификационного номера. Поскольку вашему приложению не гарантируется получение того же значения HWND при следующем запуске, единственный способ, которым вы можете сделать что-либо, для старого значка, идентифицированного HWND, - это помнить предыдущее значение HWND, чтобы вы могли использовать его для удаления старый значок, прежде чем использовать новый HWND, чтобы добавить новый значок. Но с идентификатором GUID, GUID должен быть постоянным (поскольку он хранится в Реестре для хранения настроек приложения, связанных с пиктограммой), поэтому вы должны просто продолжать обновлять существующий значок по мере необходимости или удалять его если это необходимо.
Из документации неясно, действительно ли этот идентификатор GUID идентифицирует один значок или только один * тип * значка. То есть, если несколько экземпляров программы запускаются одновременно, должны ли они использовать разные идентификаторы GUID для своих значков? Или просто экземпляр, который показывает несколько значков, должен использовать другой идентификатор GUID для каждого значка?Я бы ожидал, что последний, поскольку первый будет трудно реализовать. И если это так, то более поздний экземпляр программы все равно не сможет очистить значок предыдущего разбитого экземпляра. –
Документы предполагают, что guid однозначно идентифицирует конкретный значок для данной копии исполняемого файла приложения, а не для определенного процесса приложения, подобного комбинации HWND + ID. Документы говорят, что для нескольких значков в приложении необходимо использовать отдельные подсказки. Кроме того, эти регистры хранятся в реестре, и он сохраняет полный путь приложения к каждому направляющему устройству, поэтому для параллельной установки должны использоваться разные контуры для одного и того же значка в каждой установке. Если приложение переместится на новый путь, то (ы) пути (ов) старого пути должны быть незарегистрированы, чтобы новый путь мог быть связан с guid (s). –
Таким образом, в таких условиях он звучит как более поздний экземпляр процесса приложения, который может управлять значками предыдущего приложения, поскольку они не привязаны к какому-либо процессу, если он идентифицирован директором. Но я могу ошибаться. Я все еще использую HWND + ID комбо в своих приложениях, они еще не обновлены, чтобы использовать показы. –
FWIW, так как кода пока не существует, я подумал, что я бы это сделал. Я не знаю, поможет ли это OP 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.
Я бы не сказал, что копание в памяти другого процесса для получения недокументированной информации - это действительно «руководство в правильном направлении». –
Пожалуйста, определите, что «прерывается внезапно». –
Когда приложение аварийно завершает работу или неожиданно завершает работу. – DelPhi
В этих ситуациях у вас все еще есть возможность удалить извещение значок изящно. Я предполагаю, что вы правильно используете try/finally. Это действительно просто принудительное прекращение (TerminateProcess), с которым вы не можете защищаться. –