I have an acrobat plugin which spins up a thread. From that thread I somehow need to get back onto the UI thread. I do have the HWND but if I do a PostMessage how do I get the host application's WNDPROC to sort of call bank into an handler I own. I guess my question is, is there anyway to conceptually do something like
RegisterWndMsgHandler( HWND, MSG, CALLBACK );
You can subclass the HWND by replacing its message handler with your own via SetWindowLongPtr(GWL_WNDPROC). By sure to call GetWindowLongPtr(GWL_WNDPROC) to retreive the original handler, and then have your handler pass any unhandled messages to CallWindowProc() so the original handler can process them.
Related
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.
I'm handling ESC key in my application and when this key is received I wish to close the current window.
Should I simply call DestroyWindow(hWnd) or should I SendMessage(WM_CLOSE, hWnd, 0, 0), or should I be closing the current window in some different way?
You should PostMessage(hWnd, WM_CLOSE, 0, 0). It puts the WM_CLOSE message into the window's message queue for processing, and the window can close properly as the message queue is cleared.
You should use PostMessage instead of SendMessage. The difference is that PostMessage simply puts the message into the message queue and returns; SendMessage waits for a response from the window, and you don't need to do that in the case of WM_CLOSE.
It is up to you which you use. Should the Esc key act just like clicking the close button, or should it definitely destroy the window?
The default implementation of WM_CLOSE (as found in DefWindowProc) calls DestroyWindow, so if you're not handling WM_CLOSE specifically then one is as good as another. But WM_CLOSE doesn't necessarily have to call DestroyWindow, though, so if the window in question handles it then it could do something else. For example, it could pop up a "Are you sure?"-type message box, or simply do nothing. DestroyWindow will bypass all of that.
Either PostMessage or SendMessage WM_CLOSE
When a standard window control (e.g. an "EDIT" control) is created, its WNDPROC is defined as part of the window class (i.e. "EDIT" has a specific WNDPROC that is designed to make the window display & behave as an edit-control).
MFC allows you to interact with such controls via their wrapper classes, such as a CEdit wrappers the specialized messages for an "EDIT" window control.
MFC further allows you to bind an instance of an "EDIT" window to a C++ subclass of CEdit, say a CMyEdit, where you can override inherited virtual functions of CEdit and CWnd, and you can define a message table to gain access / override messages sent to the window instance itself.
There is CWnd::Default(), which calls this->DefWndProc with the current message arguments. This appears to lookup the WNDPROC for the HWND it is associated with. So is this the correct answer: call DefWndProc() (or equally, Default()), which will hand it off to the WNDPROC of the windows control?
Obviously, this is different than other message table handlers which can return FALSE to indicate that they didn't handle the message, and MFC will automatically route the message up the class inheritance hierarchy to the next message handler for this message, or, I assume, to Default() to be handled by the native WNDPROC?
If I define an arbitrary message handler, say for WM_SETTEXT, what is the correct way to pass this message on to the "EDIT" WNDPROC?
I'd also love to know if there is a way to pass the message up to a superclass (C++ class hierarchy) for handling? Many OnXXX style handlers do have a way to do so, but is there a mechanism that works for ON_MESSAGE handlers?
class CDynamicMenuControlEdit : public CEdit
{
...
LRESULT OnSetText(WPARAM wParam, LPARAM lParam);
...
}
BEGIN_MESSAGE_MAP(CDynamicMenuControlEdit, CEdit)
...
ON_MESSAGE(WM_SETTEXT, OnSetText)
...
END_MESSAGE_MAP()
LRESULT CDynamicMenuControlEdit::OnSetText(
WPARAM wParam, // not used; must be zero
LPARAM lParam // window-text string (LPCTSTR)
)
{
if (m_bHasFocus)
{
// do normal thing
// !!! THIS IS MY QUESTION: IS THIS CALLING EDIT's WNDPROC, or ::DefWinProc()? !!!
return DefWindowProc(WM_SETTEXT, wParam, lParam);
}
...
}
Clarification
You can have multiple MFC subclasses at the C++ level -
so C inherits B inherits A, where A is an MFC class (e.g. CEdit).
Each of those can have an MFC message table - i.e. BEGIN_MESSAGE_MAP ... END_MESSAGE_MAP which can each have a handler for any arbitrary windows message, such as WM_MESSAGE(WM_SETTEXT, OnSetText) - and that OnSetText member is not necessarily virtual - just a static member (each MFC subclass can route that message in any arbitrary way).
My question is - since a WM_MESSAGE dispatch entry doesn't have a return value, how do I allow MFC to walk up the MFC dispatch tables from C to B to A before handing back to the real windows 'EDIT' class's wndproc?
Or are all such entries intended at the MFC design-level NOT to be walked? i.e. the most subclassed layer's dispatcher is intended to be the only one called? And if it wants to leverage an inherited member it has to manually make that call - MFC simply doesn't have any specific generic structure for this?
Calling Default() will cause the "normal" processing that would have happened in response to a message to occur. I'm not entirely clear on what you are trying to achieve, but it seems to me that calling Default() is what you want to do.
If you look at a lot of the handlers from Windows messages in the CWnd handlers (and handlers from classes derived from CWnd such as CEdit) you will see that they call Default().
Word to the wise, that Default() will actually use whatever parameters the original message had - you cannot change them.
You're doing it right. CWnd::DefWindowProc() uses the WINDOWPROC you subclassed from and will call the EDIT window's window procedure.
I am passing a pointer to a string to a DialogProc via CreateDialogParam. This pointer points to dynamically allocated memory which is released immediately after CreateDialogParam returns. Is WM_INITDIALOG processed before CreateDialogParam returns?
For example:
LPWSTR lpStr = malloc( some_size )
CreateDialogParam( ... lpStr );
free( lpStr );
In DialogProc:
case WM_INITDIALOG:
... do something with lParam
Yes. According to the documentation for CreateDialogParam,
The CreateDialogParam function uses the CreateWindowEx function to create the dialog box. CreateDialogParam then sends a WM_INITDIALOG message ... to the dialog box procedure.
The operative word here is that it sends a message (as opposed to posting one). SendMessage just calls the dialog procedure directly. PostMessage would put the message in the queue.
So, yes, the WM_INITDIALOG should complete before CreateDialogParam returns. You can verify this yourself relatively trivially using a debugger and some breakpoints.
From http://msdn.microsoft.com/en-us/library/aa928175.aspx
The CreateDialogParam function uses the CreateWindowEx function to create the dialog box. CreateDialogParam then sends a WM_INITDIALOG message to the dialog box procedure. The function displays the dialog box if the template specifies the WS_VISIBLE style. Finally, CreateDialogParam returns the window handle of the dialog box.
This states that WM_INITDIALOG is sent to the dialog before it completes. However, this message isn't processed until the window message loop on the dialog fetches the message. Since you're working with two separate threads, you shouldn't rely on this behaviour. If a slow operation is being executed synchronously on the window message loop, it might take longer than the rest of the CreateWindowEx call.
EDIT: As per Joel's answer, the documentation does imply that it sends (blocking) the message rather than posting it. You should test it yourself before relying on any functionality.
CoInitialize(NULL) creates an STA by creating a hidden window. How to get an HWND handle for this window?
Function EnumThreadWindows does not work, in an example I tried:
...
CoInitialize(NULL);
EnumThreadWindows(GetCurrentThreadId(), EnumThreadWndProc, 0);
...
BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM lParam)
{
m_hwnd = hwnd;
return FALSE;
}
Nothing ever enters the EnumThreadWndProc.
Any ideas?
This hidden window is Message-Only Window, It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages. The window simply dispatches messages.
To find message-only windows, specify HWND_MESSAGE in the hwndParent parameter of the FindWindowEx function. In addition, FindWindowEx searches message-only windows as well as top-level windows if both the hwndParent and hwndChildAfter parameters are NULL.
Source:
MSDN
Btw, I would be VERY careful here - you really shouldn't be sending window messages to windows you don't own. Your code is highly likely to break in a future version of Windows.