Broadcasting to owned window with PostMessage vs. SendNotifyMessage - c++

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.

Related

WINAPI application closes when I press either ALT, why?

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().

Why is GetMessage exiting my program without sending any message?

here is my problem. I'm trying to create a windowless program that still uses trayIcon and Hooks, so I need to use messages (or not?) but when I use them, I don't know how to free my memory when I kill my process. Even classes's destructors aren't called. Here is a test main:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR pCmdLine, int nCmdShow)
{
MSG msg;
OutputDebugStringW(L"Start.\n");
while (GetMessageW(&msg, NULL, NULL, NULL) > 0)
{
OutputDebugStringW(L"one.\n");
TranslateMessage(&msg);
OutputDebugStringW(L"two.\n");
DispatchMessage(&msg);
OutputDebugStringW(L"three.\n");
}
OutputDebugStringW(L"end.\n");
return 0;
}
As I saw in the doc, the GetMessage() call should return 0 or less to exit, but when I run it, and then shut it down, I get only the "Start." log, and I don't understand why. And if there is no way to get through this loop, then how can i call my destructors? I do have a window for my trayIcon that doesn't receive any message, maybe i should pass it as parameter to GetMessage()?
PS: my project uses trayIcons and Hooks, and when I run it with the same debug prints, it display once the 4 first strings, but nothing at shutdown, not even the "end." string.
EDIT: my (terrible) trayIcon creation:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugStringW(L"Message!\n");
switch (uMsg)
{
case WM_DESTROY:
OutputDebugStringW(L"Close message received\n");
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
case WM_RBUTTONUP:
{
OutputDebugStringW(L"Trying to open Context menu\n");
POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
break;
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
bool CreateNotifyIcon(HINSTANCE& hInstance)
{
NOTIFYICONDATA notif = {};
static const wchar_t class_name[] = L"ExtendClass";
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = WindowProc;
wx.hInstance = hInstance;
wx.lpszClassName = class_name;
RegisterClassEx(&wx);
HWND win = CreateWindowEx(0, class_name, L"Windows Extend", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (win == NULL)
OutputDebugStringW(L"failed to create window\n");
ZeroMemory(&notif, sizeof(NOTIFYICONDATA));
notif.cbSize = sizeof(NOTIFYICONDATA);
notif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
// Need to find a non-busy uID
notif.uID = 44421;
notif.hWnd = win;
StringCchCopy(notif.szTip, ARRAYSIZE(notif.szTip), L"Access Windows Extend options.");
StringCchCopy(notif.szInfo, ARRAYSIZE(notif.szInfo), L"Access Windows Extend options.");
StringCchCopy(notif.szInfoTitle, ARRAYSIZE(notif.szInfoTitle), L"Windows Extend");
notif.hIcon = (HICON)LoadImage(NULL, L"./Luma.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
if (!Shell_NotifyIcon(NIM_ADD, &notif))
{
OutputDebugStringW(L"failed to create notifIcon\n");
return false;
}
return true;
}
GetMessage() does not return until it receives a window/thread message, or has a message to synthesis. In the code you have shown, you are not creating any windows, not posting any thread messages to yourself, and not creating any hooks that require a message loop, so there is nothing for GetMessage() to do. It is blocked indefinitely. That is why you are only seeing your start message and nothing else happens until you forcibly kill the program.
Since you intend to display a system tray icon, you need a window for it, even if just a hidden window, in order to receive notifications about user interaction with the icon. Your GetMessage() loop will handle messages for all windows created in the same thread as the loop, since you are setting the hWnd parameter to NULL instead of a specific window. Once you create your tray icon window, the loop will receive messages for it just fine. You can then provide your tray icon with a popup menu that contains an item that will exit your message loop when clicked, thus allowing you to exit from WinMain() gracefully, invoke destructors, etc.
GetMessage returns -1 if an error occurs. So you should do while != 0 generally. But if you are not seeing "end", then your application is probably crashing.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx

How to pass WM_KEYDOWN message to IWebBrowser2 instance?

I'm loading an embedded browser within a parent application using the IWebBrowser2 interface. My code is compiled as a dll, i.e. the browser component is dynamically loaded at runtime via a plugin interface.
The problem I'm having is that applications that load my dll are trapping certain keydown messages, and they are therefore not reaching my IWebBrowser2 instance.
I am therefore capturing these messages using the SetWindowsHookEx() API in my dll.
How can I then forward the WM_KEYDOWN or WM_CHAR messages to my IWebBrowser2 instance such that they could e.g. be used to enter text in a focused text box within the browser?
I believe the problem sits in host application's message queue implementation, where some messages are handled instead of delivering, for example to implement hotkeys. Since you can't change their code, hooking the message queue sounds like a reasonable approach.
The following code snippet demonstrates both problem and solution:
#define WINDOW_CLASS _T("StackOverflow_41911104")
HINSTANCE g_Instance = 0;
HHOOK g_Hook = 0;
HWND g_TargetWindow = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
HWND CreateMainWindow()
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = g_Instance;
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = WINDOW_CLASS;
wcex.hIconSm = nullptr;
ATOM windowClass = RegisterClassExW(&wcex);
HWND mainWindow = CreateWindowW(WINDOW_CLASS, WINDOW_CLASS, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, nullptr, nullptr, g_Instance, nullptr);
g_TargetWindow = CreateWindow(_T("Edit"), nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE, 0, 0, 300, 300, mainWindow, (HMENU)1000, g_Instance, nullptr);
return mainWindow;
}
HACCEL CreateAccelerators()
{
ACCEL acceleratorsList[] =
{
{FVIRTKEY, 'R', 1000},
{FVIRTKEY, 'T', 1001},
};
return CreateAcceleratorTable(acceleratorsList, _countof(acceleratorsList));
}
void ProcessHookMessage(MSG* a_Message)
{
// Only affect our window and its children
if ((g_TargetWindow != a_Message->hwnd) && !IsChild(g_TargetWindow, a_Message->hwnd))
return;
// Deliver the message directly
TranslateMessage(a_Message);
DispatchMessage(a_Message);
// Do not allow to process this message the second time
a_Message->message = WM_NULL;
}
LRESULT CALLBACK Hook_GetMsgProc(int a_Code, WPARAM a_WParam, LPARAM a_LParam)
{
if ((HC_ACTION == a_Code) && (PM_REMOVE == a_WParam))
ProcessHookMessage((MSG*)a_LParam);
return CallNextHookEx(g_Hook, a_Code, a_WParam, a_LParam);
}
void InstallHook()
{
g_Hook = SetWindowsHookEx(WH_GETMESSAGE, Hook_GetMsgProc, g_Instance, GetCurrentThreadId());
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int)
{
g_Instance = hInstance;
HWND mainWindow = CreateMainWindow();
HACCEL hAccelTable = CreateAccelerators();
InstallHook();
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
// The problem lurks here: some messages are handled directly and never reach the target window
if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
In this code snippet, if you comment out InstallHook() call, you won't be able to print R and T, because these keys are used for accelerator table. However, with InstallHook(), the hook forces normal message queue behavior and everything works as normal.
The suggested hook code have the following points of interest:
It only affects your window and nothing else
It works the same way as the usual message queue would, instead of messing with SendMessage / PostMessage
It prevents double-effect of messages that were not intercepted by hosting application
It sounds like the root problem is that your window is on a different thread than the host application's window, which can confuse the focus state. You can easily get into situations where the host window and the hosted window both believe they have the focus.
The solution to that is to create your window on the same thread as the parent window, and, if that's not possible (e.g., because of the plugin model or because the plugin is run in a separate process), use AttachThreadInput.
I haven't used a web browser control in many years, but I recall one project long ago, where we had similar issues when we added the web browser control as a child of a window in another process. Using AttachThreadInput there solved a lot of bugs. The drawback was that a bug in either thread (like a hang) effectively hangs both threads. We also had to be careful to detach the threads during teardown.
It seems, this is a bit trickier than the usual sending a message:
Firstly, you will need to obtain the In place active object (https://msdn.microsoft.com/en-us/library/windows/desktop/ms691299(v=vs.85).aspx) of your web browser and then call the TranslateAccelerator (https://msdn.microsoft.com/en-us/library/windows/desktop/ms693360(v=vs.85).aspx) on it.
Some very high level pseudocode would look like:
HRESULT hr;
IOleInPlaceActiveObject* pIOIPAO;
hr = webBrowser2->QueryInterface(webBrowser2,
&IID_IOleInPlaceActiveObject, (LPVOID*)&pIOIPAO);
if (SUCCEEDED(hr))
{
result = pIOIPAO->lpVtbl->TranslateAccelerator(pIOIPAO, msg);
}
where msg is the message (MSG) you should populate accordingly, and webBrowser2 is your IWebBrowser2.
PS: Didn't try this code, use at your own risk :)

WM_Quit message being sent when i click on textbox

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.

c++ / application with multiple separate windows

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.