2016-07-29 51 views
0

我編寫了一個程序,可以識別Windows系統中過時的軟件,並通過與用戶交互來更新它們。使用Windows API有條件地顯示多個氣球 - Delphi中的系統托盤圖標

它有一個軟件更新程序,它顯示系統托盤圖標並顯示關於系統中安裝的可用/下載更新和軟件的氣球提示。

問題是當每個任務正在處理它時,它不能顯示多個氣球提示。比如,當有可用的更新的軟件,它應該記住用戶展示像An update for Software Name is available.,當用戶選擇將其重新下載並最小化到系統托盤,氣球提示應該再次出現類似Updates are downloading...Click to view the Progress of Downloads.

但是氣球我想知道如何通過只使用一個系統托盤圖標來完成此操作?

我可以一次又一次使用NIM_MODIFY標記根據程序的當前狀態更改氣球提示嗎?

我搜索了這個,我發現了一些例子,但對於Visual Studio和C++。

這就是我試圖顯示多個提示,當程序運行時:

unit MainForm-1; 

... 

const 
    NIF_INFO = $10; 
    NIF_MESSAGE = 1; 
    NIF_ICON = 2; 
    NOTIFYICON_VERSION = 3; 
    NIF_TIP = 4; 
    NIM_SETVERSION = $00000004; 
    NIM_SETFOCUS = $00000003; 
    NIIF_INFO = $00000001; 
    NIIF_WARNING = $00000002; 
    NIIF_ERROR = $00000003; 

    NIN_BALLOONSHOW = WM_USER + 2; 
    NIN_BALLOONHIDE = WM_USER + 3; 
    NIN_BALLOONTIMEOUT = WM_USER + 4; 
    NIN_BALLOONUSERCLICK = WM_USER + 5; 
    NIN_SELECT = WM_USER + 0; 
    NINF_KEY = $1; 
    NIN_KEYSELECT = NIN_SELECT or NINF_KEY; 

    TRAY_CALLBACK = WM_USER + $7258; 

    PNewNotifyIconData = ^TNewNotifyIconData; 
    TDUMMYUNIONNAME = record 
    case Integer of 
     0: (uTimeout: UINT); 
     1: (uVersion: UINT); 
    end; 

    TNewNotifyIconData = record 
    cbSize: DWORD; 
    Wnd: HWND; 
    uID: UINT; 
    uFlags: UINT; 
    uCallbackMessage: UINT; 
    hIcon: HICON; 
    szTip: array [0..127] of Char; 
    dwState: DWORD;/
    dwStateMask: DWORD; 
    szInfo: array [0..255] of Char; 
    DUMMYUNIONNAME: TDUMMYUNIONNAME; 
    szInfoTitle: array [0..63] of Char; 
    dwInfoFlags: DWORD; 
end; 

type 
    MainForm-1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
private 
    IconData: TNewNotifyIconData; 
    procedure SysTrayIconMessageHandler(var Msg: TMessage); message TRAY_CALLBACK; 
    procedure AddSysTrayIcon; 
    procedure ShowBalloonTips; 
    procedure DeleteSysTrayIcon; 
public 
end; 

var 
    MainForm-1: TForm; 

implementation 

uses 
ShellAPI...,.....,; 

procedure MainForm-1.SysTrayIconMessageHandler(var Msg: TMessage); 
begin 
    case Msg.lParam of 
    WM_MOUSEMOVE:; 
    WM_LBUTTONDOWN:; 
    WM_LBUTTONUP:; 
    WM_LBUTTONDBLCLK:; 
    WM_RBUTTONDOWN:; 
    WM_RBUTTONUP:; 
    WM_RBUTTONDBLCLK:; 
    NIN_BALLOONSHOW:; 
    NIN_BALLOONHIDE:; 
    NIN_BALLOONTIMEOUT: 
    NIN_BALLOONUSERCLICK:; 
end; 
end; 

procedure MainForm-1.AddSysTrayIcon; 
begin 
    IconData.cbSize := SizeOf(IconData); 
    IconData.Wnd := AllocateHWnd(SysTrayIconMessageHandler); 
    IconData.uID := 0; 
    IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; 
    IconData.uCallbackMessage := TRAY_CALLBACK; 
    IconData.hIcon := Application.Icon.Handle; 
    IconData.szTip := 'Software Updater is running'; 
    if not Shell_NotifyIcon(NIM_ADD, @IconData) then 
    ShowMessage('System Tray Icon cannot be created.'); 
end; 

procedure MainForm-1.DisplayBalloonTips; 
var 
    TipInfo, TipTitle: string; 
