I am trying to create an application with no visible windows, simply a tray icon. I have tried to cobble together various tutorials and answers here however haven't been able to get further than this. The context menu appears when I right click however is entirely blank. I'm also not sure how I would go about detection what I clicked on once I get it working.
The end goal is to be able to switch DNS servers by clicking one of two options in the context menu.
#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);
}
}
}
}
}
Don't use cast like this (LPCWSTR)"DNSChanger"
. This only hides the compiler error. The only reason your program works at all is because this error is repeated in 2 different places and it cancels out.
You meant to write L"DNSChanger"
.
Window procedure must return DefWindowProc(hWnd, uMsg, wParam, lParam);
In WM_DESTROY
you must include PostQuitMessage(0);
if you want to close the application.
Define a new constant to use in menu
const int IDM_EXIT = 100;
...
InsertMenu(hmenu, 0, MF_BYPOSITION | MF_STRING, IDM_EXIT, L"Exit");
The menu will send IDM_EXIT command as part WM_COMMAND
message. Below are some recommended changes:
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;
}