C++ event handling for click on notification area Icon Windows - c++

I want to achieve some kind of event handling for Windows. I have a program, that has a symbol in the Notification Area (System Tray) and I want the programm to show up again, when the user clicks on the Icon. Is there a simple Way to implement that in c++ as an Event? I have only found ways to this in C#.
It is a console Application and I want to change as few things as possible. But there is no WndProc handler for Console Application as far as I can tell.

Why no WndProc? Console application is a perfect win32 application, and it can use anything which non-console application can use.
Here's a simple, but a bit long example.
#include <windows.h>
#include <shellapi.h>
#include <iostream>
#include <cstring>
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
BOOL WINAPI ConsoleRoutine(DWORD dwCtrlType);
LPCWSTR lpszClass = L"__hidden__";
int main()
{
HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASS wc;
HWND hWnd;
MSG msg;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = nullptr;
wc.hCursor = nullptr;
wc.hIcon = nullptr;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = lpszClass;
wc.lpszMenuName = nullptr;
wc.style = 0;
RegisterClass(&wc);
hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, hInstance, nullptr);
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static NOTIFYICONDATA nid;
switch (iMsg)
{
case WM_CREATE:
std::memset(&nid, 0, sizeof(nid));
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 0;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_APP + 1;
nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
lstrcpy(nid.szTip, L"con-notify");
Shell_NotifyIcon(NIM_ADD, &nid);
Shell_NotifyIcon(NIM_SETVERSION, &nid);
return 0;
case WM_APP + 1:
switch (lParam)
{
case WM_LBUTTONDBLCLK:
std::cout << "notify dblclk" << std::endl;
break;
case WM_RBUTTONDOWN:
case WM_CONTEXTMENU:
break;
}
break;
case WM_DESTROY:
Shell_NotifyIcon(NIM_DELETE, &nid);
MessageBox(nullptr, L"asdf", L"asdf", MB_OK);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,iMsg,wParam,lParam);
}
You probably want not to mess up your console program with message loop. If so, you can put notification code into another thread.

Related

How to prevent some action in app using winapi?

I would like to prevent some action in my app using winapi for example I would like to block moving my window app. How can I do this?
I try this:
if( pWindowsMessage->message == WM_MOVING )
{
return 1;
}
else if(pWindowsMessage->message == WM_MOVE)
{
return 1;
}
But it doesn't work.
Other example: How to prevent close the window?
if( pWindowsMessage->message == WM_CLOSE )
{
return 1;
}
It works. But is it a good solution?
Of course the first and the second example are in the function which get messages.
You can block messages in different areas by handling WM_NCLBUTTONDOWN messages.
And According to WM_NCHITTEST,we can handle events in different regions.
Here is a sample:
#include <Windows.h>
#include <cassert>
#include <cstdlib>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NCLBUTTONDOWN:
if (wParam == HTCAPTION || (wParam >= HTLEFT && wParam < HTBORDER))
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Message Box is not working inside WM_COMMAND! (WIN32 API)

I am new to Win32 API and trying to learn it. I was successful to create a window and it works perfectly.
I added a button to it and want to show a message box when clicked. The button works perfectly but the message box in WM_COMMAND does not appear at all and the code below message box does not get executed as well.
I have checked online for how to do it and it seems to work for them but not me.
Here is the code
#include <Windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
WNDCLASSEX wcex;
static TCHAR szWindowClass[] = _T("DesktopApp");
static TCHAR szTitle[] = _T("First Application");
HINSTANCE hInst;
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow)
{
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL, _T("Call to Register Failed"), _T("Windows Desktop Guided Tour"), NULL);
return 1;
}
hInst = hInstance;
HWND hwnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
MessageBox(NULL, _T("Failed to create a window"), _T("Windows Desktop Guided Tour"), NULL);
return 1;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
HWND button;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello world! This is the first ever application window created by dumb Bhavin.");
switch (message)
{
case WM_CREATE:
button = CreateWindow(_T("BUTTON"),_T("1") ,WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 100, 40, 50, 30, hWnd, (HMENU)1, NULL, NULL);
break;
//////////////////////////////////THIS IS WHERE THE ISSUE IS///////////////////////////////////////////
case WM_COMMAND:
{
if (LOWORD(wParam) == 1)
{
OutputDebugString(_T("The compiler executes this! That means the button is working"));
MessageBox(NULL, L"Here it is", L"ok", NULL); //Message box does not appear at all. The code below it does not execute at all.
OutputDebugString(_T("The compiler DOES NOT execute this!"));
}
break;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
case WM_PAINT:
// hdc = BeginPaint(hWnd, &ps);
// TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
Edit 1:
Here is a small video of what exactly happens.
Error Video
Note : I passed hWnd parameter in this instead of NULL. Passing hWnd as first Parameter does not help either.
The issue was my half-implemented WM_Paint. Uncommenting the BeginPaint line solves the problem.
or, passing it directly to DefWindowProc as return DefWindowProc(hWnd, message, wParam, lParam); works too.

Custom window proc in a separate file

I'm currently learning winapi, and would like to get some advice on how to set up a custom control using different window procedures.
Let's say I have 20 buttons. I want each button to respond differently when the mouse hovers over it. Say, an "exit" button draws a red rectangle when hovered, or blue when some other button is hovered.
So, I have set up a custom control procedure that handles mouse clicks, mouse hover, and such, and is stored in custom.cpp. In main.cpp, there is a MainProc() that "links/assigns" hwndButton to ButtonProc() using
CustomProcHandler = (WNDPROC)SetWindowLong(hwndButton, GWL_WNDPROC, (long)CustomProc)
main.h:
#include <windows.h>
#include <iostream>
using namespace std;
const char g_szClassName[] = "Applicaton";
static HWND hwnd, hwndButton;
static HINSTANCE hInst;
static WNDPROC buttonProcHandler;
LRESULT CALLBACK MainProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ButtonProc(HWND, UINT, WPARAM, LPARAM);
main.cpp:
#include "main.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR nCmdLine, int nCmdShow){
WNDCLASSEX wc;
MSG msg;
hInst = hInstance;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, 0);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(RGB(20, 20, 20));
wc.lpszMenuName = 0;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(hInst, 0);
RegisterClassEx(&wc);
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,
NULL, NULL, hInst, NULL);
ShowWindow(hwnd, 1);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
case WM_CREATE:
button = CreateWindow("button", 0, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 10, 10, 32, 32, hwnd, 0, hInst, 0);
buttonProcHandler = (WNDPROC)SetWindowLong(hwndButton, GWL_WNDPROC, (long)ButtonProc);
break;
case WM_MOUSEMOVE:
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
custom.cpp:
#include "main.h"
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
case WM_CREATE:
break;
case WM_MOUSEMOVE:
break;
case WM_LBUTTONDOWN:
break;
default:
return CallWindowProc(buttonProcHandler, hwnd, msg, wParam, lParam);
}
return 0;
}
The problem is the button disappears... but when I move buttonProc() inside of main.cpp then everything works fine.
So, I am guessing I did something wrong when declaring global variables like static WNDPROC buttonProcHandler.
I know what I am doing is entirely wrong, and that there is a better way of doing it. I just don't know what that is.
Can anyone can help/teach me the standard way of creating custom procedures?

