2011-12-01 70 views
5

我有一個具有系統托盤圖標的應用程序。卸載時,我正在殺死進程,如果它的運行。所以,當我沒有優雅地停止應用程序時,圖標將保留在系統托盤中,並且只有當我們將鼠標懸停在系統托盤上時纔會刪除。我編寫了一個代碼,可以沿着托盤運行光標,並將光標恢復到初始位置。這是我所做的:以編程方式刷新系統托盤圖標

 [DllImport("user32.dll")] 
     static extern IntPtr FindWindow(string className, string windowName); 
     [DllImport("user32.dll")] 
     static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string className, string windowName); 
     [DllImport("user32.dll")] 
     static extern bool GetWindowRect(HandleRef handle, out RECT rct); 

     [StructLayout(LayoutKind.Sequential)] 
     struct RECT 
     { 
      public int Left; 
      public int Top; 
      public int Right; 
      public int Bottom; 
     } 

     void RefreshTray() 
     { 
      IntPtr taskbar_Handle = FindWindow("Shell_Traywnd", ""); 
      IntPtr tray_Handle = FindWindowEx(taskbar_Handle, IntPtr.Zero, "TrayNotifyWnd", ""); 

      RECT rct; 

      if (!(GetWindowRect(new HandleRef(null, tray_Handle), out rct))) 
      { 
      } 

      System.Drawing.Point init = Control.MousePosition; 

      for (int i = rct.Left; i < rct.Right-20; i++) 
      { 
       Cursor.Position = new System.Drawing.Point(i, (rct.Bottom + rct.Top)/2); 
      } 

      Cursor.Position = init; 
     } 

這適用於所有情況,除非選項「不顯示通知圖標」啓用。在這種情況下,有什麼方法可以刷新托盤?

編輯 正如評論建議我改變了我的方法。我沒有殺死托盤應用程序,而是在我的應用程序服務(是的,忘記提及,我的服務也與應用程序一起運行)和托盤應用程序之間建立了通信。在卸載時,我停止服務,從服務停止方法,我會發送特定格式的套接字消息到托盤應用程序,並要求它關閉,我會將通知圖標可見性設置爲false。這會讓Tray應用程序在後臺運行,所以我使用「taskkill」來刪除應用程序。它在Win7和Vista中運行良好,但在Win XP中無法正常工作。但我沒有寫任何環境特定的代碼。任何可能的線索?

+2

那麼我曾經有過類似的情況。我所做的是在Form_Closing事件中處理NotifyIcon組件,它運行良好。 –

+3

更簡單的方法可能是通過卸載程序與應用程序進行通信。 (雖然我沒有這方面的知識) –

+8

你不想寫這樣的代碼。不要殺人,要好好問。 –

回答

1

不應該難以使用管道或TCP等關閉當前實例,如果您不想這樣做並且不運行.NET4.0。

大家都在暗示,問題是通過殺死你的進程它沒有機會註銷它的托盤圖標實例,所以它一直存在,直到Windows嘗試向它發送一個事件(下一次你移動鼠標移到它上面),在這一點上Windows將刪除它。

根據您使用的安裝程序,這可能很容易或更困難。最流行的安裝程序框架允許使用插件,其中一些支持Pipes,還有更多支持TCP請求。另外,寫一個小的可執行文件,安裝程序可以在它開始卸載過程之前運行它,它與主應用程序通信併發送一條關閉消息。作爲最後一點,如果你可以使用.NET4.0,那麼我會建議看看內置的System.IO.Pipes命名空間和包含的類。

8

這與我使用的相似。 我添加到觸摸畫廊界面的簡單浮動鍵盤。用戶還希望將我的鍵盤作爲桌面上的獨立應用程序。所以我做了這個,爲它創建了一個托盤應用程序。現在 - 如果它打開並且他們啓動我的畫廊呢?

他們會有兩個鍵盤。

當然 - 用戶可以結束第一個 - 但更容易結束它。我殺了它並沒有什麼影響,所以我這樣做。但托盤圖標仍然存在,因爲它正在等待事件。爲了解決這個問題,我刷新了托盤區域。

請注意 - 這隻適用於英文區域設置安裝。爲了使其能夠使用其他語言,請將「用戶推薦通知區域」和「通知區域」更改爲已翻譯的/等效的字符串。

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int left; 
    public int top; 
    public int right; 
    public int bottom; 
} 

[DllImport("user32.dll")] 
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

[DllImport("user32.dll")] 
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, 
    string lpszWindow); 

[DllImport("user32.dll")] 
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); 

[DllImport("user32.dll")] 
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); 

public static void RefreshTrayArea() 
{ 
    IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); 
    IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); 
    IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); 
    IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); 
    if (notificationAreaHandle == IntPtr.Zero) 
    { 
     notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", 
      "User Promoted Notification Area"); 
     IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null); 
     IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, 
      "ToolbarWindow32", "Overflow Notification Area"); 
     RefreshTrayArea(overflowNotificationAreaHandle); 
    } 
    RefreshTrayArea(notificationAreaHandle); 
} 

private static void RefreshTrayArea(IntPtr windowHandle) 
{ 
    const uint wmMousemove = 0x0200; 
    RECT rect; 
    GetClientRect(windowHandle, out rect); 
    for (var x = 0; x < rect.right; x += 5) 
     for (var y = 0; y < rect.bottom; y += 5) 
      SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x); 
} 
+0

難道你沒有辦法檢查應用程序是否已經在運行並告訴它們或將它關注,而不是殺死它並重新啓動它? – mbomb007

+0

這個答案效果最好,至少對於我的環境是Windows 10x64。它從所顯示和隱藏的圖標中刪除圖標:(http://maruf-dotnetdeveloper.blogspot.com/2012/08/c-refreshing-system-tray-icon.html)沒有刪除顯示的。 –

+0

使用Resource Hacker在%windir%\ system32 \ \ explorer32.exe.mui中查找本地化的字符串。您需要安裝所需的Windows語言包(MUI)。 – mcandal