Как добавить NotifyIcon на внепроцессный сервер ATL

Как я могу иметь значок уведомления на панели задач Windows для внепроцессного COM-сервера, разработанного с использованием VS2019?

До сих пор я пытался просто добавить один с Shell_NotifyIconA(NIM_ADD, &n); в соответствии с документация MSDN. . Однако, если я установлю 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);
}

person M.M    schedule 23.04.2021    source источник
comment
Вы делаете это сложнее, чем должно быть. Просто вызовите CreateWindow/Ex() напрямую, чтобы создать -level, не принадлежащее, скрытое окно, возможно, даже окно только для сообщений. Не используйте диалог для невизуальной работы.   -  person Remy Lebeau    schedule 23.04.2021
comment
Не создавайте диалог, создайте окно: docs.microsoft.com/en-us/cpp/atl/   -  person Simon Mourier    schedule 23.04.2021


Ответы (1)


Код в вопросе в основном правильный, однако dwInfoFlags должно быть uFlags. После внесения этого изменения значок уведомления заработал как положено.

Спасибо комментаторам, которые предложили способы упростить исходный код в вопросе, а также идею окна только для сообщений, созданного путем установки для родителя значения HWND_MESSAGE.

person M.M    schedule 27.04.2021