the WM_CLOSE event is never sent/received? - c++

I'm learning DX12 and, in the process, learning "good old Win32".
i have trouble exiting the main loop and it seems related to the fact that i'm not receiving the WM_CLOSE message.
In C++, Windows 10, Console application.
#include <iostream>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
std::cout << "Hello World!\n";
WNDCLASSEX wc = {
sizeof(WNDCLASSEX),
CS_CLASSDC,
WndProc,
0L, 0L,
GetModuleHandle(NULL),
NULL, NULL, NULL, NULL,
_T("ker engine"),
NULL
};
std::cout << "Registering Class\n";
::RegisterClassEx(&wc);
std::cout << "Creating Window\n";
HWND hwnd = ::CreateWindow(
wc.lpszClassName,
_T("Ker Engine DX12"),
WS_OVERLAPPEDWINDOW,
100, 100, 1280, 800, NULL, NULL,
wc.hInstance, NULL
);
std::cout << "Show Window\n";
::ShowWindow(hwnd, SW_SHOWDEFAULT);
std::cout << "Update Window\n";
::UpdateWindow(hwnd);
std::cout << "Entering main loop\n";
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != (WM_QUIT))
{
if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
std::cout << msg.message << std::endl;
switch (msg.message)
{
case WM_CLOSE:
std::cout << "close received\n";
::PostQuitMessage(0);
break;
}
continue;
}
}
std::cout << "leaving main loop\n";
std::cout << "Destroy Window\n";
::DestroyWindow(hwnd);
std::cout << "Unregister Class\n";
::UnregisterClass(wc.lpszClassName, wc.hInstance);
std::cout << "Bye\n";
return 0;
}
When i press the X (close) red window button, the window is closed but :
"closed received" isn't printed
"leaving main loop" isn't printed.
The output is :
Entering main loop
[a lot of message code, in decimal]
160 (a lot of it) (WM_NCMOUSEMOVE)
161 (WM_NCLBUTTONDOWN)
275 (WM_TIMER)
no more output printed, i have to close the console manually.
no WM_CLOSE,or WM_DESTROY, or WM_QUIT. Between BUTTONDOW and whatever TIMER is supposed to be, there should be an event related to the fact that the windows was closed, doesn't it ?
I'm a beginner at this. I tried to search google and stackoverflow but i didn't understand if the context applied to me, or it was too specific/unrelated. it's probably a duplicate but i can't find it.
Am i losing/skiping message perhaps ? that's all i can think of.

Thanks to Simon Mourier comment and link to a tutorial, the problem was solved.
The message handling had to be done in WndProc, not in the "main loop".
I'm reposting the modified, cleaned, working, code :
#include <iostream>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
std::cout << "close received\n";
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
WNDCLASSEX wc = {
sizeof(WNDCLASSEX),
CS_CLASSDC,
WndProc,
0L, 0L,
GetModuleHandle(NULL),
NULL, NULL, NULL, NULL,
_T("ker engine"),
NULL
};
::RegisterClassEx(&wc);
HWND hwnd = ::CreateWindow(
wc.lpszClassName,
_T("Ker Engine DX12"),
WS_OVERLAPPEDWINDOW,
100, 100, 1280, 800, NULL, NULL,
wc.hInstance, NULL
);
::ShowWindow(hwnd, SW_SHOWDEFAULT);
::UpdateWindow(hwnd);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (msg.message != (WM_QUIT))
{
if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
::DestroyWindow(hwnd);
::UnregisterClass(wc.lpszClassName, wc.hInstance);
return 0;
}

Related

WH_KEYBOARD_LL hook not called in UMDF driver, but works in example console app