Win32 window - no close message

I am currently making a small win32 window wrapper class, but I have a few problem.
If I hit the close(X) button of the window the window closes immediately without sending a quit or destroy message, so I can't for example prevent the window to close or save something before closing the window.
And the second problem/question is,
If I use this small code to use the window, the computer cpu gets strongly used.
But its only a small window.
How I can change/fix this?
int main()
{
glwCreate();
while(true/*Later here comes a method that checks, wether window close is requested*/)
{
glwUpdate();
}
glwDestroy();
return 0;
}
-
#include "glw.h"
#include <windows.h>
#include <iostream>
HINSTANCE instanceHandle;
WNDCLASSEX windowClass;
HWND windowHandle;
LRESULT CALLBACK WindowMessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::cout<<uMsg<<'\n';
switch(uMsg)
{
case WM_QUIT:
{
std::cout<<"QUIT\n";
return 0;
}
case WM_DESTROY:
{
std::cout<<"DESTROY\n";
return 0;
}
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
void glwCreate()
{
instanceHandle = GetModuleHandle(0);
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowMessageHandler;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instanceHandle;
windowClass.hCursor = LoadCursor(0,IDC_ARROW);
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszClassName = "atomus_window_class";
windowClass.lpszMenuName = "menu_name";
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
RegisterClassEx(&windowClass);
windowHandle = CreateWindowEx( 0,
"atomus_window_class",
"atomus title",
WS_OVERLAPPEDWINDOW,
0,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
instanceHandle,
0);
ShowWindow(windowHandle, SW_SHOW);
}
void glwDestroy()
{
DestroyWindow(windowHandle);
windowHandle = 0;
UnregisterClass(windowClass.lpszClassName, instanceHandle);
}
void glwUpdate()
{
MSG message;
while (PeekMessage (&message, 0, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage (&message);
DispatchMessage (&message);
}
}
If you add handling for WM_CLOSE you get to control whether your window closes or not. By not providing your own handling for that message you get the default from DefWindowProc which is to destroy your window.

Why aren't Shell_NotifyIcon balloon tips working?

According to everything I've seen, the following C++ program should be displaying a balloon tool tip from the tray icon when I left-click in the application window, yet it's not working. Can anyone tell me what I'm missing?
This is on XP with version 6.0 of Shell32.dll (verified with DllGetVersion).
Thanks!
#include "stdafx.h"
#include "shellapi.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "sysTrayTest";
RegisterClass(&wc);
HWND hWnd = CreateWindow("sysTrayTest", "",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 500, 500,
NULL, NULL, hInstance, NULL);
if (hWnd)
{
ShowWindow(hWnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
Shell_NotifyIcon(NIM_DELETE, &nid);
PostQuitMessage(0);
}
break;
case WM_CREATE:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER + 200;
nid.hIcon = LoadIcon(NULL, IDI_INFORMATION);
lstrcpy (nid.szTip, "Test Tip");
Shell_NotifyIcon(NIM_ADD, &nid);
}
break;
case WM_LBUTTONDOWN:
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_INFO;
lstrcpy(nid.szInfo, "Test balloon tip");
lstrcpy(nid.szInfoTitle, "Test Title");
nid.dwInfoFlags = NIIF_INFO;
nid.uTimeout = 15000;
Shell_NotifyIcon(NIM_MODIFY, &nid);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Bah, I figured it out. For some reason with the headers I have...
sizeof(NOTIFYICONDATA) == 508
whereas...
NOTIFYICONDATA_V3_SIZE == 504
NOTIFYICONDATA_V2_SIZE == 488
NOTIFYICONDATA_V1_SIZE == 88
If I specify either V2 or V3 instead of sizeof(NOTIFYICONDATA) the balloon tips show up just fine.
Have you checked in the registry under ...
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
... for EnableBalloonTips? It's something very common for users to turn off.
The problem is that you are assuming Windows is going to send you a WM_LBUTTONDOWN when the user click on the icon, but that is not correct. WM_LBUTTONDOWN is sent only when the user clicks inside the hWnd's client area, if you read carefully the documentation of NOTIFYICONDATA you will realize that when the user clicks the icon Windows will send you a WM_USER+20 message (according to your code) and in the lParam paramter you will get the WM_LBUTTONDOWN notification.