为什么我的具有自定义类的 Windows Systray 应用程序无需调用 DispatchMessage 即可工作?

问题描述 投票:0回答:1

这是我第一次在这里发帖,所以如果我的问题不清楚或结构不合理,我深表歉意。我会尽力清楚地解释我的问题。

我目前正在学习 C++ 的 Windows 应用程序编程,并且正在尝试创建系统托盘应用程序。我已经成功创建了一个系统托盘图标,右键单击该图标时,会显示一个消息框并退出应用程序。

但是,在那之后,我尝试将系统托盘功能封装在自定义类中,并遇到了一个我不完全理解的有趣行为。

我的代码: 我已将功能分解为三个文件:

主.cpp

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
  

    std::unique_ptr<windowsSystray> driver = std::make_unique<windowsSystray>(hInstance, nullptr, L"Tree App");

    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        // Normally, we'd call DispatchMessage here, but this works without it.       
    }


    if (hAppMutex) CloseHandle(hAppMutex);
    return static_cast<int>(msg.wParam);
}

windowsSystray.h

class windowsSystray :
    public SystrayDriver
{
public:
    windowsSystray(HINSTANCE hInstance, HWND hWnd, const std::wstring& tooltipText);
    ~windowsSystray();


    

private:  


    void registerWindowClass();
    void createSystrayWindow();
    void unregisterWindowClass();
    
    void addIcon() override;
    void removeIcon() override;
    
    static LRESULT CALLBACK HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



    static NOTIFYICONDATA nidApp;
    static HMENU hPopMenu;
    
    
    HINSTANCE hInst;
    HWND hWnd;
    std::wstring tooltipText;
};


windowsSystray.cpp

#include "windowsSystray.h"

NOTIFYICONDATA windowsSystray::nidApp = { 0 };


windowsSystray::windowsSystray(HINSTANCE hInstance, HWND hWnd, const std::wstring& tooltipText)
    : hInst(hInstance), hWnd(hWnd), tooltipText(tooltipText) {

    registerWindowClass();
    
    createSystrayWindow();
   
    addIcon();


}
windowsSystray::~windowsSystray() {
    
    removeIcon();
    unregisterWindowClass();
}


void windowsSystray::registerWindowClass() {
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = HandleMessage;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SYSTRAY_ICON));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = MAKEINTRESOURCE(IDM_SYSTRAY);
    wc.lpszClassName = L"TreeSystrayClass";
    wc.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SYSTRAY_ICON));
    RegisterClassEx(&wc);
}


void windowsSystray::createSystrayWindow() {
    
    HICON hMainIcon = LoadIcon(hInst, (LPCTSTR)MAKEINTRESOURCE(IDI_SYSTRAY_ICON));
    hWnd = hWnd;
    hWnd = CreateWindow(L"TreeSystrayClass", L"treeCollector", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);

    if (!hWnd) return;

    nidApp.cbSize = sizeof(NOTIFYICONDATA);                         // sizeof the struct in bytes 
    nidApp.hWnd = (HWND)hWnd;                                       //handle of the window which will process this app. messages 
    nidApp.uID = IDI_SYSTRAY_ICON;                                  //ID of the icon that willl appear in the system tray 
    nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;               //ORing of all the flags 
    nidApp.hIcon = hMainIcon;                                       // handle of the Icon to be displayed, obtained from LoadIcon 
    nidApp.uCallbackMessage = WM_SYSTRAY;
}

void windowsSystray::unregisterWindowClass() {
    UnregisterClass(L"TreeSystrayClass", hInst);
}

void windowsSystray::addIcon() {
    Shell_NotifyIcon(NIM_ADD, &nidApp);
}

void windowsSystray::removeIcon() {
    Shell_NotifyIcon(NIM_DELETE, &nidApp);
}

LRESULT CALLBACK windowsSystray::HandleMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {


    int wmId, wmEvent;
    //POINT lpClickPoint;

    switch (message)
    {

    case WM_SYSTRAY:
        switch (LOWORD(lParam))
        {
        case WM_RBUTTONDOWN:
            MessageBox(hwnd, TEXT("HERE1"), TEXT("Tree"), MB_OK);
            Shell_NotifyIcon(NIM_DELETE, &nidApp);
            DestroyWindow(hwnd);
        }
        break;
    case WM_COMMAND:
        wmId = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        /*case IDC_MENU_ABOUT:
            MessageBox(hwnd, TEXT("HERE2"), TEXT("Tree"), MB_OK);
            break;*/
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

** 我不明白的问题:**

当我将 systray 功能封装在 windowsSystray 类中并运行应用程序时,它会创建图标,然后停留在 GetMessage() 循环中,即使我从未在主循环中调用 DispatchMessage() ,它也会按预期工作。

我的问题: 为什么 GetMessage() 循环在不调用 DispatchMessage() 的情况下仍然有效?当我没有显式调用 DispatchMessage() 时,Windows 会自动调用消息处理程序吗?

使用自定义类来管理系统托盘时是否会出现这种行为?或者我在这里遗漏了一些基本的东西?

任何见解或澄清将不胜感激!

这是测试系统托盘功能的第二个版本,只是尝试将 winAPI 与 POO 混合。第一个没有上课,工作也一样。 运行时,即使 getmessage while 内没有任何内容,也不会消耗内存。

c++ windows winapi systray
1个回答
0
投票

根据 GetMessage 文档:

https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage

从调用线程的消息队列中检索消息。 该函数会分派传入的已发送消息,直到发布的消息可供检索。

...

在此调用期间, 系统将传递挂起的非排队消息,即使用 SendMessage、SendMessageCallback、SendMessageTimeout 或 SendNotifyMessage 函数发送到调用线程拥有的窗口的消息。然后,与指定过滤器匹配的第一个排队消息是检索到。系统还可以处理内部事件。

© www.soinside.com 2019 - 2024. All rights reserved.