win32 WM_NOTIFY Vs WM_COMMAND - winapi

This is an theory question released to control messages in win32 api.
What is the difference between WM_COMMAND and WM_NOTIFY?
When do we need to handle these messages and when do we choose to ignore them?.
This doubt arises because both these messages are sent when the user interacts with an control and both send the same information to the window procedure that is event type,control I'd and control handle either as an structure
(LPNMHDR in case of WM_NOTIFY) or directly (LPARAM handle to control and LOWORD (WPARAM) in case of WM_COMMAND)
With all these similarities why do we still use both together and not just depracate one ?

We use both due to reasons of backwards compatibility. New controls tend to use WM_NOTIFY (it is far more capable), but there are existing controls that send WM_COMMAND and MS is not going to change that.
I say that WM_NOTIFY is more capable because of its lParam being an NMHDR *, which if the NMHDR is the first member of a POD type (or standard layout in modern C++ terms) then you can cast the lParam to the actual type the control sent. All WM_COMMAND can provide is a command code and window handle.
As for when to handle them that is strictly a matter of need, if you need to deal with a particular action on part of a particular control then you handle the message, if not then you don't.

Related

Child windows does not receive WM_DESTROY?

I packaged the winapi CreateWindowEx into a simple class. Since every window sharing a same wndProc(hwnd,msg,wparam,lparam), I put every window created by CreateWindowEx into a map to distribute msg, like this:
wndProc(hwnd, msg, wparam, lparam){
if(map[hwnd]!=nil){
switch(msg){
map[hwnd].wndProc(...)
}
}
}
And each time a window or its parent window being destroyed, remove it from the map:
case WM_DESTROY: delete(map, hwnd)
But things like buttons do not receive WM_DESTROY. I printed all msg in WM_NOTIFY and WM_COMMAND but i got noting.
So how can I remove those child windows form the map at the right time? Or a way distribute the msg without creating a hwnd map?
They certainly do get that message. But their window procedure is inside Windows, not inside your program. So you never see it. Something you can read in the MSDN documentation, note how WM_DESTROY doesn't get any special treatment. Nor generate a notification that your parent window can see.
Short from automatically removing all the child windows when you see the parent window destroyed, subclassing controls is a pretty standard requirement for C++ class library wrappers. Best to not invent your own btw, this has been done many times already.
So how can I remove those child windows form the map at the right time?
You have to subclass every window you create, either with SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubClass(), then you will receive all of the WM_DESTROY messages.
Or a way distribute the msg without creating a hwnd map?
Frameworks like ATL and VCL handle that by dynamically allocating a thunk for each window and putting the object pointer right in the thunk, then use the thunk as the window procedure. That way, whenever the thunk is called, it passes the message directly to its associated object.

Modeless dialog keyboard handling (winapi)

I've got an application with a main window which has a bunch of controls, including the spacebar, which is handled by a simple method called onSpacebar(). On top of that main window, I've got a persistent modeless dialog.
I need the spacebar to behave the exact same way, regardless of whether the dialog has focus, or the main window has focus.
This dialog is backed by a DialogProc which looks something like this:
BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_NOTIFY:
std::cout<< "WM_NOTIFY" <<std::endl;
switch(LOWORD(wParam))
{
// which component caused the message?
case COMP_TREE:
if(((LPNMHDR)lParam)->code == NM_DBLCLK){
onDoubleclk()
}
//...
break;
// other components...
}
break;
case WM_CLOSE:
// the dialog can only be closed when the whole app is closed
//EndDialog(hDlg, IDCANCEL);
return TRUE;
case WM_DESTROY:
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
From what I gather, I should call my onSpacebar() method from within the DialogProc, similarily how I handle the double click. I can see that WM_NOTIFY is received by the dialog when the spacebar is pressed (the phrase WM_NOTIFY is printed to cout), but I can't seem to differentiate the spacebar notification from the other numerous notifications the dialog receives.
Please, tell me how to recognize that the particular WM_NOTIFY was in response to a spacebar keypress.
A WM_NOTIFY message is not the standard way that a window processes key press events. When a key is pressed, your window should be receiving WM_KEYDOWN, WM_KEYUP, and possibly WM_CHAR messages. WM_NOTIFY serves an entirely different purpose altogether: passing on a message from a common control to its parent window.
So the fact that you're receiving a WM_NOTIFY message in response to a key press is a fairly unusual thing, explainable when you understand how focus works (which is key to solving your ultimate question).
In Windows, only one window can be focused at a time, and the currently focused window is the one that receives all keyboard input. Thus, if a dialog box has the focus, it will receive key press notifications. If a child control on that dialog box has the focus, it (not its parent dialog) will receive key press notifications. And there is a focusable child control on a dialog box, it will always receive the focus in preference to its parent dialog, therefore it will also always receive key press notifications.
So the likely explanation for your curious WM_NOTIFY messages is that one of the common controls on your dialog has the focus, it is receiving the space key press event, and after processing it, passing on a notification to its parent window (your dialog) in the form of a WM_NOTIFY message. As you might imagine, this is not a reliable method of detecting that the space bar has been pressed.
Instead, you need to figure out some way of trapping key press notifications before they get sent to the focused control. To do that, you'll need to modify your application's message loop to trap WM_KEYDOWN or WM_KEYUP messages before calling either DispatchMessage or IsDialogMessage.
If the key event corresponds to the space bar, you will call your onSpacebar function and indicate that the message was handled, preventing it from being passed on and processed by another window.
If the key event does not correspond to the space bar, then you will need to handle the message as you usually would, ensuring that it does get passed on and processed by the other window.
Since this approach filters out space key presses at a global level, it solves both the problems of child controls on a dialog stealing the key press and the other modeless dialog. However, you do need to be careful because it's very easy to screw things up so that the user can't navigate your dialog using the keyboard at all.
More fundamentally, I think your idea to handle presses of the space bar is fundamentally flawed. The logic of certain common controls basically requires that they process presses of the space bar. For example, consider a textbox: if you filter out all presses of the space bar at a global level, the user will never be able to type a space in a textbox. If you insist on handling the space bar, you will need to check the focused control in your global handler, and if it's a textbox (or other common control that you wish to receive spaces), pass it on; otherwise, handle it yourself.
Honestly, what I'd do instead is choose a more unique key combination (like, I don't know, Ctrl+Space) and set that up as an accelerator. Presumably, your global message loop is already processing accelerator keys by calling the TranslateAccelerator function, so that would take care of all the dirty work for you. No code is even required—you'd do everything simply by editing the accelerators resource file in your project. The MSDN documentation on keyboard accelerators is here, but you'll probably have an easier time consulting your favorite book on Visual C++.

