65.9K
CodeProject 正在变化。 阅读更多。
Home

显示Notify Icon的气球提示

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.54/5 (22投票s)

2002年3月29日

3分钟阅读

viewsIcon

461739

downloadIcon

9290

显示通知图标的气球工具提示。

Sample Image - NotifyBalloon.png

引言

本文介绍了一种使用FCL的NotifyIcon类创建的通知图标显示气球工具提示的方法。通知图标的这个相对较新的功能不受NotifyIcon类的支持,并且如果不进行一些创造性的编码,将此功能添加到我自己的代码中并不明显。这就是我在这里介绍它的原因。另一个原因是,我希望有人可以告诉我我做错了,并且有一个更“正确”的解决方案。

本文还用作平台调用工具使用的示例。它展示了如何创建Win32 API函数所需的数据结构,以及如何将该结构传递给API函数。

我完成这项任务的方式包括以下两个步骤

  1. 1. 使用Win32 API获取隐藏的FCL创建的通知消息窗口的句柄。
  2. 2. 使用Win32 API将“气球提示”消息发送到通知图标。

一点背景知识

通知图标通过将窗口消息发送到创建通知图标时提供的窗口来与其父应用程序通信。FCL NotifyIcon类不提供有关此窗口的任何信息(在更高级别的抽象上工作的一个缺点)。FCL为NotifyIcon创建一个隐藏窗口,并将发送给它的窗口消息转换为.NET可使用的事件。

为了使用Win32 API与通知图标通信,您必须获取接收消息的窗口的Win32窗口句柄,以及图标的数字ID。

解决方案

使用Spy++我确定了FCL创建的隐藏窗口的类名。然后,我使用Win32 API查找该窗口的句柄(仅查看我的线程拥有的窗口)。ID是通过反复试验确定的!我发现,如果你的应用程序创建了一个通知图标,则ID将为1(而不是0)。一旦我有了窗口句柄,我就会调用Shell_NotifyIcon()向它发送“气球”消息。这部分的唯一技巧是定义发送到此API函数所需的数据结构。

注意事项

此解决方案基本上是一种hack,原因如下。以下是我所做的可能并不总是正确的假设。

  1. 我假设调用Win32 API函数GetCurrentThreadId()会返回我正确的线程ID。
  2. 我假设FCL创建的窗口类的名称是固定的。当然,将来可能并非如此。
  3. 我假设对于第一个创建的图标,该图标的ID为1。
  4. 我发布此代码的部分原因是,也许可以获得有关如何减少所做假设数量的一些反馈。所以,如果你有什么想法,请告诉我。

这是显示气球的NotifyIcon类的代码。

public class NotifyIcon
{
    [StructLayout(LayoutKind.Sequential)]
        public struct NotifyIconData
    {
        public System.UInt32 cbSize; // DWORD
        public System.IntPtr hWnd; // HWND
        public System.UInt32 uID; // UINT
        public NotifyFlags uFlags; // UINT
        public System.UInt32 uCallbackMessage; // UINT
        public System.IntPtr hIcon; // HICON
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
        public System.String szTip; // char[128]
        public System.UInt32 dwState; // DWORD
        public System.UInt32 dwStateMask; // DWORD
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
        public System.String szInfo; // char[256]
        public System.UInt32 uTimeoutOrVersion; // UINT
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
        public System.String szInfoTitle; // char[64]
        public System.UInt32 dwInfoFlags; // DWORD
        //GUID guidItem; > IE 6
    }

    public enum NotifyCommand {Add = 0, Modify = 1, Delete = 2, SetFocus = 3, 
                               SetVersion = 4}
    public enum NotifyFlags {Message = 1, Icon = 2, Tip = 4, State = 8, Info = 16, 
                             Guid = 32}

    [DllImport("shell32.Dll")]
    public static extern System.Int32 Shell_NotifyIcon(NotifyCommand cmd, 
                                                       ref NotifyIconData data);

    [DllImport("Kernel32.Dll")]
    public static extern System.UInt32 GetCurrentThreadId();

    public delegate System.Int32 EnumThreadWndProc(System.IntPtr hWnd, 
                                                   System.UInt32 lParam);

    [DllImport("user32.Dll")]
    public static extern System.Int32 EnumThreadWindows(System.UInt32 threadId, 
                                        EnumThreadWndProc callback, 
                                        System.UInt32 param);

    [DllImport("user32.Dll")]
    public static extern System.Int32 GetClassName(System.IntPtr hWnd, 
                                                  System.Text.StringBuilder className,
                                                  System.Int32 maxCount);

    private System.IntPtr m_notifyWindow;
    private bool m_foundNotifyWindow;

    // Win32 Callback Function
    private System.Int32 FindNotifyWindowCallback(System.IntPtr hWnd, 
                                                  System.UInt32 lParam)
    {
        System.Text.StringBuilder buffer = new System.Text.StringBuilder(256);
        GetClassName(hWnd, buffer, buffer.Capacity);

		// but what if this changes?  - anybody got a better idea?
        if(buffer.ToString() == "WindowsForms10.Window.0.app1") 
        {
            m_notifyWindow = hWnd;
            m_foundNotifyWindow = true;
            return 0; // stop searching
        }
        return 1;
    }

    public void ShowBalloon(uint iconId, string title, string text, uint timeout)
    {
        // find notify window
        uint threadId = GetCurrentThreadId();
        EnumThreadWndProc cb = new EnumThreadWndProc(FindNotifyWindowCallback);
        m_foundNotifyWindow = false;
        EnumThreadWindows(threadId, cb, 0);
        if(m_foundNotifyWindow)
        {
            // show the balloon
            NotifyIconData data = new NotifyIconData();
            data.cbSize = (System.UInt32)
                          System.Runtime.InteropServices.Marshal.SizeOf(
                                                                typeof(NotifyIconData));
            data.hWnd = m_notifyWindow;
            data.uID = iconId;
            data.uFlags = NotifyFlags.Info;
            data.uTimeoutOrVersion = 15000;
            data.szInfo = text;
            data.szInfoTitle = title;
            Shell_NotifyIcon(NotifyCommand.Modify, ref data);
        }
    }
}

这是显示该类用法的代码。

private void OnShowBalloon(object sender, System.EventArgs e)
{
    NotifyBalloonDemo.NotifyIcon notifyIcon = new NotifyBalloonDemo.NotifyIcon();
    notifyIcon.ShowBalloon(1, "My Title", "My Text", 15000);
}

所以,大家快来告诉我如何正确地做到这一点。

© . All rights reserved.