begin 
    IconData.cbSize := SizeOf(IconData); 
    IconData.uFlags := NIF_INFO; 
    if ssHelperState = UpdatesAvailable then TipInfo := 'Updates are available to the programs installed on your Computer' + ' Click to see details.'; 
    if ssHelperState = UpdatesDownloading then TipInfo := 'Updates are downloading in the background. Click to view the details.'; 
    strPLCopy(IconData.szInfo, TipInfo, SizeOf(IconData.szInfo) - 1); 
    IconData.DUMMYUNIONNAME.uTimeout := 2500; 
    if ssHelperState = UpdatesAvailable then TipTitle := 'Updates are Available...'; 
    if ssHelperState = UpdatesDownloading then TipTitle := 'Downloading the Updates...'; 
    strPLCopy(IconData.szInfoTitle, TipTitle, SizeOf(IconData.szInfoTitle) - 1); 
    IconData.dwInfoFlags := NIIF_INFO; 
    Shell_NotifyIcon(NIM_MODIFY, @IconData); 
    {Following code is for testing purpose.} 
    IconData.DUMMYUNIONNAME.uVersion := NOTIFYICON_VERSION; 
    if not Shell_NotifyIcon(NIM_SETVERSION, @IconData) then 
    ShowMessage('Setting the Version is Failed.'); 
end; 

procedure MainForm-1.DeleteSysTrayIcon; 
begin 
    DeallocateHWnd(IconData.Wnd); 
    if not Shell_NotifyIcon(NIM_DELETE, @IconData) then 
    ShowMessage('Unable to delete System Tray Icon.'); 
end; 

procedure MainForm-1.FormCreate(Sender: TObject); 
begin 
    AddSysTrayIcon; 
    ShowBalloonTips; 
end; 

procedure MainForm-1.FormDestroy(Sender: TObject); 
begin 
    DeleteSysTrayIcon; 
end; 
... 
end. 

但是,這是失敗的,我不斷收到同樣的氣球提示(第一個)一次又一次當程序運行.......

我不知道如何使用NIN_BALLOONSHOWNIN_BALLOONHIDE正確的標誌。所以,在此先感謝您的重要幫助。

回答

1

爲什麼要手動聲明所有內容?德爾福2009年已經有關於Shell_NotifyIcon() API的聲明。他們在ShellAPI單位。它聲明瞭你正在嘗試使用的所有內容,除了uVersion字段(在Delphi 2010中添加了該字段)。您沒有使用guidItemhBalloonIcon字段,所以我們不要在這裏擔心它們。 uTimeout字段存在,並且由於它與uVersion結合在一起,因此數據大小不會更改,因此,如果要使用uVersion(或者您可以定義自己的聯合並鍵入字段,則可以使用uTimeout)但這是矯枉過正)。你當然不需要重新聲明整個API。

你每次調用Shell_NotifyIcon()時間,這是很好的重用相同IconData變量,但你不清除szTipszInfoTitle領域,如果你的幫手狀態不是UpdatesAvailableUpdatesDownloading,所以托盤圖標持續顯示最後一個技巧/你設定的氣球。當你不再需要提示/氣球時,你需要清除這些字段。

NIN_BALLOONSHOWNIN_BALLOONHIDE不是標誌。他們是通知發送到您的托盤圖標的註冊HWND。要接收通知,您需要填寫WnduCallbackMessage字段並啓用NIF_MESSAGE標誌。

此外,您需要處理WM_TASKBARCREATED消息。如果因任何原因(崩潰或被用戶殺死)重新啓動資源管理器,任務欄會重新創建,因此您必須重新添加您的托盤圖標。

此外,請確保您的消息處理程序將任何未處理的窗口消息傳遞到DefWindowProc(),或者您可以鎖定系統或至少您的應用程序。

最後,Delphi 2009是Delphi的一個Unicode版本,但是有些代碼段沒有正確處理Unicode。具體來說,當使用StrPLCopy()填充szTipszInfoTitle時,您需要使用Length()而不是SizeOf()。副本以字符數表示,而不是以字節數表示。

隨着中說,嘗試更多的東西是這樣的:

unit MainForm1; 

interface 

uses 
    ..., ShellAPI; 

type 
    eHelperState = (Idle, UpdatesAvailable, UpdatesDownloading); 

    MainForm = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    TaskbarCreatedMsg: UINT; 
    IconData: NOTIFYICONDATA; 
    IconAdded: Boolean; 
    ssHelperState: eHelperState; 
    procedure SysTrayIconMessageHandler(var Message: TMessage); 
    procedure AddSysTrayIcon; 
    procedure ShowBalloonTips; 
    procedure DeleteSysTrayIcon; 
    procedures SetHelperState(NewState: eHelperState); 
    ... 
end; 

var 
    MainForm: TForm; 

implementation 

const 
    TRAY_CALLBACK = WM_USER + $7258; 
    {$IF RTLVersion < 21} 
    NOTIFYICON_VERSION_4 = 4; 
    {$IFEND} 

procedure MainForm.FormCreate(Sender: TObject); 
begin 
    TaskbarCreatedMsg := RegisterWindowMessage('TaskbarCreated'); 
    IconData.cbSize := SizeOf(IconData); 
    IconData.Wnd := AllocateHWnd(SysTrayIconMessageHandler); 
    IconData.uID := 1; 
    AddSysTrayIcon; 