What is the correct solution to support IAccesible interface for caret movement in text editors?

I want to implement a text editor from scratch which supports IAccessible interface. I am using MFC and Win32 API.
When the caret position change in the standard text editors like Notepad, the corresponding letter, word or line to the caret movement is pronounced by client tools like Narrator, JAWS or etc. I don't know how to implement this feature. I search the internet and read the MSDN documentation.
I read in http://msdn.microsoft.com/en-us/library/dd317978.aspx and http://msdn.microsoft.com/en-us/library/dd373892.aspx that client asks for caret by AccessibleObjectFromWindow method from OS, and OS send WM_GETOBJECT to the application. WM_GETOBJECT messages received in the corresponding window callback function, but hWnd for caret movement event is NULL. I checked the thread message queue, but WM_GETOBJECT didn't receive at all in the thread message queue.
One method that has worked somewhat, but not the correct solution is to call
NotifyWinEvent( EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_CLIENT, CHILDID_SELF )
when the caret move by user. And when client ask for the changed name, I return the corresponding text related to the caret movement.
HRESULT CMyEditor::get_accName(VARIANT varChild, BSTR *pszName)
{
*pszName = SysAllocString( L"CORESPONDING TEXT TO THE CARET MOVEMENT" );
return S_OK;
}
The client would use the SetWinEventHook() function to track the following events of the caret :
EVENT_OBJECT_CREATE
EVENT_OBJECT_DESTROY
EVENT_OBJECT_SHOW
EVENT_OBJECT_HIDE
EVENT_OBJECT_LOCATIONCHANGE
EVENT_OBJECT_FOCUS
If you use a custom control, you need to use NotifyWinEvent() to fire those events yourself, especially EVENT_OBJECT_LOCATIONCHANGE which should trigger the narration.
When the client handle thoses events, it should access the IAccessible interface of the object he's tracking using AccessibleObjectFromEvent().
As you say, Microsoft Active Accessibility would handle this call and send an WM_GETOBJECT message to the corresponding window depending on the handler given to AccessibleObjectFromEvent() (which should be the handler contained in the event).
When you receive the WM_GETOBJECT for the caret you should return the corresponding IAccessible interface which would report the proper accRole and accLocation.
If you're not receiving the right WM_GETOBJECT message it may be because you're not triggering the right events.
You can use the Accessible Event Watcher to check if the right events are sent :
http://msdn.microsoft.com/en-us/library/windows/desktop/dd317979%28v=vs.85%29.aspx
See the Developer's Guide for Active Accessibility Servers on MSDN :
http://msdn.microsoft.com/en-us/library/windows/desktop/dd318053%28v=vs.85%29.aspx
Edit
Also, if you're using the standard caret provided by Riched20.dll (in a Rich Edit as instance), the documentation stipulate that unlike other UI elements, it does not have an associated window handle.

How to track focus changes between several TrackBars using WTL?

