【问题标题】:A way to ensure that a system tray icon is removed... guaranteed一种确保删除系统托盘图标的方法......保证
【发布时间】:2010-10-02 09:11:49
【问题描述】:

有没有办法保证您的系统托盘图标被删除?

添加系统托盘图标:

Shell_NotifyIcon(NIM_ADD, &m_tnd);

删除系统托盘图标:

Shell_NotifyIcon(NIM_DELETE, &m_tnd);

我想知道:如果您的应用程序崩溃了怎么办?该图标会一直保留在系统托盘中,直到您将鼠标悬停。有没有办法保证图标会被删除,即使应用程序崩溃?由于各种原因,我宁愿不使用结构化异常处理。

我要处理的另一种情况是进程被终止,但不一定崩溃。

【问题讨论】:

    标签: c++ windows winapi system-tray


    【解决方案1】:

    大多数程序员忘记检查的另一件事是资源管理器是否重新启动/崩溃。如果应用程序处理此问题并重新创建自己的图标,那就太好了。

    只需检查消息 WM_TASKBARCREATED 并重新创建图标。

    【讨论】:

      【解决方案2】:

      您可以有一个单独的、更简单(因此可能更健壮)的程序来监控您的应用程序。该程序实际上可以启动您的程序,然后监视该过程。是的,这是一个非常丑陋的解决方案。

      【讨论】:

      • 这个外部程序也可以管理托盘图标,并且主程序使用通知来传达其状态,如果他们没有达到超时,则图标被隐藏,类似于保持活动消息。跨度>
      【解决方案3】:

      我个人会使用向量异常处理程序。是的,它基于 SEH,但您不必处理可能需要展开的所有不同堆栈。

      TerminateProcess() 必须更具破坏性。你真的无法避免这种情况;当它发生时,你的进程已经死了。没有矿石指令被处理,所以你的应用程序中有什么代码并不重要。

      外部应用程序并没有真正的帮助,不是吗?它也可能崩溃,或者被杀死。

      【讨论】:

        【解决方案4】:

        嗯,您始终可以让外部监视器进程调用带有 WM_PAINT 消息的 SendMessage 到系统托盘窗口(您必须根据窗口的类执行此操作)。这应该会删除不再有效的图标。

        【讨论】:

          【解决方案5】:

          在应用程序崩溃的情况下,有很多方法可以确保在 C++ 中调用Shell_NotifyIcon(NIM_DELETE, &m_tnd);;在您正在使用的 NOTIFYICONDATA 上使用 RAII 包装器将完成工作,例如:

          struct NID
          {
              NID() : icon_data() { icon_data.cbSize = sizeof(icon_data); }
              ~NID() { Shell_NotifyIcon(NIM_DELETE, &icon_data); }
              void Show(HWND w) { icon_data.hWnd = w; Shell_NotifyIcon(NIM_ADD, &icon_data); }
              NOTIFYICONDATA icon_data;
          };
          

          这是包装器的简化版本,但它将说明主要思想:如果您在静态存储中创建NID 的实例,它将在WinMainmain 调用之前初始化,其析构函数将是调用程序清理,即使此清理是由于异常终止。

          所以,我们可以这样使用包裹在struct NID 中的NOTIFYICONDATA 资源:

          NID nid; // <--- automatic storage duration, cleared after WinMain return
                   // even if it returns normal or abnormally
          
          int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
          {
              try
              {
                  // GetMessage(&message, NULL, 0, 0) loop ...
                  // ...
                  // use nid.icon_data as you please
              }
              catch (...)
              {
                  // something bad happened...
              }
          
              return 0;
          }
          

          上例在程序终止时(异常后或关闭程序后)调用~NID(),析构函数将调用Shell_NotifyIcon(NIM_DELETE, &amp;icon_data);,图标从通知区删除;此代码涵盖了正常终止和异常终止,您可以在this good answer 中从NPE 阅读有关此主题的更多信息:

          对于杀死进程的情况,没有简单的方法可以做到这一点。

          我已经测试过 std::atexitstd::at_quick_exit 函数在通过任务管理器杀死程序后不会被调用,所以我猜你必须挂钩终止调用......它似乎是一项相当复杂的任务,但在this answer 中的BSH 中进行了解释:

          当一个进程终止(未关闭)时,除非你开始做一些挂钩,否则实际上什么都做不了,无论是通过在任务管理器进程中挂钩 TerminateProcessNtTerminateProcess

          希望它有所帮助(虽然是 6 年后的答案,哈哈)

          【讨论】:

            【解决方案6】:

            您必须在以某种方式崩溃时处理应用程序退出,否则图标不会消失。

            看看这个是否有帮助:http://www.codeproject.com/KB/shell/ashsystray.aspx

            【讨论】:

              【解决方案7】:

              您可以使用SetUnhandledExceptionFilter 来捕获崩溃。我通常使用它来创建崩溃转储文件,以便可以调试崩溃,但没有理由不能进行一些简单的清理,例如删除托盘图标。

              【讨论】:

                【解决方案8】:

                没有直接解决您的问题,但这对我来说是一个非常有用的解决方法:

                我想避免混淆系统托盘状态。所以对我来说,在启动时“刷新”通知托盘就足够了。这比我最初想象的要棘手,但是following 演示了一个 SendMessage 解决方案,它模拟了用户鼠标悬停清理,不需要实际移动用户的光标。

                请注意,在 Windows 7 机器上,名称 Notification Area 应替换为 User Promoted Notification Area

                【讨论】:

                • 如果用户在 win XP 或更高版本中隐藏了带有“箭头”的图标,则会中断。你打算做什么 - 以编程方式尝试单击箭头,然后将鼠标移到弹出窗口中的每个图标上 - 如果弹出窗口消失,重复单击箭头?那么当新版本的 Windows 出现时呢?这个“箭头”功能是在 XP 中引入的,在 Vista 中进行了更改,据我所知,Windows 8 中将再次更改。您必须为每个操作系统进行硬编码,您的应用程序将在新版本上中断。这是一个非常不支持的方法!
                猜你喜欢
                • 1970-01-01
                • 2011-11-11
                • 1970-01-01
                • 2016-08-22
                • 1970-01-01
                • 2011-04-16
                • 2011-06-25
                • 2023-03-06
                • 1970-01-01
                相关资源
                最近更新 更多