如何在不向通知区域添加图标的情况下使用Shell_NotifyIcon函数

6

MSDN关于通知和通知区域的文档在需要在通知区域中拥有一个图标才能显示通知的要求上非常清晰:

要显示通知,您必须在通知区域中拥有一个图标。在某些情况下,例如Microsoft Communicator或电池电量,该图标将已经存在。然而,在许多其他情况下,您只需要在需要显示通知时添加一个图标到通知区域。

由于我不想在通知区域中添加任何图标,因此我想也许可以“重用”一个最有可能在典型桌面上出现的现有图标。一个好的选择可能是系统时钟。

我的问题是:

  1. 如何找到/枚举系统时钟(还原时称为“日期和时间属性”)的NOTIFYICONDATA结构?
  2. 是否有更好的方法来完成这个操作(而不是添加一个图标)?

您的引用明确表示您无法做到这一点 - 我不明白您实际的问题可能是什么:“文档说我不能,但我还想怎么办?”如果您可以劫持现有的通知图标,那将是恶意软件的一个相当大的安全漏洞,您不认为吗?投票关闭为“不是一个真正的问题”。 - Ken White
1
@Ken White 感谢您的回答。我的第一个问题特别询问如何重复使用现有图标,而不是劫持它,只是发送可显示的文本消息。这应该不会涉及任何安全风险,因为将要显示的文本发送到我的自己的图标或其他图标应该得到完全相同的结果。除非您当然能够解释出不同之处。 - WinWin
1
抱歉,我有些误解你的问题;然而,根据文档的明确说明,这仍然行不通。由于文本实际上是一个字符指针,只有几种安全地在应用程序之间传递文本的方法,而通知并不包括在其中。我不理解为什么要避免添加图标,因为即使是短期目的,它们也是*设计用于使用的方式(正如你引用的文档中的最后一句话所述)。 - Ken White
2
我仍然不清楚。你现在说你可能有“几百(或几千)”个应用程序实例要使用通知气球?那有什么意义-用户怎么看或有时间阅读它们呢?我现在比以往任何时候都更加困惑... :) 但是,您仍然可以拥有一个通知应用程序,作为单个图标,并通过某种进程间通信从其他实例接收更新,并处理通知显示。 - Ken White
您可以注册一条消息(链接),并让您的卫星应用程序将其发送到您的通知应用程序。请注意,自Windows Vista起,超时已被弃用。 - ggponti
显示剩余2条评论
1个回答

10

Shell_NotifyIcon在底层使用IUserNotification。我尝试过它,并将其制成了一个实用工具。我听说有个视障系统管理员使用它来使他的脚本与屏幕阅读器兼容。它是命令行的,没有消息循环。

它是自我感知的,这意味着发送到它的通知将被排队(您可以控制它)。为了使其工作,我提供了一个IQueryContinue实现。该项目使用C++编写,是开源的,请自行使用。

以下是其核心内容:

 HRESULT NotifyUser(const NOTIFU_PARAM& params, IQueryContinue *querycontinue, IUserNotificationCallback *notifcallback)
 {
    HRESULT result = E_FAIL;

    IUserNotification *un = 0;
    IUserNotification2 *deux = 0; //French pun : "un" above stands for UserNotification but it also means 1 in French. deux means 2.

    //First try with the Vista/Windows 7 interface
    //(unless /xp flag is specified
    if (!params.mForceXP)
       result = CoCreateInstance(CLSID_UserNotification, 0, CLSCTX_ALL, IID_IUserNotification2, (void**)&deux);

    //Fall back to Windows XP
    if (!SUCCEEDED(result))
    {
       TRACE(eWARN, L"Using Windows XP interface IUserNotification\n");
       result = CoCreateInstance(CLSID_UserNotification, 0, CLSCTX_ALL, IID_IUserNotification, (void**)&un);
    }
    else
    {
       TRACE(eINFO, L"Using Vista interface IUserNotification2\n");
       un = (IUserNotification*)deux; //Rather ugly cast saves some code...
    }

    if (SUCCEEDED(result))
    {
       const std::basic_string<TCHAR> crlf_text(L"\\n");
       const std::basic_string<TCHAR> crlf(L"\n");
       std::basic_string<TCHAR> text(params.mText);
       size_t look = 0;
       size_t found;

       //Replace \n with actual CRLF pair
       while ((found = text.find(crlf_text, look)) != std::string::npos)
       {
          text.replace(found, crlf_text.size(), crlf);
          look = found+1;
       }

       result = un->SetIconInfo(params.mIcon, params.mTitle.c_str());
       result = un->SetBalloonInfo(params.mTitle.c_str(), text.c_str(), params.mType);

       //Looks like it controls what happends when the X button is
       //clicked on
       result = un->SetBalloonRetry(0, 250, 0);

       if (deux)
          result = deux->Show(querycontinue, 250, notifcallback);
       else
          result = un->Show(querycontinue, 250);

       un->Release();
    }

    return result;
 }

1
这是一个非常棒的答案。已经点赞+1了。谢谢你。如果我有多个相同应用程序的实例(彼此不知道),每个都发送通知消息,这是否意味着每个都会在系统托盘中创建自己的图标? - WinWin
2
是的,不幸的是。通知是由进程排队的,因此多个进程将各自获得它们的图标。我通过实现带有信号量的IQueryContinue来解决了这个问题。运行中的实例会检查另一个(notifu)进程是否想要显示某些内容。如果是这样,它将解除自身。用户将看到图标很快消失并再次出现。要么这样做,要么创建一个通知中心,可以对来自多个进程的消息进行排队和显示。 - ixe013

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接