创建系统托盘右键菜单 C++

问题描述

我正在尝试在虚幻引擎 4 中创建 Systemtray 图标。 我不是 C++ 专家。到目前为止,这是我从互联网上剥取的代码

#define NOTIFICATION_TRAY_ICON_MSG (WM_USER + 0x100)
AddTrayIcon(hwnd,1,NOTIFICATION_TRAY_ICON_MSG,0);
void AddTrayIcon(HWND hWnd,UINT uID,UINT uCallbackMsg,UINT uIcon) {

    //CREATE SYSTEN TRAY ICON.---------------------------------------------------------------------

    NOTIFYICONDATA  nid;

    nid.hWnd = hWnd;

    nid.uID = uID;

    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;

    nid.uCallbackMessage = uCallbackMsg;
    FString ThePath = FPaths::ConvertRelativePathToFull(FPaths::RootDir()) ;
    FString GameName = FApp::GetName();
    
    ThePath.Append(GameName);
    ThePath.Append(".exe");
    GEngine->AddOnScreenDebugMessage(-1,12.f,FColor::White,ThePath);
    WORD id = 0;
    nid.hIcon = ExtractAssociatedIcon(nullptr,ThePath.GetChararray().GetData(),&id);
    //LPCWSTR iconfile = "C:/Temp/icon.ico";
    //ExtractIconEx(iconfile,NULL,&(nid.hIcon),1);

    //strcpy(nid.szTip,"Tool Tip");

    //SEND MESSAGE TO SYstem TRAY TO ADD ICON.--------------------------------------------

     Shell_NotifyIcon(NIM_ADD,&nid);
    

}

LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WParaM wParam,LParaM lParam)
{
    switch (message)
    {

    case NOTIFICATION_TRAY_ICON_MSG:
    {
        // This is a message that originated with the
        // Notification Tray Icon. The lParam tells use exactly which event
        // it is.
        switch (lParam)
        {
        case WM_LBUTTONDBLCLK:
        {
            const int IDM_EXIT = 100;
            POINT pt;
            GetCursorPos(&pt);
            HMENU hmenu = CreatePopupMenu();
            InsertMenu(hmenu,MF_BYPOSITION | MF_STRING,IDM_EXIT,L"Exit");
            TrackPopupMenu(hmenu,TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BottOMALIGN,pt.x,pt.y,GetActiveWindow(),NULL);
            break;
        }
        }
    }
    }
    return 0;
}

图标已创建,但我无法右键单击并从中获取弹出菜单 我也无法弄清楚 LRESULT CALLBACK WndProc() 函数是如何被触发的。

谁能帮忙谢谢

解决方法

您没有正确初始化 NOTIFYICONDATA。具体而言,您没有设置其 cbSizeuVersion 成员,这会直接影响 Shell_NotifyIcon() 通过您分配给 uCallbackMessage 成员的消息与您的应用程序交互的方式。以下 MSDN 文档对此进行了详细讨论:

Notifications and the Notification Area: Define the NOTIFYICONDATA Version

Shell_NotifyIcon: Remarks

NOTIFYICONDATA structure

此外,您说要在右键单击时显示弹出菜单,但您的 WndProc() 正在寻找左键双击 .

但更重要的是,您需要将您的 WndProc() 与您提供给 HWNDShell_NotifyIcon() 相关联,这样您才能真正接收到您的 NOTIFICATION_TRAY_ICON_MSG 消息。由于您尝试使用的 HWND 不是您的创建者,UE4 是创建者,因此您必须subclass the window,例如:

#define NOTIFICATION_TRAY_ICON_MSG (WM_USER + 0x100)

void AddTrayIcon(HWND hWnd,UINT uID,UINT uCallbackMsg,UINT uIcon);
void RemoveTrayIcon(HWND hWnd,UINT uID);
LRESULT CALLBACK SubclassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);

...

HWND hwnd = ...; // GetActiveWindow(),etc
if (SetWindowSubclass(hwnd,&SubclassProc,1,0))
    AddTrayIcon(hwnd,NOTIFICATION_TRAY_ICON_MSG,0);

...