I'm trying to detect all key presses and mouse events by registering WH_KEYBOARD_LL and WH_MOUSE_LL hooks. Apparently these low-level hooks don't require the hook procedure to reside in a separate DLL.
I have this working in the following example app (a console application).
#include <iostream>
#include <Windows.h>
#include <Winuser.h>
#include <thread>
#include <chrono>
#include <sstream>
#include <atomic>
#include <cassert>
LRESULT CALLBACK wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::cout << std::endl << "EVENT: " << hWnd << ", " << message << ", " << wParam << ", " << lParam << std::endl;
return 0;
}
LRESULT CALLBACK keyboardHook(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
std::cout << "Keyboard: " << nCode << ", " << wParam << ", " << lParam << std::endl;
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK mouseHook(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
std::cout << "Mouse: " << nCode << ", " << wParam << ", " << lParam << std::endl;
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main()
{
const wchar_t pszClassName[] = L"MyMessageWindow";
auto hInstance = GetModuleHandle(NULL);
WNDCLASSEXW wcl;
ZeroMemory(&wcl, sizeof(WNDCLASSEXW));
wcl.cbSize = sizeof(WNDCLASSEXW);
wcl.hInstance = hInstance;
wcl.lpszClassName = pszClassName;
wcl.lpfnWndProc = wndProc;
assert(RegisterClassExW(&wcl) != 0);
auto hwnd = CreateWindowW(
pszClassName,
pszClassName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
NULL,
NULL,
NULL
);
auto threadId = 0;
std::thread t([hwnd, threadId]() {
auto keyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, NULL, threadId);
auto mouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, mouseHook, NULL, threadId);
while (true) {
MSG msg;
GetMessage(&msg, hwnd, 0, 0);
}
});
t.join();
return 0;
}
So this works, and I can see the keyboard and mouse events printed to the console regardless of what apps are in focus (if any), which is great.
However, when I do this inside a UMDF driver, the messages don't come through. The calls to SetWindowsHookEx appear to succeed without error.
I'm thinking of creating a minimal UMDF driver to test if it's possible, but thought I'd first ask here in case someone can tell me whether it's possible. If not, I have a few other approaches in mind.
Thanks

C++ ignore events for x amount of seconds

I got a bit of code off github that locks a laptop if the monitor goes to sleep or if the laptop lid is closed. The issue is, if the laptop is started up while the lid is already closed, such as when it's on a docking station for example. The second the code runs it sends the notification to lock the laptop. I have the complied program sitting in the startup folder so the behavior is such that when I login the program starts and then locks the pc immediately if it's closed.
I need to be able to ignore the GUID_LIDSWITCH_STATE_CHANGE and GUID_MONITOR_POWER_ON events for x amount of seconds once the program is started so that this behavior will stop, but I cannot figure out how to do this. From what I understand the bit of code to ignore all events for x seconds needs to be inserted under the "static LRESULT CALLBACK" portion of the code
Here is the code below:
#include <chrono>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <memory>
#include <sstream>
#include <windows.h>
namespace laplock {
namespace {
class LogLine {
public:
LogLine() {
if (!logfile) return;
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
tm localnow;
if (localtime_s(&localnow, &now) == 0) {
*logfile << std::put_time(&localnow, L"%F %T ");
}
}
~LogLine() {
if (!logfile) return;
*logfile << std::endl;
logfile->flush();
}
template <typename T> LogLine& operator<<(T value) {
if (!logfile) return *this;
*logfile << value;
return *this;
}
static std::unique_ptr<std::wofstream> logfile;
};
std::unique_ptr<std::wofstream> LogLine::logfile;
std::wostream& operator<<(std::wostream& stream, const GUID& guid) {
wchar_t str[128] = { 0 };
StringFromGUID2(guid, str, sizeof(str));
stream << str;
return stream;
}
static void systemError(wchar_t* what)
{
const DWORD error = GetLastError();
LogLine() << "Error " << error << " during: " << what;
static wchar_t buffer[1024];
const wchar_t* errorMessage = buffer;
if (!FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
0,
error,
0,
buffer,
sizeof(buffer),
0
))
errorMessage = L"(cannot format error message)";
else
LogLine() << "System error message: " << errorMessage;
std::wstringstream message;
message << L"A system error occured within laplock." << std::endl;
message << L"Operation: " << what << std::endl;
message << L"System message: " << errorMessage;
MessageBox(NULL, message.str().c_str(), L"laplock error", MB_OK | MB_ICONERROR);
exit(EXIT_FAILURE);
}
static LRESULT CALLBACK windowProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE) {
LogLine() << "Window received irrelevant message";
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
if (GetSystemMetrics(SM_REMOTESESSION)) {
LogLine() << "Ignoring window message because session is currently remote";
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
const POWERBROADCAST_SETTING* setting = reinterpret_cast<const POWERBROADCAST_SETTING*>(lParam);
LogLine() << "Received POWERBROADCAST_SETTING " << setting->PowerSetting;
if (setting->PowerSetting != GUID_MONITOR_POWER_ON && setting->PowerSetting != GUID_LIDSWITCH_STATE_CHANGE) {
LogLine() << "Received irrelevant POWERBROADCAST_SETTING";
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
const DWORD* state = reinterpret_cast<const DWORD*>(&setting->Data);
LogLine() << "POWERBROADCAST_SETTING state: " << *state;
if (*state != 0) {
LogLine() << "Irrelevant POWERBROADCAST_SETTING state";
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LogLine() << "Locking";
if (LockWorkStation() == 0)
systemError(L"locking workstation");
else
LogLine() << "Locked";
return 0;
}
static void registerWindowClass(HINSTANCE instance)
{
LogLine() << "Registering window class";
WNDCLASSEX windowClass = { 0 };
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.lpfnWndProc = &windowProcedure;
windowClass.hInstance = instance;
windowClass.lpszClassName = L"laplock";
if (RegisterClassEx(&windowClass) == 0)
systemError(L"registering window class");
}
static HWND createWindow(HINSTANCE instance)
{
LogLine() << "Creating window";
HWND hWnd = CreateWindow(
L"laplock",
NULL,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
NULL,
instance,
NULL
);
if (hWnd == NULL)
systemError(L"creating window");
return hWnd;
}
static void registerNotification(HWND window)
{
LogLine() << "Registering GUID_MONITOR_POWER_ON (GUID: " << GUID_MONITOR_POWER_ON << ")";
if (!RegisterPowerSettingNotification(window, &GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE))
systemError(L"cannot register GUID_MONITOR_POWER_ON power setting notification");
LogLine() << "Registering GUID_LIDSWITCH_STATE_CHANGE (GUID: " << GUID_LIDSWITCH_STATE_CHANGE << ")";
if (!RegisterPowerSettingNotification(window, &GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE))
systemError(L"cannot register GUID_LIDSWITCH_STATE_CHANGE power setting notification");
}
static WPARAM messageLoop()
{
for (;;)
{
LogLine() << "Awaiting next window message";
MSG message;
BOOL result = GetMessage(&message, NULL, 0, 0);
if (result == -1)
systemError(L"getting window message");
if (result == 0)
return message.wParam;
LogLine() << "Dispatching message";
DispatchMessage(&message);
}
}
int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, LPSTR commandLine, int)
{
if (*commandLine != 0)
LogLine::logfile.reset(new std::wofstream(commandLine, std::ios_base::app));
LogLine() << "laplock initializing";
registerWindowClass(instance);
HWND window = createWindow(instance);
registerNotification(window);
WPARAM result = messageLoop();
LogLine() << "laplock terminating";
return result;
}
}
}
int CALLBACK WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR commandLine, int cmdShow)
{
return laplock::WinMain(instance, prevInstance, commandLine, cmdShow);
}

