如何将 NotifyIcon 添加到 ATL 进程外服务器
How to add a NotifyIcon to an ATL out-of-process server
如何为使用 VS2019 开发的进程外 COM 服务器提供 Windows 托盘通知图标?
到目前为止,我已经尝试根据 MSDN documentation 添加一个 Shell_NotifyIconA(NIM_ADD, &n);
。 .However if I set the NOTIFYICONDATA::m_hWnd
to 0
then this call is rejected with 0x80004005
(Invalid handle).
所以我必须指定一个 window 图标的消息将转到的句柄,但应用程序当前没有任何 windows。它确实有一个位于 ATL::CAtlExeModule<T>::RunMessageLoop()
的消息泵(这是 ATL 样板代码的一部分),但我看不到任何关于 window 句柄将消息发送到此循环的位置的提及。
我试过使用 CWindowImpl::Create 创建的仅消息 Window,但是当程序运行时,行为是意外的。通知托盘中出现空白 space(图标未正确显示),鼠标悬停或单击 space 不会导致输入消息处理程序。日志消息出现,表明 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);
}
问题中的代码大部分是正确的,但是dwInfoFlags
应该是uFlags
。进行更改后,通知图标按预期工作。
感谢评论者提出了简化问题中原始代码的方法,以及通过将父级设置为 HWND_MESSAGE
来创建“仅消息 window” 的想法。
如何为使用 VS2019 开发的进程外 COM 服务器提供 Windows 托盘通知图标?
到目前为止,我已经尝试根据 MSDN documentation 添加一个 Shell_NotifyIconA(NIM_ADD, &n);
。 .However if I set the NOTIFYICONDATA::m_hWnd
to 0
then this call is rejected with 0x80004005
(Invalid handle).
所以我必须指定一个 window 图标的消息将转到的句柄,但应用程序当前没有任何 windows。它确实有一个位于 ATL::CAtlExeModule<T>::RunMessageLoop()
的消息泵(这是 ATL 样板代码的一部分),但我看不到任何关于 window 句柄将消息发送到此循环的位置的提及。
我试过使用 CWindowImpl::Create 创建的仅消息 Window,但是当程序运行时,行为是意外的。通知托盘中出现空白 space(图标未正确显示),鼠标悬停或单击 space 不会导致输入消息处理程序。日志消息出现,表明 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);
}
问题中的代码大部分是正确的,但是dwInfoFlags
应该是uFlags
。进行更改后,通知图标按预期工作。
感谢评论者提出了简化问题中原始代码的方法,以及通过将父级设置为 HWND_MESSAGE
来创建“仅消息 window” 的想法。