乐者为王

Do one thing, and do it well.

使用Win32实现系统托盘程序

废话不多说,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <windows.h>
#include "resource.h"

static LPCTSTR szAppName = TEXT("TrayHelper");
static LPCTSTR szCaption = TEXT("TrayIcon");

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HMENU hMenu;
    static UINT WM_TASKBARCREATED;

    POINT point;
    HINSTANCE hInstance;
    NOTIFYICONDATA nid;

    switch (message)
    {
    case WM_CREATE:
        // 不要修改TaskbarCreated字符串,这是系统任务栏自定义的消息
        WM_TASKBARCREATED = RegisterWindowMessage(TEXT("TaskbarCreated"));

        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_TRAYMENU));
        hMenu = GetSubMenu(hMenu, 0);

        nid.cbSize = sizeof(nid);
        nid.hWnd = hWnd;
        nid.uID = IDI_PIRAMIDE;
        nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
        nid.uCallbackMessage = WM_USER;
        nid.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PIRAMIDE));
        strcpy(nid.szTip, szAppName);
        Shell_NotifyIcon(NIM_ADD, &nid);
        break;

    case WM_USER:
        if (lParam == WM_RBUTTONDOWN)
        {
            GetCursorPos(&point);
            // 处理当用户按下ESCAPE键或者在菜单之外单击鼠标时菜单不会消失的情况
            SetForegroundWindow(hWnd);
            TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y, 0, hWnd, NULL);
        }
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDM_TRAYSETTINGS:
            MessageBox(hWnd, TEXT("Settings not yet implemented!"),
                             szAppName, MB_ICONEXCLAMATION | MB_OK);
            return 0;

        case IDM_TRAYHELP:
            MessageBox(hWnd, TEXT("Help not yet implemented!"),
                             szAppName, MB_ICONEXCLAMATION | MB_OK);
            return 0;

        case IDM_TRAYABOUT:
            MessageBox(hWnd, TEXT("About not yet implemented!"),
                             szAppName, MB_ICONEXCLAMATION | MB_OK);
            return 0;

        case IDM_TRAYEXIT:
            SendMessage(hWnd, WM_CLOSE, 0, 0);
            return 0;
        }
        break;

    case WM_DESTROY:
        // 处理点击Exit菜单退出后图标仍在托盘区显示,要把鼠标在图标上面过一下才会消失的情况
        nid.uID = IDI_PIRAMIDE;
        nid.hWnd = hWnd;
        Shell_NotifyIcon(NIM_DELETE, &nid);
        PostQuitMessage(0);
        break;

    default:
        /*
         * 防止当Explorer.exe崩溃以后,程序在系统托盘中的图标就消失了。
         *
         * 原理:Explorer.exe重新载入后会重建系统任务栏。当系统任务栏建立的时候会向系统内所有
         * 注册接收TaskbarCreated消息的顶级窗口发送一条消息,我们只需要捕捉这个消息,并重建系
         * 统托盘的图标即可。
         */
        if (message == WM_TASKBARCREATED)
        {
            SendMessage(hWnd, WM_CREATE, wParam, lParam);
        }
        break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                                        LPSTR szCmdLine, int iCmdShow)
{
    HWND hWnd;
    MSG msg;
    WNDCLASS wc;

    HWND handle = FindWindow(NULL, szCaption);
    if (handle != NULL)
    {
        MessageBox(NULL, TEXT("Application is already running"),
                         szAppName, MB_ICONERROR);
        return 0;
    }

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;

    if (!RegisterClass(&wc))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
                         szAppName, MB_ICONERROR);
        return 0;
    }

    // 此处使用WS_EX_TOOLWINDOW属性来隐藏显示在任务栏上的窗口程序按钮
    hWnd = CreateWindowEx(WS_EX_TOOLWINDOW,
                        szAppName, szCaption,
                        WS_POPUP,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

Comments