I have a child window that is maximized within a parent.
It needs to be disabled so that it doesn't receive inputs until I press a key.
When I press key 'A' for instance, I want the child window to be enabled, receive inputs sent with SendInput() and disable again.
So I do this:
EnableWindow( hwnd, TRUE );
SetForegroundWindow( hwnd);
SetFocus( hwnd);
Sleep(50);
SendInput()...x7-8 times
EnableWindow( hwnd, FALSE );
Now, EnableWindow functions work fine, except window misses some of the inputs. I tried to put some delay after EnableWindow (like 6-7 seconds!!) and still it doesn't work right.
I tried SetWindowPos() to have it update its frame, I tried setting the WS_DISABLE bit manually but still no luck. Inputs work fine if the child window is enabled all the time.
Any help is appreciated.
A Child window is serviced by the same thread as it's parent window. So the send SendInput doesn't do any good unless you go back to the pump and handle the events before you disable the window again.
If you explain what you are trying to accomplish, we could probably give you a better way to do it. But in any case, at the very least you need to run a message pump after SendEvents until you run out of events.
Be aware the a pump will also pump other messages, so it may make your whole design fall over. But, here it is.
// process messages until the queue is empty.
//
MSG msg;
while (PeekMessage(&msg, NULL, 0, PM_REMOVE))
{
// make sure we don't eat the quit message.
if (WM_QUIT == msg.message)
{
PostQuitMessage();
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Oh, and the Sleep() isn't doing anything useful, you can take it out.
Edit: this code goes after the SendInput call and before the window is disabled.
You may also want to use it instead of Sleep() to get the window to settle down before
you SendInput.
Instead of SendInput, I would try sending keyboard or mouse messages directly to the child window. For example:
EnableWindow(hwnd, TRUE);
SendMessage(hwnd, WM_KEYDOWN, ..., ...);
SendMessage(hwnd, WM_KEYUP, ..., ...);
// etc...
EnableWindow(hwnd, FALSE);
Related
I can disable window movement by doing this in the message loop:
case WM_MOVING:
GetWindowRect(hWnd, (RECT*)lParam);
break;
This, however does not work if you remove the call to GetWindowRect(). Why ?
This looks like the message loop is only modifying the message in passing (WM_MOVING.lParam points to the destination RECT of the movement). The message still gets processed (by the system ?). Just doing a break; does not throw away the message, the window is movable.
The message is processed no matter what (even if you don't call DefWindowProc() on it), you can just set its lParam so that the window goes back to its initial position. Is there no way to discard the message completely ? And who's doing the processing ? (obviously, the system is, is there a ubiquitous DefWindowProc() up there in the sky - that you cannot turn off- ?)
What is exactly going on ?
WM_MOVING is one of calbacks done inside callig SetWindowPos. Window is moved after returnig from callback, and after finally moving window next is send WM_MOVE. There are also WM_NCCALCSIZE, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGE, WM_GETMINMAXINFO, WM_SIZING, WM_SIZE.
Some messages when routed to DefWindowProc do real work (for example WM_NCPAINT, WM_ERASEBKGND), some are used for gathering information for further processing (WM_NCHITTEST, WM_GETMINMAXINFO), some are used for notifying you about changes (WM_MOVING, WM_MOVE, WM_SETTINGCHANGE) or actions (WM_COMMAND, WM_LBUTTONDOWN). It is recommended that when you don't process message, you should route it through DefWindowProc. If you fail to do this for some messages that wouldn't have any effect, but for others you will miss essential functionality.
For modifying interactive moving or sizing of a window, WM_NCHITTEST is a good choice. You can disable default action activated by part of window, or implement selected action on any part of the window.
case WM_NCHITTEST:
{
LRESULT r = DefWindowProc( hwnd, msg, wparam, lparam );
if ( r == HTCAPTION )
r = HTNOWHERE;
return r;
}
Or try bellow code and see what happens when left or top border is dragged.
case WM_NCHITTEST:
{
LRESULT r = DefWindowProc( hwnd, msg, wparam, lparam );
if ( r == HTLEFT )
r = HTTOP;
else if ( r == HTTOP )
r = HTLEFT;
return r;
}
The "Window Procedures" doc says:
For example, the system defines a window procedure for the combo box
class (COMBOBOX); all combo boxes then use that window procedure
I imagine that procedure belongs with the system (the appropriate system DLL, comctl32.dll I guess, unless you subclass it), but it executes in the app thread of course (after it loads that DLL). What that excerpt, and the apparent behavior suggest is the following:
If the style you select requires a title bar, a title bar window (a
system facility) is instantiated for your app, with its own
windowProc, let's say "sysTtlBarProc".
When you try to move this window,
messages (system-created, starting from the input driver) are sent to
both your app's windowProc() and to sysTtlBarProc()
sysTtlBarProc() waits on your windowProc() (which can call DefWindowProc() or not) to return before acting on the message
This would explain the behavior: this protocol gives your windowProc() a chance to act (on its own) and/or modify the message, thus potentially modifying sysTtlBarProc's behavior. This is just a general concept, some messages I guess are purely informative (you can't change sysTtlBarProc's behavior for these), you can only monitor what's happening, and do something of your own next to what sysTtlBarProc() does.
This can easily be demonstrated by doing this :
case WM_SIZING:
case WM_MOVING:
Sleep(1000);
break;
You can then try and move/resize the window and watch the new "sluggish" behavior. Whoever is processing those two WM_SIZING/MOVING messages (you're not calling DefWinProc(), and you're not doing anything with the messages) is definitely delaying his work by a second now.
I'm trying to send BM_CLICK to a window in another process.
When the target button get clicked, the target window pops up a modal dialog, then my calling thread never return even I send the timeout to 1 second.
SendMessageTimeout(handle, BM_CLICK, 0, 0, SMTO_ABORTIFHUNG | SMTO_NORMAL, 1000, NULL);
What's the reason of this? Is there any idea I can prevent this?
There are some potential solutions:
Use PostMessage with WM_LBUTTONDOWN and WM_LBUTTONUP to simulate the click
If I remember correctly, PostMessage doesn't support BM_CLICK, isn't it?
I'm using the win32sdk, but some messages would never work as expected while mouse was captured by calling SetCapture(), such as:
case WM_LBUTTONDOWN:
SetCapture(hWnd);
SendMessage(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
ReleaseCapture();
return 0;
The Window won't be maximized. but why?
Additional,
1. if I use PostMessage() instead, it works.
2. if I use PostMessage() instead and remove the ReleaseCapture(); statement, it doesn't work again.
In general:
PostMessage is asynchronous and you call ReleaseCapture() before WM_SYSCOMMAND is processed. So you have only one question: Why you can't maximize if mouse is captured?
I haven't found any info on this but read here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646262(v=vs.85).aspx
"When the mouse is captured, menu hotkeys and other keyboard accelerators do not work." I suppose WM_SYSCOMMAND is not processed also because of this restriction. May be it was done like this to keep coords consistent.
As norekhov said menu hotkeys don't work when the mouse is captured. When the mouse is captured the the only action a user can take that will result in the WM_SYSCOMMAND message being sent is to use a system menu hotkey.
Note that the WM_SYSCOMMAND message is only meant to be used to notify a window of a user initiated action. When you send it to a window, you're in essence trying to mimic a user's action. In this case you don't need to do this. You can tell the window to maximize itself directly:
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
In this case it won't appear to be a user command, so it won't be ignored because the mouse has been captured.
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
I'd like to prevent my window from being updated until I finish receiving data from the server and render it. Can I hook on the WM_PAINT event, or better still call some Win32API method to prevent the window from being updated and unfreeze it later?
More info:
In the context of an MMC snapin written in C#, our application suffers from annoying flickering and double sorting behaviour:
We use MMC's listViews, but since we subscribe to the sort event.
MMC does it's own magic and sorts the page being displayed (and we can't override that), and when we receive a reply from our server we change the listView again.
each row change is done sequentially, there's no beginUpdate etc. (AFAIK).
Normally hooking into WM_PAINT is the way to go, but make sure you also ignore all WM_ERASEBKGND notifcations, otherwise you'll still get flicker, because Windows erases the Windows area for you. (Return non-zero to prevent Windows from doing that)
One other possibility is to use the LockWindowUpdate function, but it has some drawbacks:
Only one window can be locked
Upon unlock the whole desktop and all sub-windows (i.e. everything) is repainted, resulting in short flash of the whole desktop. (It's worse on XP than on Vista)
Some controls have BeginUpdate and EndUpdate APIs for this purpose.
If you do something (e.g. hook and ignore paint events) do disable painting, then a way to force a repaint later is to call the Invalidate method.
OK, after all searching and checking I've found that LockUpdateWindow is bad idea - see for example articles of Raimond Chen OldNewThing. But even to implement the idea of SetRedrawWindow wasn't so simple - because what I had was only received from IConsole2* pConsole->GetMainWindow() HWND handler of main window. By setting it to SetRedraw = FALSE it was disappeared in very strange manner. Though to make the procedure run only for the TreeView and not for the whole application (ours left panel) I ran
EnumChildWindows(hWnd, SetChildRedraw, FALSE); //stopping redraw
//... here you do your operations
EnumChildWindows(hWnd, SetChildRedraw, TRUE); //restarting redraw
where SetChildRedraw callback was defined in next way:
#define DECLARE_STRING(str) TCHAR str[MAX_PATH]; ZeroMemory(str, sizeof(str));
BOOL CALLBACK SetChildRedraw(HWND hwndChild, LPARAM lParam)
{
RECT rcChildRect; ZeroMemory(&rcChildRect, sizeof(rcChildRect));
DECLARE_STRING(sText)
GetClassName(hwndChild, sText, MAX_PATH);
if (wcsstr(sText, L"SysTreeView32") != NULL)
{
SetWindowRedraw(hwndChild, lParam);
if (lParam == TRUE)
{
GetWindowRect(hwndChild, &rcChildRect);
InvalidateRect(hwndChild, &rcChildRect, TRUE);
}
}
return TRUE;
}