Обработка сообщений для иконки в трее в консольном приложении
Задача состоит в том чтобы консольное(обязательно) приложение сворачивалось в трей. То есть окно скрывалось, а в трее появлялась иконка, по нажатию на которую окно снова отображалось. Скрытие окна и создание иконки проблем не доставили, а вот с нажатием на иконку возникла проблема. Как я понял, сообщения консольного окна обрабатываются отдельным процессом(или чем то там) так что их получить нам нельзя а значит нет смысла привязывать иконку к консольному окну и нужно создать обычное(невидимое, но существующее) и делать его обработчик событий. В интернете ничего толкового не нашел. По разным сайтам собрал такой вот код, но иконка в таком случае начинает пропадать при наведении мыши(то есть уже не кликнуть):
...
void createTrayIcon()
{
HINSTANCE instance = GetModuleHandle(nullptr);
WNDCLASSEX notify = { 0 };
notify.cbSize = sizeof(WNDCLASSEX);
notify.hInstance = instance;
notify.lpszClassName = TEXT("Main");
notify.lpfnWndProc = WndProc;
RegisterClassEx(¬ify);
const HWND g_hWnd = CreateWindowEx(0, _T("PlaceholderWindow"), nullptr, 0, 0, 0, 0, 0,
HWND_MESSAGE, nullptr, instance, nullptr);
NOTIFYICONDATA *data = new NOTIFYICONDATA{};
data->uID = 1234;
data->hWnd = g_hWnd;
data->uFlags = NIF_TIP | NIF_MESSAGE | NIF_ICON;
data->hIcon = LoadIcon(nullptr, IDI_APPLICATION);
data->uCallbackMessage = WM_USER;
_tcscpy(data->szTip, _T("Сервер работает"));
Shell_NotifyIcon(NIM_ADD, data);
}
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_USER:
if (lParam == WM_LBUTTONDOWN)
std::cout << "Icon clicked" << std::endl; //тут будет код разворачивания
break;
}
return 0;
}
Как заставить иконку сидеть на месте? И что еще нужно сделать, чтобы по нажатию на иконку окно получило сообщение и запустило его обработчик?
Ответы (1 шт):
Просто создать окно недостаточно, ему нужен цикл обработки сообщений.
Добавил цикл и ещё несколько правок:
void createTrayIcon() {
WNDCLASS cls = { 0 };
cls.lpszClassName = TEXT("MsgWnd");
cls.hInstance = GetModuleHandle(NULL);
cls.lpfnWndProc = WndProc;
RegisterClass(&cls);
NOTIFYICONDATA data = { 0 };
data.hWnd = CreateWindow(cls.lpszClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, cls.hInstance, nullptr);
data.uFlags = NIF_TIP | NIF_MESSAGE | NIF_ICON;
data.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
data.uCallbackMessage = WM_USER;
_tcscpy(data.szTip, TEXT("Сервер работает"));
Shell_NotifyIcon(NIM_ADD, &data);
MSG msg; BOOL r;
while ((r = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (r == -1) break;
DispatchMessage(&msg);
}
Shell_NotifyIcon(NIM_DELETE, &data);
}
LRESULT CALLBACK WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_USER) {
switch (lParam) {
case WM_LBUTTONDOWN:
std::cout << "Icon clicked" << std::endl;
PostQuitMessage(0); // для выхода из цикла обработки сообщений
return 0;
}
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
Но в этом случае сама программа будет заблокирована пока пользователь не кликнет по иконке, т. к. будет крутить цикл обработки сообщений. Так что если во время отображения иконки программа должна что-то делать, то эту функцию (createTrayIcon) нужно вызывать в отдельном потоке.