回答
在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),你無法抵禦。 –