앱이 갑자기 종료되면 알림 영역 (시스템 트레이)에서 NotifyIcon
을 제거 할 수 있습니까?알림 영역에서 NotifyIcon 제거
만약 아니라면, 다음에 앱을 실행할 때 어떻게 제거 할 수 있습니까?
앱이 갑자기 종료되면 알림 영역 (시스템 트레이)에서 NotifyIcon
을 제거 할 수 있습니까?알림 영역에서 NotifyIcon 제거
만약 아니라면, 다음에 앱을 실행할 때 어떻게 제거 할 수 있습니까?
갑자기? 아닙니다. 프로그램이 종료되었으므로 코드를 실행하여 셸에 아이콘을 제거해야한다고 알릴 기회가 없습니다.
아이콘을 제거하려면 아이콘 위로 마우스를 이동하십시오. 쉘은 프로그램에 알림을 보내고 더 이상 아무 것도 없다는 것을 깨닫고 아이콘 만 제거합니다.
Windows 7 이상에서는 사용자 정의 GUID로 알림 아이콘을 식별 할 수 있습니다. 이전 버전에서는 HWND와 ID 번호의 조합으로 식별되었습니다. 앱이 다음 번에 실행될 때 동일한 HWND 값을 얻지 못하기 때문에 HWND로 식별되는 이전 아이콘에 대해 아무 것도 할 수있는 유일한 방법은 이전 HWND 값을 기억하여이를 사용하여 이전 아이콘을 누른 다음 새 HWND를 사용하여 새 아이콘을 추가하십시오. 그러나 GUID로 식별 된 아이콘의 경우 GUID는 영구적이어야합니다 (아이콘과 연결된 응용 프로그램 설정을 저장하기 위해 레지스트리에 저장되므로). 필요에 따라 기존 아이콘을 계속 업데이트하거나 제거 할 수 있어야합니다 만약 원한다면.
설명서에서 GUID가 실제로 하나의 아이콘인지, 아니면 하나의 * 유형 * 아이콘인지를 명확히 알 수는 없습니다. 즉, 프로그램의 여러 인스턴스가 동시에 실행되는 경우 모두 해당 아이콘에 고유 한 GUID를 사용해야합니까? 아니면 여러 아이콘을 보여주는 인스턴스가 각 아이콘에 대해 다른 GUID를 사용해야한다는 것입니까?나는 후자를 구현하기가 어려울 것이기 때문에 후자를 기대할 것이다. 그리고 이것이 사실이라면 프로그램의 나중 인스턴스가 여전히 이전에 손상된 인스턴스의 아이콘을 정리할 수 없습니다. –
문서에서는 GUID가 HWND + ID 콤보와 같이 앱의 특정 프로세스가 아니라 앱 실행 파일의 특정 복사본에 대한 특정 아이콘을 고유하게 식별한다고 제안합니다. 워드 프로세서는 앱의 여러 아이콘이 별도의 가이드를 사용해야한다고 말합니다. 또한 guid는 레지스트리에 저장되며 각 GUID와 함께 앱의 전체 경로를 저장하므로 병행 설치는 각 설치에서 동일한 아이콘에 대해 다른 guid를 사용해야합니다. 앱이 새 경로로 이동하면 이전 경로의 GUID가 등록 취소되어야하므로 새 경로가 GUID와 연결될 수 있습니다. –
그런 상황에서는 앱 프로세스의 나중 인스턴스가 이전 app 프로세스의 아이콘을 제어 할 수있는 것처럼 들리지만, 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.
문서화되지 않은 정보를 가져 오기 위해 다른 프로세스의 메모리를 파헤 치는 것이 실제로 올바른 방향으로 인도 될 것이라고 나는 말할 수 없습니다. –
"갑자기 종료"를 지정하십시오. –
응용 프로그램이 충돌하거나 예기치 않게 종료됩니다. – DelPhi
이러한 상황에서는 알림 아이콘을 정상적으로 제거 할 수 있습니다. 나는 try/finally를 제대로 사용하고 있다고 가정합니다. 그것은 정말로 당신을 방어 할 수없는 강제 종료 (TerminateProcess)입니다. –