I had practised winapi apps before, but never got this problem.
I have tried making a custom WindowProc to override default behaviour for WM_QUIT/WM_CLOSE/WM_DESTROY messages to do nothing, but the window still closes when I press ALT!
This is the entire code:
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
const LPCSTR className = "Class name";
WNDCLASS wc = {};
wc.style = 0;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = className;
RegisterClass(&wc);
HWND window = CreateWindow(className, "Title", WS_OVERLAPPEDWINDOW, 200, 200, 600, 400, nullptr, nullptr, nullptr, nullptr);
ShowWindow(window, SW_SHOW);
MSG msg = {};
while (msg.wParam != WM_QUIT)
{
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
I believe the window shouldn't close when I press ALT, but it does.
You're mixing the message field with the wParam field:
while (msg.wParam != WM_QUIT)
When you press ALT, the window receives a WM_KEYDOWN message whose wParam is the virtual keycode. It so happens that ALT has the same VK code as the constant for WM_QUIT (0x12).
In addition, you're also translating and dispatching WM_QUIT before quitting. You can deal with both of these by having a single level of indentation with an appropriate check:
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)
As pointed out in the comment, this is cleaner with GetMessage:
while (GetMessage(&msg, nullptr, 0, 0) > 0)
There are a few problems with your code.
First, you should be using GetMessage() and not PeekMessage(). The fundamental difference is that PeekMessage() is non-blocking, and therefore your program will use 100% of a CPU running that loop.
Also, because the inner loop may or may not loop through multiple messages in a row, checking things on the outer loop like you're doing will be a hit or miss and won't check all messages. You should be checking every message in the inner loop instead.
But in this case it's not necessary since this loop will end if the Window is closed anyway. This check you're doing is pointless.
There are a few other inconsistencies with your code, wc.hInstance should be hInstance, that one you received as parameter in WinMain(). There's no need to go looking for this information using APIs.
Also CreateWindow() should receive hInstance again as it's second to last parameter, not nullptr.
And finally as pointed out by chris' answer, you should be looking for the message type in msg.message and not in msg.wParam.
In addition to all that, this is no place to do a "custom WindowProc". If you want to do a custom WndProc then you should set wc.lpfnWndProc to your own function instead of DefWindowProc, and there you can define custom behavior to your Window.
For example:
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
case WM_DESTROY:
// do nothing
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Note there's no need to test WM_QUIT as this message will only happen if your application calls PostQuitMessage().
Related
I really have two questions; one is much broader than the other.
First, at the top of my code, I've used #define for naming my window class.
On MSDN they recommend using "const wchar_t CLASS_NAME[]" down in the main function, but I couldn't get that to work. Does this have something to do with Unicode (I've had problems with the lpsz stuff as well which seems to be related to that perhaps)? Anyway, point being, I can't figure out why that didn't work. It gave an error saying "Cannot assign const wchar_t to LPCSTR."
My second, and more broad question is, even in the state the code is in below, I debug with no errors and run, but no window opens. I cannot figure out what's missing. Any help would be much appreciated. Thanks.
#include <Windows.h>
#define CLASS_NAME TEXT("Window Class")
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR lpCmdLine, int nCmdShow)
{
MSG msg;
//Registering the window class.//
WNDCLASS wc = {};
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.lpszMenuName = NULL;
RegisterClass(&wc);
if (!S_OK)
{
return 0;
}
//Create Window//
HWND hwnd = CreateWindowEx(0, "CLASS_NAME", NULL, NULL, 100, 100, 800, 600, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
return 0;
}
//Show Window//
ShowWindow(hwnd, nCmdShow);
//Message loop//
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
return msg.wParam;
}
The code in question terminates right after registering a window class. It never even creates a window. The issue is the following attempt at error handling:
if (!S_OK)
{
return 0;
}
S_OK is a COM HRESULT value, that evaluates to the constant value 0. Consequently, !S_OK evaluates to true, so that branch is always taken, exiting the user-provided entry point. The compiler issues warning C4127 ("conditional expression is constant") at warning level 4 (which you should be using if at all possible).
Your first question is about mismatching character types (and encodings). The rules are documented under working with strings.
I have super simple WinApi program. Close button does not destroy process of the program. What should be added?
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hINSTANCE;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpstr,
int nCmdShow) {
// VARIABLES
TCHAR className[] = _T("win32api");
TCHAR windowName[] = _T("Protected Window");
WNDCLASSEX wcex;
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(NULL, IDI_INFORMATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = className;
wcex.hIconSm = LoadIcon(NULL, IDI_SHIELD);
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL, _T("Cannot register window"), _T("Error"), MB_OK | MB_ICONERROR);
return 0;
}
HWND hWnd = CreateWindow(className,
windowName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
300,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd) {
MessageBox(hWnd, _T("Call to register windows isn't working"), _T("Error"), NULL);
return 1;
}
hINSTANCE = hInstance;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, hWnd, 0, 0) != 0 || GetMessage(&msg, hWnd, 0, 0) != -1) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.lParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uInt, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
switch (uInt) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 20, _T("Text sample right here boyz."), _tcsclen(_T("Text sample right here boyz.")));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uInt, wParam, lParam);
break;
}
return 0;
}
If I add one more case
case WM_CLOSE:
PostQuitMessage(0);
break;
It doesnt close at all (pressing close button is not doing anything). Any other tips are very appreciated.
Your message loop is wrong, for two reasons:
you are calling GetMessage() twice. Don't do that! Think of what happens if you do. If the GetMessage() call on the left-hand side of || were to detect WM_QUIT (which it can't, see further below), it would return 0. Which would cause || to call the GetMessage() on the right-hand side, ignoring the WM_QUIT from the previous call and blocking the loop until a new message arrives later. You should call GetMessage() only once per loop iteration, and then act on the return value as needed:
BOOL bRet;
do
{
bRet = GetMessage(&msg, hWnd, 0, 0);
if (bRet == 0) break;
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
while (1);
However, you are also filtering messages by HWND (don't do that!), and so it will only return messages that are posted to the specified HWND via PostMessage(). PostQuitMessage() posts its WM_QUIT message to the input queue of the calling thread itself, not to the HWND. So the filtering will never see the WM_QUIT message. In order for WM_QUIT to break the loop, you need to stop filtering by HWND altogether, or at least make an unfiltered call periodically.
Your message loop should look like this instead. This is the standard message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Note in this case, there is no need to handle -1 from GetMessage()!.
To close the app, you need to destroy the window. Typically that's done in one of two ways:
Have your WM_CLOSE handler call DestroyWindow.
Have the DefWindowProc handle the message after you do your thing.
Your WndProc is a little out of the ordinary. I usually see them written as:
{
switch(msg)
{
case WM_CLOSE:
// prompt user, clean stuff up, etc.
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
// Other cases here. Most will fall out of the switch so that
// DefWindowProc can handle them (for other system notifications).
// Only return from the case if you really don't want anybody else
// to handle the message.
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
For specifics on WM_CLOSE, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx, which specifically says that you must call DestroyWindow or let DefWindowProc do it.
I am working on window creation with Win32 API and I'm having a problem with this part:
GetMessage(&message, NULL, 0, 0);
My problem is that when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work; for example, when I try to close the window, it only hides and doesn't close.
Here is the full code:
#include <windows.h>
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS window;
window.cbClsExtra = NULL;
window.cbWndExtra = NULL;
window.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
window.hCursor = LoadCursor(hInst, IDC_ARROW);
window.hIcon = NULL;
window.hInstance = hInst;
window.lpfnWndProc = WinProc;
window.lpszClassName = "WINDOW";
window.lpszMenuName = NULL;
window.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&window);
HWND hwnd = CreateWindow("WINDOW", "Win32 Window Application", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return 1;
}
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
default:
break;
}
return DefWindowProc(window, message, wParam, lParam);
}
"... when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work."
Thread messages are not sent to a window; they're posted to the thread message queue with a NULL window handle, and will NOT be picked up with a GetMessage() loop tailored to a specific window handle.
Ex: PostQuitMessage() posts a thread message; not a window message. You need the NULL. From the GetMessage() docs:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
Recently I came about a strange difference between the two Win32 API calls "PostMessage" and "SendNotifyMessage" (at least noticed on Win7 64bit SP1):
An owned top-level window of another process seems not to receive messages broadcasted (HWND_BROADCAST) with "PostMessage" while it receives messages broadcasted with "SendNotifyMessage" in its WndProc.
The sent message have been registered with the help of a call to "RegisterWindowMessage".
Even using Spy++, I cannot see the message arriving when using "PostMessage". In addition, I want to mention, that if I send the message directly to the specific HWND with "PostMessage", it arrives as expected. So it looks like the windows-internal implementation of "PostMessage" just skips my window when iterating for execution of the broadcast.
Reading the respective MSDN documentation, I cannot see any statement about this difference and I am wondering if this is a bug in PostMessage or in SendNotifyMessage and if I can rely on SendNotifyMessage to continue to show this behavior in future versions of Windows.
So does someone have a plausible explanation why the both functions treat the broadcasts differently in this situation?
In addition, I would want to ask if there is any way to still use PostMessage to broadcast to an owned top-level window, because I would prefer to post the message because I would prefer to not skip the message queue (which is what SendNotifyMessage does).
In case you are curious why I want to reach a top-level owned window: in WPF, windows are hidden from the taskbar (Window.ShowInTaskbar property) by making them owned top-level windows with a hidden owner window.
Thanks a lot in advance for any ideas or comments on this topic.
Attachment: here a sample showing the behavior ... simply build it, and start it two times ... the second process should make a message show up in the first one.
Here is also a link to the complete solution including a build EXE: Link to the complete VS solution
#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>
HWND hwndMain = NULL;
HWND ownerHwnd = NULL;
std::vector<std::string> theOutput;
UINT MyRegisteredMessage1 = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc = NULL;
if (message == MyRegisteredMessage1 && wParam != (WPARAM) hwndMain)
{
if (lParam == (LPARAM) 1)
theOutput.push_back("Got a 'MyRegisteredMessage1' via PostMessage");
if (lParam == (LPARAM) 2)
theOutput.push_back("Got a 'MyRegisteredMessage1' via SendNotifyMessage");
InvalidateRect(hwndMain, NULL, TRUE);
}
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for(size_t i = 0, pos = 0; i < theOutput.size(); ++i, pos += 20)
TextOutA(hdc, 0, pos, theOutput[i].c_str(), theOutput[i].size());
EndPaint (hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProcHidden(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASSA wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProcHidden;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);;
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MyOwnerWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.lpszClassName = "MyOwnedWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
}
ownerHwnd = CreateWindowA("MyOwnerWindowClass", "OwnerWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, (HWND) NULL,
(HMENU) NULL, hInstance, (LPVOID) NULL);
hwndMain = CreateWindowA("MyOwnedWindowClass", "OwnedWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, ownerHwnd,
(HMENU) NULL, hInstance, (LPVOID) NULL);
// only show the "real" window
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
MyRegisteredMessage1 = RegisterWindowMessageA("MyRegisteredMessage1");
char infoText[256];
_snprintf_s(infoText, 256,
"HWND = %X, registered message code for 'MyRegisteredMessage1' = %d",
hwndMain, MyRegisteredMessage1);
theOutput.push_back(infoText);
InvalidateRect(hwndMain, NULL, TRUE);
PostMessage(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 1);
Sleep(1000);
SendNotifyMessageA(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 2);
while( (bRet = ::GetMessage( &msg, NULL, 0, 0 )) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
You may need to register your message using RegisterWindowMessage() -- see the Remarks section of this MSDN article
Just adding this here for info..
I was able to get around this issue in c# by registering an IMessageFilter object on the Application level. PreFilterMessage on this object will receive the message and I can handle it from there.
public class FooMessageFilter : IMessageFilter
{
uint UM_FOO = 0;
public event EventHandler OnFoo;
public FooMessageFilter()
{
UM_FOO = Win32.RegisterWindowMessage("UM_FOO");
}
public bool PreFilterMessage(ref Message m)
{
if(m.Msg == UM_FOO)
{
if(OnFoo != null)
OnFoo(this, new EventArgs());
return true;
}
return false;
}
}
I then added this message filter to the Application context in my owned top-level form's constructor.
public partial class Form1 : Form
{
private FooMessageFilter fooFilter = new FooMessageFilter();
public Form1()
{
InitializeComponent();
// Register message filter
Application.AddMessageFilter(fooFilter);
// Subscribe to event
fooFilter.OnFoo += HandleFoo;
}
private void HandleFoo(object o, EventArgs e)
{
MessageBox.Show("Foo!");
}
}
From there it was just a matter of hooking up events in my top-level window to the message filter. This was necessary because of the need to adhere to current architecture, and the message originating from a third party process.
The documentation page(s) for PostMessage() mention that integrity level restrictions apply:
Starting with Windows Vista, message posting is subject to UIPI. The thread of a process can post messages only to message queues of threads in processes of lesser or equal integrity level.
There is no mention of such restrictions on SendNotifyMessage(). Since you don't check the return value of either, you could be running into that, and you wouldn't know it.
So i have this code that creates two windows:
WNDCLASS wc;
wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = StaticWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = nullptr;
wc.lpszClassName = _T("Move Engine");
RegisterClass(&wc);
m_hWnd = CreateWindow("Move Engine", "Move Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, usWidth, usHeight, nullptr, nullptr, wc.hInstance, this);
// Create the settings window
wc.lpszClassName = _T("Settings");
RegisterClass(&wc);
s_hWnd = CreateWindow("Settings", "Settings", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 20, nullptr, nullptr, wc.hInstance, this);
ShowWindow(m_hWnd, SW_SHOW);
ShowWindow(s_hWnd, SW_SHOW);
The problem is, application terminates each time i close one of the windows. What i need is two separate windows; main application window and the one that gives access to various settings of the application - therefore closing the settings window should not affect the main window. Is there any way to achieve this? Thank you.
P.S. My WinMain function is modified like this:
int WINAPI WinMain(HINSTANCE a_hInstance, HINSTANCE a_hPrevInstance, LPTSTR a_lpCmdLine, int a_iCmdShow)
{
int iReturnCode;
// Initialise the engine.
if (!MyEngine.InitInstance(a_hInstance, a_lpCmdLine, a_iCmdShow)) return 0;
// Begin the gameplay process and return when the application due to exit
iReturnCode = MyEngine.StartEngine();
// Return the correct exit code.
return iReturnCode;
}
and StaticWndProc looks like this:
LRESULT CALLBACK CMoveEngine::StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
// If this is a create message, trap the 'this' pointer passed in and store it within the window.
if (Message == WM_CREATE) SetWindowLong(hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
// Obtain the correct destination for this message
CMoveEngine *Destination = (CMoveEngine*)GetWindowLong(hWnd, GWL_USERDATA);
// If the hWnd has a related class, pass it through
if (Destination) return Destination->DisplayWndProc(hWnd, Message, wParam, lParam);
// No destination found, defer to system...
return DefWindowProc(hWnd, Message, wParam, lParam);
}
Message loop:
int CMoveEngine::StartEngine()
{
MSG msg;
// Start main loop
while (true)
{
// Did we recieve a message, or are we idling?
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage (&msg);
}
else
{
}
}
return 0;
}
You register both window classes with the same lpfnWndProc. Thus, all the messages (including mouse clicks and keyboard presses) will be directed to the same function, StaticWndProc.
Thus, when this function receives the WM_CLOSE message, it will respond by destroying the window, which stops the message pump and terminates the program. Since both windows have the same message handler function, they will both respond in the same way.
The solution is to define two different WndProc methods, one for each of your windows, or special-case the handling of the WM_CLOSE message, only calling DefWindowProc for the window that you want to allow to close the entire application.
Make your StaticWndProc handle either WM_CLOSE or WM_DESTROY: In that case it should decrement an openedWindows-counter. If that counter reaches zero call PostQuitMessage.