I'm trying to track focus changes between several TrackBars (aka sliders, all within the same window) using WTL.
So far I've tried a
MESSAGE_HANDLER(WM_SETFOCUS, func)
as well as one
COMMAND_HANDLER(IDC_SLIDERn, WM_SETFOCUS, func)
for each slider without success.
The about trackbar controls page on msdn says: "WM_SETFOCUS Repaints the trackbar window." ..
edit:
I now have derived the sliders from my own class where I handle WM_SETFOCUS with MESSAGE_HANDLER and notify the parent window by posting the message to it with m_hWnd as lParam so I can check in the parent which slider gained focus.
This works, but is there a more elegant way to do this?
WM_SETFOCUS is sent to the specific window that gets focus, not to the parent, as you've discovered.
However, there's an alternate tecnique that you can use to avoid subclassing; most controls (specifically the 'common controls', which includes sliders) will send a WM_NOTIFY to their parent when certain events happen, allowing the parent to handle these events for a collection of children.
In your case, try listening for WM_NOTIFY message at the parent window, specifically checking for the case where the notification ID is NM_SETFOCUS - from MSDN:
Notifies a control's parent window that the control has received the input focus. This notification code is sent in the form of a WM_NOTIFY message.
...which sounds like what you are looking for. Apparently ATL supports these in the message map using NOTIFY_HANDLER, something like:
NOTIFY_HANDLER(IDC_SLIDERn, NM_SETFOCUS, func)
Note that this works because the Win32 Common Controls support this sort of notification forwarding; if you were to use some other custom control instead, you may not get these notifications and would have to resort to subclassing. But for the common controls, this is the simplest way of doing it.
You don't need to derive your class, subclassing with CContainedWindowT is just fine.
BEGIN_MSG_MAP_EX(CDialog)
// ...
ALT_MSG_MAP(IDC_TRACKBAR)
MSG_WM_SETFOCUS(OnControlSetFocus)
MSG_WM_KILLFOCUS(OnControlKillFocus)
END_MSG_MAP()
// ...
CContainedWindowT<CTrackBarCtrl> m_TrackBar;
// ...
CDialog() :
m_TitleListView(this, IDC_TRACKBAR)
// ...
LRESULT OnInitDialog(HWND, LPARAM)
{
// ...
ATLVERIFY(m_TrackBar.SubclassWindow(GetDlgItem(IDC_TRACKBAR)));
// ...
// ...
LRESULT OnControlSetFocus(...) { }
LRESULT OnControlKillFocus(...) { }

What Windows message is sent to repaint a partially occluded window?

I know that WM_PAINT tells a window that it needs to repaint itself entirely, but apparently that's not the message that gets sent when it's been covered partially and then the window that was in front of it is no longer in the way and it needs to repaint the dirty portion. Does anyone know what message is sent in this case?
EDIT: Found the problem:
The issue involved a Delphi control I wrote to embed a SDL rendering surface on a Delphi form. SDL has an API to build its renderer on another window's HWND, and it marks it as a "foreign window".
SDL usually handles WM_PAINT internally, so I ended up having to add some code to SDL's WindowProc to forward the message on to the external WindowProc if it's a foreign window. That was working sometimes, but it turns out there was a glitch that was stripping the foreign window flag from the window's data structure, so it was swallowing the message instead of passing it on to my app. Once I fixed that, I started getting the WM_PAINT messages all the time.
Why do you say it's apparently not? WM_PAINT should be called for partial redraws (the updated rect is returned by BeginPaint or GetUpdateRect). If it doesn't appear to be getting called, there may be a bug elsewhere in your app that's preventing it. What are you seeing that leads you to believe that it's not working?
WM_PAINT is sent to indicate that some portion (including the entirity) of the window needs to be repainted.
Call GetUpdateRect() to get a rectangle that bounds the region to be updated. This information is also included in the PAINTSTRUCT (as the rcPaint field) passed to BeginPaint().
The BeginPaint() function returns the rect that requires validation in its 2nd parameter: http://msdn.microsoft.com/en-us/library/dd183362(VS.85).aspx
case WM_PAINT:
{
PAINTSTRUCT psPaint;
HDC hdc = BeginPaint( hwnd, &psPaint );
// psPaint.rcPaint contains invalidated area
EndPaint (hwnd, &psPaint);
}
return 0;
Look at psPaint.rcPaint : http://msdn.microsoft.com/en-us/library/dd162768(VS.85).aspx
I'm pretty certain the Win32 API uses WM_PAINT even for partial repaints. From MSDN:
The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window. [My italics].
That link has the full detail on WM_PAINT but if, as you say, the WM_PAINT message is not being sent for partial redraws, the Spy++ is the tool you need to find out for sure.
Take a look at WM_PRINTCLIENT. There are some circumstances when WM_PAINT is not sent and a WM_PRINTCLIENT message is sent instead. (AnimateWindow for example.)

Resources