void AddTrayIcon(HWND hWnd,UINT uIcon) {

    //CREATE SYSTEM TRAY ICON.---------------------------------------------------------------------

    NOTIFYICONDATA nid = {};
    nid.cbSize = sizeof(nid);
    nid.hWnd = hWnd;
    nid.uID = uID;
    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    nid.uCallbackMessage = uCallbackMsg;
    nid.uVersion = NOTIFYICON_VERSION_4;

    FString ThePath = FPaths::ConvertRelativePathToFull(FPaths::RootDir()) ;
    FString GameName = FApp::GetName();
    
    ThePath.Append(GameName);
    ThePath.Append(".exe");
    GEngine->AddOnScreenDebugMessage(-1,12.f,FColor::White,ThePath);

    WORD id = 0;
    nid.hIcon = ExtractAssociatedIcon(nullptr,ThePath.GetCharArray().GetData(),&id);
    //LPCWSTR iconfile = L"C:/Temp/icon.ico";
    //ExtractIconEx(iconfile,NULL,&(nid.hIcon),1);

    //strcpy(nid.szTip,"Tool Tip");

    //SEND MESSAGE TO SYSTEM TRAY TO ADD ICON.--------------------------------------------

    if (Shell_NotifyIcon(NIM_ADD,&nid))
        Shell_NotifyIcon(NIM_SETVERSION,&nid);
}

void RemoveTrayIcon(HWND hWnd,UINT uID) {

    //REMOVE SYSTEM TRAY ICON.---------------------------------------------------------------------

    NOTIFYICONDATA nid = {};
    nid.cbSize = sizeof(nid);
    nid.hWnd = hWnd;
    nid.uID = uID;

    //SEND MESSAGE TO SYSTEM TRAY TO REMOVE ICON.--------------------------------------------

    Shell_NotifyIcon(NIM_DELETE,&nid);
}

LRESULT CALLBACK SubclassProc(HWND hWnd,DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
        case WM_NCDESTROY:
            RemoveTrayIcon(hWnd,1);
            RemoveWindowSubclass(hWnd,uIdSubclass);
            break;

        case NOTIFICATION_TRAY_ICON_MSG:
        {
            // This is a message that originated with the
            // Notification Tray Icon. The lParam tells use exactly which event
            // it is.
            switch (LOWORD(lParam))
            {
                case NIN_SELECT:
                case NIN_KEYSELECT:
                case WM_CONTEXTMENU:
                {
                    const int IDM_EXIT = 100;

                    POINT pt;
                    GetCursorPos(&pt);

                    HMENU hmenu = CreatePopupMenu();
                    InsertMenu(hmenu,MF_BYPOSITION | MF_STRING,IDM_EXIT,L"Exit");

                    SetForegroundWindow(hWnd);

                    int cmd = TrackPopupMenu(hmenu,TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_NONOTIFY | TPM_RETURNCMD,pt.x,pt.y,hWnd,NULL);

                    PostMessage(hWnd,WM_NULL,0);

                    if (cmd == IDM_EXIT)
                    {
                        ...
                    }

                    break;
                }
            }

            return 0;
        }
    }

    return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}

话虽如此,您确实应该创建自己的窗口来处理与系统托盘的交互。您可以直接调用 CreateWindowEx() 来创建一个隐藏的 message-only window,然后将您图标的 WndProc() 与该窗口相关联。您不需要使用 UE4 窗口:

LRESULT CALLBACK SysTrayWndProc(HWND hWnd,LPARAM lParam);

...

LPCTSTR SysTrayWndClass = TEXT("MySysTrayWnd");

HINSTANCE hInst = ...; // GetModuleHandle(NULL),etc

WNDCLASS wc = {};
wc.lpfnWndProc = &SysTrayWndProc;
wc.hInstance = hInst;
wc.lpszClassName = SysTrayWndClass;
RegisterClass(&wc);

HWND hSysTrayIconWnd = CreateWindowEx(0,SysTrayWndClass,HWND_MESSAGE,hInst,NULL);
if (hSysTrayIconWnd) {
    AddTrayIcon(hSysTrayIconWnd,0);
}
...
if (hSysTrayIconWnd) {
    RemoveTrayIcon(hSysTrayIconWnd,1);
    DestroyWindow(hSysTrayIconWnd);
}

...

LRESULT CALLBACK SysTrayWndProc(HWND hWnd,LPARAM lParam)
{
    switch (uMsg)
    {
        case NOTIFICATION_TRAY_ICON_MSG:
        {
            // This is a message that originated with the
            // Notification Tray Icon. The lParam tells use exactly which event
            // it is.
            switch (LOWORD(lParam))
            {
                case NIN_SELECT:
                case NIN_KEYSELECT:
                case WM_CONTEXTMENU:
                {
                    const int IDM_EXIT = 100;

                    POINT pt;
                    GetCursorPos(&pt);

                    HMENU hmenu = CreatePopupMenu();
                    InsertMenu(hmenu,TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN,0);

                    break;
                }
            }

            return 0;
        }

       case WM_COMMAND:
           if (lParam == 0 && LOWORD(wParam) == IDM_EXIT)
           {
               ...
           }
           break;
    }

    return DefWindowProc(hWnd,lParam);
}