end; 

procedure MainForm.FormDestroy(Sender: TObject); 
begin 
    DeleteSysTrayIcon; 
    DeallocateHWnd(IconData.Wnd); 
end; 

procedure MainForm.AddSysTrayIcon; 
begin 
    IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; 
    IconData.uCallbackMessage := TRAY_CALLBACK; 
    IconData.hIcon := Application.Icon.Handle; 
    StrLCopy(IconData.szTip, 'Software Updater is running', Length(IconData.szTip)); 

    IconAdded := Shell_NotifyIcon(NIM_ADD, @IconData); 
    if not IconAdded then 
    begin 
    ShowMessage('Unable to add System Tray Icon.'); 
    Exit; 
    end; 

    if CheckWin32Version(5, 0) then 
    begin 
    IconData.{$IF RTLVersion >= 21}uVersion{$ELSE}uTimeout{$IFEND} := NOTIFYICON_VERSION_4; 
    if not Shell_NotifyIcon(NIM_SETVERSION, @IconData) then 
     ShowMessage('Unable to set version for System Tray Icon.'); 
    end; 
end; 

procedure MainForm.DisplayBalloonTips; 
var 
    Tip, InfoText, InfoTitle: string; 
begin 
    if not IconAdded then Exit; 

    case ssHelperState of 
    UpdatesAvailable: begin 
     Tip := 'Updates are Available. Click to see details.'; 
     InfoText := 'Updates are available to the programs installed on your Computer. Click to see details.'; 
     InfoTitle := 'Updates are Available'; 
    end; 
    UpdatesDownloading: begin 
     Tip := 'Downloading Updates. Click to see details.'; 
     InfoText := 'Updates are downloading in the background. Click to see details.'; 
     InfoTitle := 'Downloading Updates'; 
    end; 
    else 
    Tip := 'Software Updater is running'; 
    end; 

    IconData.uFlags := NIF_TIP or NIF_INFO; 
    StrPLCopy(IconData.szTip, Tip, Length(IconData.szTip)); 
    StrPLCopy(IconData.szInfo, InfoText, Length(IconData.szInfo)); 
    StrPLCopy(IconData.szInfoTitle, InfoTitle, Length(IconData.szInfoTitle)); 
    IconData.uTimeout := 2500; 
    IconData.dwInfoFlags := NIIF_INFO; 

    if not Shell_NotifyIcon(NIM_MODIFY, @IconData) then 
    ShowMessage('Unable to update System Tray Icon.') 
end; 

procedure MainForm.DeleteSysTrayIcon; 
begin 
    if IconAdded then 
    begin 
    IconAdded := False; 
    if not Shell_NotifyIcon(NIM_DELETE, @IconData) then 
     ShowMessage('Unable to delete System Tray Icon.'); 
    end; 
end; 

procedures MainForm.SetHelperState(NewState: eHelperState); 
begin 
    if ssHelperState <> NewState then 
    begin 
    ssHelperState := NewState; 
    DisplayBalloonTips; 
    end; 
end; 

procedure MainForm.SysTrayIconMessageHandler(var Message: TMessage); 
begin 
    if Message.Msg = TRAY_CALLBACK then 
    begin 
    case LOWORD(Message.LParam) of 
     WM_MOUSEMOVE: begin 
     //... 
     end; 

     WM_LBUTTONDBLCLK, 
     NIN_BALLOONUSERCLICK: begin 
     // display status window... 
     end; 

     WM_CONTEXTMENU, 
     NIN_KEYSELECT, 
     NIN_SELECT: begin 
     // display popup menu at coordinates specified by Msg.WParam... 
    end; 

     NIN_BALLOONSHOW:; 
     NIN_BALLOONHIDE:; 
     NIN_BALLOONTIMEOUT:; 
    end; 
    end 
    else if (Message.Msg = TaskbarCreatedMsg) and (TaskbarCreatedMsg <> 0) then 
    begin 
    IconAdded := False; 
    AddSysTrayIcon; 
    DisplayBalloonTips; 
    end 
    else begin 
    Message.Result := DefWindowProc(IconData.Wnd, Message.Msg, Message.WParam, Message.LParam); 
    end; 
end; 

... 

end. 
+0

我知道,但我手動宣佈他們因爲'DUMMYUNIONNAME'不'的Windows 7(這臺電腦),它的ShellAPI'版本聲明是我知道的'NotifyIconData'的新結構,你能告訴我如何清除'szTip'和其他字段嗎?我現在還添加了我的代碼的所有其他部分。 – GTAVLover

+0

'DUMMYUNIONNAME'非常有用,我添加了它,因爲它可以確定系統托盤圖標的氣球的顯示持續時間........ :) – GTAVLover

+0

您不需要重新聲明整個Shell_NotifyIcon()API使用版本控制功能。儘管'uVersion'字段不存在(它在Delphi 2010中添加了),但在Delphi 2009中可以使用它。我已經更新了我的答案來證明這一點。 –