如何将 NotifyIcon 添加到 ATL 进程外服务器

Posted

技术标签:

【中文标题】如何将 NotifyIcon 添加到 ATL 进程外服务器【英文标题】:How to add a NotifyIcon to an ATL out-of-process server 【发布时间】:2021-07-17 07:18:28 【问题描述】:

如何为使用 VS2019 开发的进程外 COM 服务器创建 Windows 托盘通知图标?

到目前为止,我已经尝试根据MSDN documentation 只添加一个Shell_NotifyIconA(NIM_ADD, &n);。 .但是,如果我将NOTIFYICONDATA::m_hWnd 设置为0,则此调用将被0x80004005(无效句柄)拒绝。

所以我必须指定图标消息将转到的窗口句柄,但应用程序当前没有任何窗口。它确实有一个消息泵,位于 ATL::CAtlExeModule<T>::RunMessageLoop()(这是 ATL 样板代码的一部分),但我看不到任何提及窗口句柄将消息发送到此循环的位置。

我尝试使用通过 CWindowImpl::Create 创建的仅消息窗口,但是当程序运行时,该行为是意外的。通知托盘中出现一个空白区域(图标未正确显示),鼠标或单击该空间不会导致输入消息处理程序。将出现日志消息,指示 Shell_NotifyIcon() 成功并且句柄有效,但没有进一步的日志消息。

在 VS2019 中执行此操作的正确方法是什么? (我之前在 C++Builder 中做过,它可以让你简单地添加一个表单,将其标记为主表单,并为其添加一个通知图标组件。


ATLExeModule 的代码(这是样板代码加上我的修改):

class CNotifyWnd : public CWindowImpl<CNotifyWnd>

public:
    BEGIN_MSG_MAP(CMyCustomWnd)
        MESSAGE_HANDLER(WM_USER+1, OnMsg)
    END_MSG_MAP()

    LRESULT OnMsg(UINT, WPARAM, LPARAM, BOOL&)
    
        DEBUG_LOG("Received notification");
        return 0;
    
;

static void create_notifyicon()

    auto * pw = new CNotifyWnd;
    HWND hwnd = pw->Create(HWND_MESSAGE);

    auto hInst = GetModuleHandle(NULL);
    NOTIFYICONDATAA n;
    n.cbSize = sizeof n;

    n.hIcon = LoadIcon(NULL, IDI_SHIELD);
    #pragma warning(disable : 4996)
    strcpy(n.szTip, "Tooltip string");
    n.dwInfoFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    n.uVersion = NOTIFYICON_VERSION;
    n.hWnd = hwnd;
    n.uID = 1234;
    n.uCallbackMessage = WM_USER + 1;
        
    int hr = Shell_NotifyIconA(NIM_ADD, &n);
    DEBUG_LOG("Shell_NotifyIcon = ; Icon handle , window ",
        hr, (uint64_t)n.hIcon, (uint64_t)n.hWnd);


class CMyProjectModule : public ATL::CAtlExeModuleT< CMyProjectModule >

public :
    DECLARE_LIBID(LIBID_MyProjectLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJECT, "d0d2e9f7-8578-412a-9311-77ff62291751")

    using Parent = ATL::CAtlExeModuleT< CMyProjectModule >;
    HRESULT PreMessageLoop(int nShowCmd) throw()
    
        HRESULT hr = Parent::PreMessageLoop(nShowCmd);
        create_notifyicon();
        return hr;
    
;

CMyProjectModule _AtlModule;

extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/,
                                LPTSTR /*lpCmdLine*/, int nShowCmd)

    return _AtlModule.WinMain(nShowCmd);

【问题讨论】:

你让这变得比它必须的更难。只需直接调用CreateWindow/Ex() 即可创建一个***的、无主的、隐藏的窗口,甚至可能是一个message-only window。不要将对话框用于非视觉工作。 不创建对话框,创建窗口:docs.microsoft.com/en-us/cpp/atl/… 【参考方案1】:

问题中的代码大部分是正确的,但是dwInfoFlags 应该是uFlags。进行更改后,通知图标按预期工作。

感谢建议简化问题中原始代码的方法的评论者,以及通过将父级设置为 HWND_MESSAGE 来创建“仅消息窗口”的想法。

【讨论】:

以上是关于如何将 NotifyIcon 添加到 ATL 进程外服务器的主要内容,如果未能解决你的问题,请参考以下文章

如何将 WTL 和 ATL 添加到 Visual Studio C++ Express 2008

C# 公共控件之NotifyIcon 将窗口最小化到托盘

Visual Studio:无法将ATL类添加到ATL项目中?

如何添加atl工程实现接口实现ipersistfile

我可以将 MFC 支持添加到现有 ATL COM 项目吗

【C#】WinForm中如何让窗口最小化时直接最小化到托盘,后台运行