2013-10-04 2 views
4

앱이 갑자기 종료되면 알림 영역 (시스템 트레이)에서 NotifyIcon을 제거 할 수 있습니까?알림 영역에서 NotifyIcon 제거

만약 아니라면, 다음에 앱을 실행할 때 어떻게 제거 할 수 있습니까?

+2

"갑자기 종료"를 지정하십시오. –

+1

응용 프로그램이 충돌하거나 예기치 않게 종료됩니다. – DelPhi

+1

이러한 상황에서는 알림 아이콘을 정상적으로 제거 할 수 있습니다. 나는 try/finally를 제대로 사용하고 있다고 가정합니다. 그것은 정말로 당신을 방어 할 수없는 강제 종료 (TerminateProcess)입니다. –

답변

5

갑자기? 아닙니다. 프로그램이 종료되었으므로 코드를 실행하여 셸에 아이콘을 제거해야한다고 알릴 기회가 없습니다.

아이콘을 제거하려면 아이콘 위로 마우스를 이동하십시오. 쉘은 프로그램에 알림을 보내고 더 이상 아무 것도 없다는 것을 깨닫고 아이콘 만 제거합니다.

+0

트레이 아이콘이있는 응용 프로그램이 충돌 할 때 Windows가 동작하는 방식이라는 것을 알고 있습니다. 하지만 다음 번에 앱을 실행할 때 제거하고 싶습니다. – DelPhi

+0

@DelPhi, 이전 아이콘 매개 변수를 적어두고 시작할 때'NIM_DELETE '을 시도 할 수 있습니다. –

+0

try/finally와이 명령 {Shell_NotifyIcon (NIM_DELETE, @IconData);}을 사용하고 있습니다. – DelPhi

3

Windows 7 이상에서는 사용자 정의 GUID로 알림 아이콘을 식별 할 수 있습니다. 이전 버전에서는 HWND와 ID 번호의 조합으로 식별되었습니다. 앱이 다음 번에 실행될 때 동일한 HWND 값을 얻지 못하기 때문에 HWND로 식별되는 이전 아이콘에 대해 아무 것도 할 수있는 유일한 방법은 이전 HWND 값을 기억하여이를 사용하여 이전 아이콘을 누른 다음 새 HWND를 사용하여 새 아이콘을 추가하십시오. 그러나 GUID로 식별 된 아이콘의 경우 GUID는 영구적이어야합니다 (아이콘과 연결된 응용 프로그램 설정을 저장하기 위해 레지스트리에 저장되므로). 필요에 따라 기존 아이콘을 계속 업데이트하거나 제거 할 수 있어야합니다 만약 원한다면.

+0

설명서에서 GUID가 실제로 하나의 아이콘인지, 아니면 하나의 * 유형 * 아이콘인지를 명확히 알 수는 없습니다. 즉, 프로그램의 여러 인스턴스가 동시에 실행되는 경우 모두 해당 아이콘에 고유 한 GUID를 사용해야합니까? 아니면 여러 아이콘을 보여주는 인스턴스가 각 아이콘에 대해 다른 GUID를 사용해야한다는 것입니까?나는 후자를 구현하기가 어려울 것이기 때문에 후자를 기대할 것이다. 그리고 이것이 사실이라면 프로그램의 나중 인스턴스가 여전히 이전에 손상된 인스턴스의 아이콘을 정리할 수 없습니다. –

+0

문서에서는 GUID가 HWND + ID 콤보와 같이 앱의 특정 프로세스가 아니라 앱 실행 파일의 특정 복사본에 대한 특정 아이콘을 고유하게 식별한다고 제안합니다. 워드 프로세서는 앱의 여러 아이콘이 별도의 가이드를 사용해야한다고 말합니다. 또한 guid는 레지스트리에 저장되며 각 GUID와 함께 앱의 전체 경로를 저장하므로 병행 설치는 각 설치에서 동일한 아이콘에 대해 다른 guid를 사용해야합니다. 앱이 새 경로로 이동하면 이전 경로의 GUID가 등록 취소되어야하므로 새 경로가 GUID와 연결될 수 있습니다. –

+0

그런 상황에서는 앱 프로세스의 나중 인스턴스가 이전 app 프로세스의 아이콘을 제어 할 수있는 것처럼 들리지만, guid로 식별 할 때 주어진 프로세스에 연결되어 있지 않기 때문입니다. 그러나 나는 틀릴 수도 있습니다. 나는 여전히 애플 리케이션에서 HWND + ID 콤보를 사용하지만, 아직 GUID를 사용하도록 업데이트되지 않았다. –

0

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

문서화되지 않은 정보를 가져 오기 위해 다른 프로세스의 메모리를 파헤 치는 것이 실제로 올바른 방향으로 인도 될 것이라고 나는 말할 수 없습니다. –