Cannot receive WM_CLIPBOARDUPDATE messages

I am trying to use Clipboard Format Listener for my C++ Console Application. The goal is to monitor every change in the clipboard. I create MessageOnly window, successfully call AddClipboardFormatListener in WM_CREATE, but never get WM_CLIPBOARDUPDATE message in WindowProc function.
#include <iostream>
#include "windows.h"
using namespace std;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
if (AddClipboardFormatListener(hwnd))
cout << " Listener started" << endl;
else
cout << " Start listener failed" << endl;
break;
case WM_DESTROY:
if (RemoveClipboardFormatListener(hwnd))
cout << " Listener stopped" << endl;
else
cout << " Stop listener failed" << endl;
break;
case WM_CLIPBOARDUPDATE:
// Clipboard content has changed
cout << " Clipboard updated" << endl;
break;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int main(int argc, char* argv[])
{
HWND hWindow = nullptr;
static const wchar_t* className = L"ClipboardListener";
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = WindowProc;
wx.hInstance = GetModuleHandle(NULL);
wx.lpszClassName = className;
if (!RegisterClassEx(&wx)) {
cout << "Cannot register class" << endl;
}
else
{
hWindow = CreateWindowEx(
0,
className,
L"ClipboardListener",
0, 0, 0, 0, 0,
HWND_MESSAGE,
NULL, NULL, NULL);
}
if (!hWindow)
{
cout << "Cannot create window" << endl;
}
else
{
while (true)
{
// Peek for a WM_CLIPBOARDUPDATE message
MSG message = { 0 };
PeekMessage(&message, hWindow, WM_CLIPBOARDUPDATE, WM_CLIPBOARDUPDATE, PM_REMOVE);
if (message.message == WM_CLIPBOARDUPDATE)
{
cout << "Sample window received WM_CLIPBOARDUPDATE message" << endl;
}
}
}
cin.get();
DestroyWindow(hWindow);
return 0;
}
PeekMessage works well, but I don't want to use loop to receive messages.
If I delete PeekMessage or replace PM_REMOVE with PM_NOREMOVE, nothing changes.
Your message loop is wrong.
CreateWindowEx() sends a WM_CREATE message before exiting, which is why your WindowProc() receives that message.
However, PeekMessage() does not dispatch messages to windows, which is why your WindowProc() does not receive the WM_CLIPBOARDUPDATE messages. Your message loop needs to call DispatchMessage() for that. You should also be using GetMessage() instead of PeekMessage(), so that the loop makes the calling thread sleep when there are no messages to process.
A standard message loop looks more like this:
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}

