【问题标题】:System Tray Context Menu Blank系统托盘上下文菜单空白
【发布时间】:2017-11-10 13:09:56
【问题描述】:

我正在尝试创建一个没有可见窗口的应用程序,只是一个托盘图标。我试图在这里拼凑各种教程和答案,但没有比这更进一步。右键单击时会出现上下文菜单,但是完全空白。我也不确定一旦我开始工作,我将如何检测我点击的内容。

最终目标是能够通过单击上下文菜单中的两个选项之一来切换 DNS 服务器。

#include <Windows.h>
#include <shellapi.h>
#include <tchar.h>
#include <WinUser.h>


HINSTANCE gInstance = NULL;

LRESULT CALLBACK pWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wx;
    HWND hWnd;
    ZeroMemory(&wx, sizeof(WNDCLASSEX));

    wx.cbSize = sizeof(WNDCLASSEX);
    wx.lpfnWndProc = pWndProc;
    wx.hInstance = hInstance;
    wx.lpszClassName = (LPCWSTR)"DNSChanger";
    RegisterClassEx(&wx);
    CreateWindowEx(0, (LPCWSTR)"DNSChanger", (LPCWSTR)"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    gInstance = hInstance;

    MSG stMsg;
    while (GetMessage(&stMsg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&stMsg);
        DispatchMessage(&stMsg);
    }

    return 0;
}

LRESULT CALLBACK pWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    NOTIFYICONDATA niData;
    ZeroMemory(&niData, sizeof(NOTIFYICONDATA));

    switch (uMsg)
    {
        case WM_CREATE:
        {
            niData.cbSize = sizeof(NOTIFYICONDATA);
            niData.uID = 1;
            niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
            niData.hIcon = LoadIcon(gInstance, MAKEINTRESOURCE(IDI_SHIELD));
            niData.hWnd = hWnd;
            niData.uCallbackMessage = WM_USER + 1;

            Shell_NotifyIcon(NIM_ADD, &niData);
        }
        return 0;

        case WM_DESTROY:
        {
            niData.hWnd = hWnd;
            Shell_NotifyIcon(NIM_DELETE, &niData);
        }
        return 0;

        case WM_USER + 1:
        {
            switch (LOWORD(lParam))
            {
                case WM_RBUTTONUP:
                {
                    POINT lpClickPoint;
                    HMENU hPopMenu;

                    UINT uFlag = MF_BYPOSITION | MF_UNCHECKED | MF_STRING;
                    GetCursorPos(&lpClickPoint);
                    hPopMenu = CreatePopupMenu();
                    InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, WM_USER + 1, _T("Exit"));
                    SetForegroundWindow(hWnd);
                    TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL);
                }
            }
        }
    }
}

【问题讨论】:

    标签: c++ windows system-tray


    【解决方案1】:

    不要像(LPCWSTR)"DNSChanger" 这样使用演员表。这只会隐藏编译器错误。您的程序能够正常工作的唯一原因是,这个错误在 2 个不同的地方重复出现,并且被取消了。

    你的意思是写L"DNSChanger"。

    窗口过程必须返回DefWindowProc(hWnd, uMsg, wParam, lParam);

    如果您想关闭应用程序,则必须在 WM_DESTROY 中包含 PostQuitMessage(0);。

    定义要在菜单中使用的新常量

    const int IDM_EXIT = 100;
    ...
    InsertMenu(hmenu, 0, MF_BYPOSITION | MF_STRING, IDM_EXIT, L"Exit");
    

    菜单将发送 IDM_EXIT 命令作为WM_COMMAND 消息的一部分。以下是一些建议的更改:

    HINSTANCE gInstance = NULL;
    const int IDM_EXIT = 100;
    LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        static NOTIFYICONDATA niData = { sizeof(NOTIFYICONDATA) };
    
        switch(uMsg)
        {
        case WM_CREATE:
        {
            niData.uID = 1;
            niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
            niData.hIcon = LoadIcon(gInstance, IDI_SHIELD);
            niData.hWnd = hWnd;
            niData.uCallbackMessage = WM_USER + 1;
            Shell_NotifyIcon(NIM_ADD, &niData);
            return 0;
        }
    
        case WM_DESTROY:
        {
            niData.hWnd = hWnd;
            Shell_NotifyIcon(NIM_DELETE, &niData);
            PostQuitMessage(0);
            return 0;
        }
    
        case WM_COMMAND:
        {
            if(LOWORD(wParam) == IDM_EXIT)
                PostQuitMessage(0);
            break;
        }
    
        case WM_USER + 1: 
        {
            WORD cmd = LOWORD(lParam);
            if (cmd == WM_RBUTTONUP || cmd == WM_LBUTTONUP)
            {
                POINT pt;
                GetCursorPos(&pt);
                HMENU hmenu = CreatePopupMenu();
                InsertMenu(hmenu, 0, MF_BYPOSITION | MF_STRING, IDM_EXIT, L"Exit");
                TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, pt.x, pt.y, 0, hWnd, NULL);
            }
            break;
        }
    
        }
    
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
    {
        gInstance = hInstance;
        WNDCLASSEX wx = { sizeof(WNDCLASSEX) };
        wx.lpfnWndProc = WndProc;
        wx.hInstance = hInstance;
        wx.lpszClassName = L"DNSChanger";
        RegisterClassEx(&wx);
    
        CreateWindowEx(0, L"DNSChanger", L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    
        MSG msg;
        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return 0;
    }
    

    【讨论】:

    • 谢谢!已经解决了,我现在有了退出选项,应该可以从这里完成剩下的事情了!但是图标消失了...之前我在任务栏中显示盾牌图标但现在它只是空白,不确定哪些更改可能会影响...
    • 我还将MAKEINTRESOURCE(IDI_SHIELD) 更改为IDI_SHIELD,因为IDI_SHIELD 在"WinUser.h" 中定义为MAKEINTRESOURCE(32518),可能之前的错误ID 会导致显示错误。您可能需要重新启动计算机以清除图标缓存。无论如何,您都想提供自己的图标。
    • 不知道为什么图标消失了,但添加我自己的图标后没有任何问题。程序现在已经完成,感谢您的帮助:) 让我感到困惑的一件事是,从调试更改为发布意味着我必须从所有字符串中删除前导 'L' 才能编译...?
    • 在“项目属性”下,您已将“字符集”设置为“Unicode”,但对于 ReleaseMode,它显示“未设置”。建议将它们都设置为 Unicode
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多