回答
在Windows 7和更高版本上,通知图标可以由用户定义的GUID标识。在较早的版本中,它们由HWND和ID号码的组合来代替。由于您的应用程序不能保证在下次运行时获得相同的HWND值,因此您可以对由HWND标识的旧图标执行任何操作的唯一方法是如果您记住了以前的HWND值,因此您可以使用它来删除旧图标,然后使用新的HWND添加新图标。但是,对于GUID标识的图标,GUID需要保持持久性(因为它存储在注册表中以存储与图标关联的应用设置),所以您应该能够根据需要随时更新现有图标,或者将其删除如果需要。
从文档中不清楚该GUID是否确实标识了单个图标,或者只是一个*类型的图标。也就是说,如果一个程序的多个实例同时运行,它们都必须为它们的图标使用不同的GUID吗?还是只是显示多个图标的实例必须为每个图标使用不同的GUID?由于前者难以实施,我期望后者。如果这是真的,那么稍后的程序实例仍然无法清理先前崩溃的实例的图标。 –
文档建议guid唯一标识应用程序可执行文件的特定副本的特定图标,而不是像HWND + ID组合一样用于应用程序的给定进程。文档确实说应用中的多个图标需要使用单独的GUID。此外,guid存储在注册表中,它存储每个guid的应用程序完整路径,因此并排安装必须在每次安装中对同一图标使用不同的guid。如果应用程序移动到新路径,则旧路径的GUID必须未注册,以便新路径可以与GUID关联。 –
因此,在这些条件下,它听起来像是应用程序进程的后期实例可以控制以前的应用程序进程的图标,因为它们在通过guid进行标识时并不与任何给定的进程绑定。但我可能是错的。我仍然在我的应用程序中使用HWND + ID组合,但尚未更新为使用GUID。 –
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.
我不会说挖掘另一个进程的内存来获取无证信息真的是“正确方向的指导”。 –
- 1. 如何从通知区域删除特定的远程通知
- 2. Android:从通知栏中删除通知
- 3. 从android通知栏中删除通知
- 4. 如何从DateTime值中删除区域?
- 5. 从通知列表中删除持久通知(通知API)
- 6. 通知区域弹出链接区域
- 7. 如何从通知区域删除我的应用程序的幽灵图标?
- 8. 删除通知
- 9. 删除通知
- 10. 通过点击从通知中心删除通知
- 11. 从通知中心删除UILocalNotifications
- 12. 从通知中删除振动
- 13. 从Firebase中删除过期通知
- 14. 从通知中删除应用程序
- 15. 从DNS区域删除主机
- 16. Android通知区域风格
- 17. 删除HTML中未知的空白区域
- 18. 以编程方式从通知中心删除远程通知
- 19. 如何从通知中删除特定的推送通知Swift
- 20. 从通知抽屉中删除所有通知
- 21. 从通知中心重新删除通知
- 22. 从通知中心删除单个通知iOS
- 23. iPhone:如何从通知中心删除通知
- 24. 同时从列表视图和通知栏中删除通知
- 25. 在PHP中删除通知
- 26. HBase:如何删除区域
- 27. OneSignal删除通知
- 28. Android通知删除
- 29. 删除通知xamarin
- 30. 通知区域中的下拉列表
请定义“突然终止”。 –
当应用程序崩溃或意外关闭。 – DelPhi
在这些情况下,您仍然有机会优雅地删除通知图标。我假设你正在尝试/最终正确使用。这实际上只是强制终止(TerminateProcess),你无法抵御。 –