WH_KEYBOARD_LL not working for regular letters and digits

I'm using SetWindowsHookExA(WH_KEYBOARD_LL, HookCallback, GetModuleHandleA(NULL), 0); to set a global hook for capturing the keystrokes, but the result is strange.
The callback function can be executed when I press the "special" keys such as "Enter", "Tab", "Shift", "Ctrl" and other keys having a Virtual Key Code, while it fails to capture the keystrokes when I press the regular letters and digits.
I am confused about it and could anyone tell me the reason?
#include <Windows.h>
#include <iostream>
using namespace std;
HHOOK keyboardHook = 0;
LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam;
cout<< "[TEST] " << ks->vkCode << endl;
return CallNextHookEx(0, code, wParam, lParam);
}
int main()
{
keyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, HookCallback, GetModuleHandleA(NULL), 0);
if (keyboardHook == 0)
{
cout << "failed" << endl;
return -1;
}
cout << "ok" << endl;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(keyboardHook);
return 0;
}

Separate window message pumps and receiving WM_QUIT

I'm trying to create independent window wrapper classes for my project. It's mostly working but cannot figure out how to get WM_QUIT in my main message pump. In the interest of learning Windows, I don't want to use other libraries for this.
This is a quick example of whats happening.
#include <iostream>
#include <Windows.h>
void TestPump()
{
MSG msg = { 0 };
PostQuitMessage(0);
std::cout << "Posted WM_QUIT" << std::endl;
while (true)
{
BOOL result = PeekMessage(&msg, (HWND) -1, 0, 0, PM_REMOVE);
std::cout << "PeekMessage returned " << result << std::endl;
if (result == 0)
break;
if (WM_QUIT == msg.message)
std::cout << "got WM_QUIT" << std::endl;
}
}
void MakeWindow()
{
auto hwnd = CreateWindowEx(0, "Button", "dummy", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL);
std::cout << std::endl << "Created Window" << std::endl << std::endl;
}
int main()
{
TestPump();
MakeWindow();
TestPump();
std::cin.get();
return EXIT_SUCCESS;
}
The PeekMessage documentation is here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx
I haven't been able to find any examples of using a -1 HWND filter, but MSDN says that it'll receive thread messages where the HWND is NULL (I've checked that this is true for WM_QUIT), which I believe PostQuitMessage does with WM_QUIT.
The problem only occurs if I create a Window.
Is there anything I'm doing wrong, or are there better methods?
This seems to be an interesting case so I've created a mcve, though I'm struggling to explain this behavior:
#include <Windows.h>
#include <CommCtrl.h>
#include <iostream>
#include <cassert>
void
Create_ThreadMessagePump(void)
{
::MSG msg;
::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
::std::wcout << L"initialized thread message pump" << ::std::endl;
}
void
Create_Window(void)
{
auto const hwnd{::CreateWindowExW(0, WC_STATICW, L"dummy window", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL)};
(void) hwnd; // not used
assert(NULL != hwnd);
::std::wcout << L"created window" << ::std::endl;
}
void
Test_QuitMessageExtraction(void)
{
::PostQuitMessage(0);
::std::wcout << L"posted WM_QUIT" << ::std::endl;
for(;;)
{
::MSG msg;
auto const result{::PeekMessageW(&msg, (HWND) -1, 0, 0, PM_REMOVE)};
::std::wcout << L"PeekMessageW returned " << result << ::std::endl;
if(0 == result)
{
::std::wcout << L"no more messages to peek" << ::std::endl;
break;
}
if(WM_QUIT == msg.message)
{
::std::wcout << L"got WM_QUIT" << ::std::endl;
}
}
}
int
main()
{
//Create_ThreadMessagePump(); // does not change anything
Test_QuitMessageExtraction();
Create_Window();
Test_QuitMessageExtraction();
system("pause");
return(0);
}
output:
posted WM_QUIT
PeekMessageW returned 1
got WM_QUIT
PeekMessageW returned 0
no more messages to peek
created window
posted WM_QUIT
PeekMessageW returned 0
no more messages to peek
The problem appears to be with the way WM_QUIT and PostQuitMessage() operates. You can find information here:
Why is there a special PostQuitMessage function?.
In short, the WM_QUIT message will not be received while the queue is non-empty. Even if you filter out other messages by using a -1 HWND, the queue is still non-empty, and thus WM_QUIT is not returned. Creating of a window results in multiple HWNDs being created.