I am writing a windows application using the WinAPI (win32) on C++, but can't figure out what's happening with the ALT key. I am handling both WM_KEYDOWN and WM_SYSKEYDOWN, so all keypresses should be coming through. However, while every ALT keyup is detected, every second ALT keydown is being skipped.
Do I have to generate my own keydown event whenever I get a second WM_SYSKEYUP in a row, or am I doing something wrong with my message handling?
Details
Tap these keys: ALT ALT ALT ALT ALT (alt five times)
Expected message sequence: DUDUDUDUDU (down then up, repeated five times)
Observed message sequence: DUUDUUDU (every second down is missing)
Tap these keys: ALT C ALT C ALT C ALT C ALT C (add other keypresses in between - release ALT before tapping C)
Expected VK_MENU message sequence: DUDUDUDUDU (down then up, repeated five times)
Observed VK_MENU message sequence: DUDUDUDUDU (as expected!)
It seems that adding other keypresses (or even mouse events) cancels whatever Windows was doing and forces it to send the app all the ALT keypresses.
Example code (Visual c++17):
Creates a window which adds 'D' to the window title when ALT is pressed and 'U' when ALT is released
#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define STRICT
#include <Windows.h>
#include <optional>
#include <malloc.h>
WCHAR* title;
int titleLen = 0;
const int maxTitleLen = 64;
void updateWindowTitle(WCHAR c, HWND hwnd)
{
if (titleLen >= maxTitleLen) return;
title[titleLen++] = c;
title[titleLen] = 0;
SetWindowTextW(hwnd, title);
}
LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_SYSKEYDOWN: //needed for keys like alt (VK_MENU)
case WM_KEYDOWN:
//process_keydown_event(wParam);
if (!(lParam & 0x40000000)) { //ignore autorepeat
if (wParam == VK_MENU) updateWindowTitle(L'D', hwnd);
}
break;
case WM_SYSKEYUP: //needed for keys like alt (VK_MENU)
case WM_KEYUP:
//process_keyup_event(wParam);
if (wParam == VK_MENU) updateWindowTitle(L'U', hwnd);
break;
//etc...
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// *********** STANDARD BORING SETUP BELOW ************
std::optional<int> processMessages() noexcept
{
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
// Return argument to PostQuitMessage as we are quitting
return msg.wParam;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return {}; //return empty optional when not quitting
}
int CALLBACK WinMain(
HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR lpCmdLine,
int nShowCmd)
{
// Make global title point to valid memory
title = static_cast<WCHAR*>(malloc(sizeof(WCHAR) * maxTitleLen));
// Register window class
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.hInstance = hInst;
wc.hIcon = nullptr;
wc.hIconSm = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = L"my window class name";
RegisterClassEx(&wc);
// Create window 800x500
HWND hwnd = CreateWindowW(
wc.lpszClassName, L"",
WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 500,
nullptr, nullptr, hInst, nullptr
);
// (check HWND is not NULL)
//main loop
while (true)
{
if (const auto errorcode = processMessages())
{
return errorcode.value();
}
}
}
I can reproduce the problem through code, and then I capture the message of the window through spy++.
We can see that when the ALT key is pressed for the first time, the WM_SYSKEYDOWN and WM_SETTEXT messages are successfully triggered:
When the ALT key is pressed the second time, it actually triggers the WM_SYSKEYDOWN message, but after a series of message sending(WM_CAPTURECHANGED,WM_MENUSELECT...).This resulted in the message not being processed in the main window, but sent to hmenu:00000000, so the main window did not process the WM_SYSKEYDOWN message.
You can handle it by processing the WM_SYSCOMMAND message:
case WM_SYSCOMMAND:
if (wParam == SC_KEYMENU)
{
return 0;
}
else
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
It works for me.
Related
I am creating a window with the following 2 aims both of which are requird:
when person clicks outside of the window, the window is destroyed.
when a person clicks a button inside the window, we process the WM_COMMAND message, and then the window is destroyed.
To achieve both these goals, I looked and found there is a WM_KILLFOCUS message which is sent when the window looses focus.
I wrote destroy window code in the WM_KILLFOCUS handler, but then the button click message box does not come. I searched and found the destroy event is not serialized and hence it could be that it is being sent before the window button click could be caught. I have hence changed the code like below, where it also sends a WM_COMMNAD to kill the window.
case WM_KILLFOCUS:
SendMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);// hope it serializes message
break;
case WM_COMMAND:
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_CLICKED:
default:
MessageBox(NULL, L"Hurray", L"Hurray MSG reveid", MB_OK);
break;
}
I see that when I click the button inside the window, the window is destroyed and the MessageBox "Hurray" is not received.
How can I ensure that the messages are processed before destroying the window?
Update 1: Minimal code as asked. Now handling the WM_ACTIVATE message instead for handling WM_KILLFOCUS . Both the two aims are written above:
#include <windows.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <windowsx.h>
#include <map>
#include <commctrl.h>
#define KILLTHEWIN 10
#define BUTTON_PRESS 11
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow)
{
WNDCLASSW wc = { 0 };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInst;
wc.lpszClassName = L"mywindowsclass";
wc.lpfnWndProc = windowprocedure;
// before creating window for a class we need to register that class
if (!RegisterClassW(&wc))
{
return -1; // registration failed
}
HWND hWnd = CreateWindowW(L"mywindowsclass", L"My window", WS_POPUP | WS_VISIBLE/*| WS_EX_TOOLWINDOW*/ /* check for no task bar*/,
100, 100, 170, 500, NULL, NULL, NULL, NULL);
MSG msg{ 0 };
while (GetMessage(&msg, NULL, NULL, NULL))
{
// translate the messahe
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// main window message handling
LRESULT CALLBACK windowprocedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
DWORD dwStyleOfStaticText = 0;
HWND hBmp2 = NULL;
WORD reason = 0;
switch (msg)
{
case WM_CREATE:
dwStyleOfStaticText = SS_NOTIFY | SS_LEFT | WS_CHILD | WS_VISIBLE | WS_TABSTOP /*| WS_BORDER */;
hBmp2 = CreateWindowW(L"Button", L"Button1", WS_VISIBLE | WS_CHILD,
5, 5 + 4 * 15, 100, 50,
hwnd, (HMENU)BUTTON_PRESS, NULL, NULL);
SetWindowLong(hwnd, GWL_STYLE, 0);
break;
case WM_ACTIVATE:
reason = LOWORD(wp);
if (reason == WA_ACTIVE)
{
//Activated by some method other than a mouse click (for example, by a call to the SetActiveWindow function or by use of the keyboard interface to select the window).
}
else if (reason == WA_CLICKACTIVE)
{
// activated by mouse click
}
else if (reason == WA_INACTIVE)
{
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
}
break;
case WM_COMMAND: // when ever a menu is clicked
// WM_COMMAND has been received for which control handle and then kill it
switch (wp)
{
case KILLTHEWIN:
DestroyWindow(hwnd);
break;
case BUTTON_PRESS:
MessageBoxW(hwnd, L"button pressed", L"button pressed caption ", MB_OK);
break;
}
PostMessage(hwnd, WM_COMMAND, KILLTHEWIN, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);// case other as default window procedure
}
return (LRESULT)0;
}
The WM_ACTIVATE was handled above instead of the kill focus .
The two aims written above are really important . Still on pressing the button the MessageBox does not appear and window is killed .
I put it in debugger
one time the breakpoint hit the button message box but it still did not came . The other time the breakpoint hit the WM_ACTIVATE deactivate reason first .
I'm trying to understand how does the Message Loop and generally keyboard messages works in the Windows systems. I've written a simple program featuring the Message Loop:
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
char MsgBuff[300];
template <typename T>
char* BinVal(T SrcVal) {
const short BitsCount = sizeof(T) * CHAR_BIT;
const short SeparatorCount = sizeof(T) * 2 - 1;
static char BinValStr[BitsCount + SeparatorCount] = { 0 };
printf("BitsCount: %d\n", BitsCount);
printf("SeparatorCount: %d\n", SeparatorCount);
printf("BinValStr size: %d\n", BitsCount + SeparatorCount);
int i = 0;
int j = 0;
for (i = BitsCount + SeparatorCount - 1; i >= 0; i--) {
if ((j + 1) % 5 == 0) {
BinValStr[i] = ' ';
}
else {
if ((SrcVal & 1) == 1)
BinValStr[i] = '1';
else
BinValStr[i] = '0';
SrcVal >>= 1;
}
j++;
//printf(": %d, %d\n", i, SrcVal&1);
}
return BinValStr;
}
// the entry point for any Windows program
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow) {
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// Enter the infinite message loop
while (TRUE) {
// Check to see if any messages are waiting in the queue
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// If the message is WM_QUIT, exit the while loop
if (msg.message == WM_QUIT)
break;
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
int LoopCnt = 0;
int BinMask = 0b00000000'00000000'00000000'11111111;
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY: {
PostQuitMessage(0);
} break;
case WM_KEYDOWN: {
sprintf_s(MsgBuff, 300, "WM_KEYDOWN(#%i) wParam (virtual-key code): %d (%Xh), scan code: %X, lParam(bin): %s\n", LoopCnt, wParam, wParam, lParam & BinMask, BinVal(lParam));
OutputDebugStringA(MsgBuff);
} break;
case WM_CHAR: {
sprintf_s(MsgBuff, 300, "WM_CHAR(#%i) wParam (virtual-key code): %d (%Xh), scan code: %X, lParam(bin): %s\n", LoopCnt, wParam, wParam, lParam & BinMask, BinVal(lParam));
OutputDebugStringA(MsgBuff);
} break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
LoopCnt++;
}
There i examine the WM_KEYDOWN message lParam param for the scan code send.
Now i have following questions:
Why pressing the left Alt key doesn't generate a WM_KEYDOWN message while pressing right Alt does? (the debugger doesn't go into the WM_KEYDOWN: case statement when pressing the left Alt)
Why pressing the right Alt key generates TWO WM_KEYDOWN messages simultaneously - having 1D(hex) & 38 (hex) scan codes respectively?
The code below, sends a WM_CHANGEUISTATE to the window procedure itself, with the arguments:
LOWORD(wParam) = UIS_CLEAR
HIWORD(wParam) = UISF_HIDEACCEL
lParam = 0x00000000
when the window client area is left clicked with the mouse.
According to this blog by Raymond Chen this should make the mnemonics in the System menu to be shown, when the menu is accessed with the mouse. The paragraph below was extracted from this article:
Clearing a flag shows the corresponding indicator. For example, if you
have a UIS_CLEAR for UISF_HIDEĀFOCUS, that means that you want to show
focus indicators.
In my case, I have a UIS_CLEAR for UISF_HIDEACCEL, meaning that I want to show the menu accelerators.
If you run the code below and click with the mouse left button on the app client area, you should make the accelerators visible in the System menu, even when this menu is accessed with the mouse. But that doesn't happen, i.e., if you activate the system menu with a left click on the window's icon, or with a right click on the window's title bar, the mnemonics in the System menu will not be shown. What am I missing?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
WNDCLASSEX wndclassx;
wchar_t szAppName[] = L"WM_CHANGEUISTATE";
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = CS_HREDRAW | CS_VREDRAW;
wndclassx.lpfnWndProc = WndProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(0, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszClassName = szAppName;
wndclassx.lpszMenuName = nullptr;
wndclassx.hIconSm = 0;
if (!RegisterClassEx(&wndclassx)) return 0;
HWND hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
{
BOOL b;
// Note that in my system (Windows 10) I have:
//
// Control Panel > Ease of Access > Ease of Access Center > Make the keyboard easier
//
// and the option "Underline keyboard shortcuts and access keys" unmarked (the default). Therefore, the value
// returned in b below will be 0x00000000 (FALSE).
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &b, 0);
// If b = FALSE, the SendMessage() below should underline the accelerators in the System menu, when this menu is
// accessed with the mouse. But that doesn't work. Why?
if( !b ) SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), NULL);
}
break;
// Setting a break in the return below, one can see that WM_CHANGEUISTATE message is being sent to the window and
// passed to DefWindowProc().
case WM_CHANGEUISTATE:
return DefWindowProc(hwnd, message, wParam, lParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
This seems like a bug/oversight in Windows. DefWindowProc does not send WM_QUERYUISTATE before displaying the system menu. The menu implementation checks the last input event directly and if it was from a keyboard it displays the underlines.
I tried sending and posting WM_CHANGEUISTATE and WM_UPDATEUISTATE in response to WM_ENTERMENULOOP, WM_INITMENUPOPUP, WM_NCRBUTTONDOWN and WM_SYSCOMMAND without any luck.
The only workaround I was able to come up with is to hack the HTSYSMENU/icon menu by changing SC_MOUSEMENU to SC_KEYMENU:
case WM_SYSCOMMAND:
if ((wParam & 0xFFF0) == SC_MOUSEMENU)
{
return SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ');
}
return DefWindowProc(hwnd, message, wParam, lParam);
SC_KEYMENU has special handling in DefWindowProc and forces underlines if applicable.
This does not handle right-click on the icon, caption nor task bar...
so title says it all. i was thinking maybe because there are 81 textboxes it has somthing to do with layers but quite frankly i have no idea.. just started learning windows api like 2 days ago and ive been learning streight off the msdn library for functions.. i googled this problem multiple times and no luck so here i am. the help is much appreciated ^.^
// Win32Project9.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Win32Project9.h"
#include "Resource.h"
#include <Windows.h>
#include <vector>
#include <cstring>
using namespace std;
HWND Hwnd;
HMENU hMenu;
HWND boxes[81];
int x, y;
vector<LPWSTR> BoxNum;
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
PostQuitMessage(0);
return 0;
break;
case ID_SOLVE:
for (int i = 0; i < 81; i++)
{
GetWindowText(boxes[i], BoxNum[i], NULL);
}
break;
}
break;
}
if (msg == WM_COMMAND)
{
if (LOWORD(wParam) > 199 && LOWORD(wParam) < 281)
{
if (HIWORD(wParam) == EN_SETFOCUS | HIWORD(wParam) == EN_UPDATE)
{
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
}
else if (msg == WM_CLOSE)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void DrawBoard()
{
x = 10;
y = 10;
int count = 0;
for (int i = 0; i < 81; i++)
{
int BOX_ID = 200 + i;
boxes[i] = CreateWindow(TEXT("Edit"), NULL, WS_CHILD | WS_BORDER | WS_VISIBLE, x, y, 20, 20, Hwnd, (HMENU)BOX_ID, NULL, NULL);
x += 30;
count++;
if (count == 9)
{
y += 30;
x = 10;
count = 0;
}
}
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//structure to hold window specs
WNDCLASSEX Wc;
//allocate memory for window class
ZeroMemory(&Wc, sizeof(WNDCLASSEX));
//fill in neccessary info
Wc.cbSize = sizeof(WNDCLASSEX);
Wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
Wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
Wc.hInstance = hInstance;
Wc.lpfnWndProc = WindowProcedure;
Wc.lpszClassName = L"MyClass";
Wc.style = CS_HREDRAW | CS_VREDRAW;
//register class
RegisterClassEx(&Wc);
//load menu into handle
hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(ID_MENU));
//Create Window with class and create handle
Hwnd = CreateWindow(L"MyClass", L"Sudoku", WS_OVERLAPPEDWINDOW, 0, 0, 300, 340, NULL, hMenu, hInstance, NULL);
//DisplayWindow
ShowWindow(Hwnd, nCmdShow);
DrawBoard();
//structure to hold input stream
MSG msg;
//listen for input
while(GetMessage(&msg, Hwnd, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
also i read that wm_close is processed when i press the x button. well this message is being recieved even when i click the text boxes. and if you look at my case WM_Close.. i coded it to make a message box and give the user a chance to accept or not.... so when this happens after clicking on a text box i click no and another message box apperas asking again, i click no and it goes away but when i click the x button and i click no the window still dissapears .....
The problem is probably that WM_COMMAND is not handled properly.
The arguments received are as follows:
WORD code = HIWORD(wParam);
WORD id = LOWORD(lParam);
The problem is that code depends on the type of control you are using. For example, if it is a button it will be some of the BTN_* values, if it is an edit it will be EN_* and so on. But these values overlap badly, so you cannot use them in a single switch.
For example CBN_KILLFOCUS==4, but also LBN_SETFOCUS==4... Also menu items will get here a 0 and accelerators a 1. By the way, BN_CLICKED==0 and it looks like no other notification message uses 0, so you can use the same IDs in menus and buttons and it will just work. And accelerators too, with a bit of care... BN_PAINT==1, I think this one does not exist anymore, but you get the point...
Anyway, to your problem. My guess is that you have an EDIT that happens to have an ID equal to IDM_EXIT. Since you are not checking the HIWORD(wParam) you are quitting when you receive the EN_SETFOCUS on this control.
The solution is: First, always check both WORDs from wParam. Second, avoid collisions between menu options and ID controls, except maybe with buttons.
case WM_COMMAND:
switch (LOWORD(wParam))
That isn't quite good enough. Edit controls also send WM_COMMAND messages to notify their parent window about stuff going on. Like EN_UPDATE whenever you type a character. Or EN_SETFOCUS when they get the focus, sounds like your case when you see this go wrong when you click on them. These notifications are wrapped in a WM_COMMAND message.
You must therefore pay attention to where the WM_COMMAND message came from. The LPARAM argument tells you. If IDM_EXIT comes from a menu item then you must verify that LPARAM is 0. Check the MSDN library for details.
I have been experimenting with the WINAPI trying to learn it but the window I have created closes instantly. As you see when the W key is pressed or the left button is pressed it will close the program but when running it with no buttons being pressed it still closes.
#include <windows.h>
#include <windowsx.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Game", // title of the window
WS_OVERLAPPEDWINDOW, // window style
1, // x-position of the window
1, // y-position of the window
1800, // width of the window
1000, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// wait for the next message in the queue, store the result in 'msg'
while (GetMessage(&msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_MOUSEMOVE:
{
// Retrieve mouse screen position
int x = (short)LOWORD(lParam);
int y = (short)HIWORD(lParam);
// Check to see if the left button is held down:
bool leftButtonDown = wParam & MK_LBUTTON;
// Check if right button down:
bool rightButtonDown = wParam & MK_RBUTTON;
if (leftButtonDown == true)
{
//left click
//example lets close the program when press w
PostQuitMessage(0);
return 0;
}
}
case WM_KEYDOWN:
{
switch (wParam)
{
case 'W':
//w pressed
//example lets close the program when press w
PostQuitMessage(0);
return 0;
}
}
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
}
default:
break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
You're missing some break statements in your switch, so for example, if you get the WM_MOUSEMOVE message and the leftButtonDown != true, execution will fall through to WM_KEYDOWN, etc.
Eventually you get to case WM_DESTROY:, which will Post you a lovely QuitMessage.
As an aside, this would be very easy to spot by stepping through, statement-by-statement, in a debugger.
There is no break in your switch statement.
You end up exetuting
PostQuitMessage(0);
You could do something like this:
case WM_FOO:
{
if ( bar ) {
return 0;
}
break;
}
Don't detect clicks via the WM_MOUSEMOVE message, use the WM_MOUSEDOWN instead.
The problem is that your code is probably launched by you clicking on something, so when your window gets its first WM_MOUSEMOVE message, the button is still actually pressed. Code runs much faster than fingers..