Find all windows beneath a point - windows

I want to find all the top-level windows (children of the desktop) beneath a given point on the desktop. I can't find an API for this.
My scenario is that I'm dragging a window across the screen and want to drop it into another (known) window. I can hit test the bounds of the target window ok, but that doesn't tell me whether it's occluded by another (unknown) window. Using WindowFromPoint and friends won't work, because the window being dragged is necessarily directly under the mouse. So I'm wondering if I can obtain all windows at the mouse position, and review them to see whether one of the windows I'm tracking is directly beneath the window I'm dragging.
Is there a way to do this without resorting to EnumDesktopWindows/GetWindowRect on every mouse drag? Or perhaps there's another solution I'm missing.

If you ask kindly, WindowFromPoint will ignore your window (the one currently being dragged) and return the next window. This is what Internet Explorer does when you drag a tab.
To do that:
Handle WM_NCHITTEST in window being dragged
Return HTTRANSPARENT during dragging. Call default window proc otherwise.
WindowFromPoint will ignore HTTRANSPARENT windows, but only those belonging to the calling thread. This shouldn't be a problem for you, because you should be calling WindowFromPoint from the window owner thread anyway.
Make sure there're no child windows at point passed to WindowFromPoint, or handle WM_NCHITTEST for these child windows as well.
Troubleshooting (if you still get your window from WindowFromPoint)
Test GetCurrentThreadID() == GetWindowThreadProcessId(WindowFromPoint(), 0) to ensure you're calling from correct thread
In WM_NCHITTEST, test that hwnd parameter equals what you get from WindowFromPoint()
Example (the area within rectangle returns the underlying window from WindowFromPoint):
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const RECT s_TransparentRect = {100, 100, 200, 200};
switch (message)
{
case WM_NCCREATE:
SetTimer(hWnd, 1, 100, 0);
break;
case WM_TIMER:
{
POINT cursorPos;
GetCursorPos(&cursorPos);
TCHAR buffer[256];
_snwprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("WindowFromPoint: %08X\n"), (int)WindowFromPoint(cursorPos));
SetWindowText(hWnd, buffer);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Rectangle(ps.hdc, s_TransparentRect.left, s_TransparentRect.top, s_TransparentRect.right, s_TransparentRect.bottom);
EndPaint(hWnd, &ps);
}
break;
case WM_NCHITTEST:
{
POINT cursorPos;
GetCursorPos(&cursorPos);
MapWindowPoints(HWND_DESKTOP, hWnd, &cursorPos, 1);
if (PtInRect(&s_TransparentRect, cursorPos))
return HTTRANSPARENT;
}
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

Right, you already know what WindowFromPoint() is going to return, should be the one you are dragging. Then use GetWindow() with uCmd = GW_HWNDNEXT to get the one below it in the Z-order. GetWindowRect() to get its bounds, IntersectRect() to compute the overlap.
Keep calling GetWindow() to find more windows that might be overlapped. Until it returns NULL or the overlap is good enough. If not then you'll normally favor the one that has the largest result rectangle from IntersectRect().

Related

How to initialize the background color of Win32 App to something other than white to avoid flash on ShowWindow?

I was looking into why when running my Windows App, it has a short flash of white background before rendering the actual app (i.e., before WM_ERASEBKGND and WM_PAINT is received).
Now, I just noticed that this problem is also present in the default sample app created by Visual Studio. At least this is the case for me when running under Windows 10,21H1 (in VS2008 & VS2013).
The only thing you have to do, after creating a "new Win32 Project", is change the background color of the window class, e.g., to the color red:
//wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.hbrBackground = (HBRUSH) CreateSolidBrush(RGB(255, 0, 0));
And then add a WM_ERASEBKGND with a Sleep to the WndProc:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_ERASEBKGND:
Sleep(1000);
return DefWindowProc(hWnd, message, wParam, lParam);
The Sleep exaggerates the problem, causing the white background to show for at least a second.
After that the red background is drawn as expected.
I'm including a short video when running the App with those changes.
For any app, it looks quite unprofessional that the window flashes white before rendering,
especially if the interface is dark.
So my question is: What is causing this behavior?
The background color is set through RegisterClassEx and passed to CreateWindow, before calling ShowWindow(..) So Windows should know that the background color is red. So why does it render it white? Am I missing something?
Ideally, I would like to change this initial background color to something other than white, such as black. But how? I've tried drawing to the window before calling ShowWindow, without luck.
This indeed seems to be a Windows bug as demonstrated by the excellent research by the OP.
The bug is even affecting applications developed by Microsoft.
The question is what is best workaround, especially for products that need to support backwards compatibility even after a fix is released in a specific version of Windows 11 (or Windows 10).
The main problem is that it is the act of making the window visible that makes Windows paint it with the white brush prior to correctly applying the background brush, regardless of what was painted into its DC beforehand. Therefore tricks such as painting into the DC prior to showing the window are unsatisfying, as the white background will still be shown, even if only for a few frames.
One method that seems to work well is to make the window visible, but fully transparent, paint the background, and then make the window opaque. We also need to animate the activation of the window, so it doesn't just pop in. For example, we can hijack WM_SHOWWINDOW for this:
case WM_SHOWWINDOW:
{
if (!GetLayeredWindowAttributes(hWnd, NULL, NULL, NULL))
{
SetLayeredWindowAttributes(hWnd, 0, 0, LWA_ALPHA);
DefWindowProc(hWnd, WM_ERASEBKGND, (WPARAM)GetDC(hWnd), lParam);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
AnimateWindow(hWnd, 200, AW_ACTIVATE|AW_BLEND);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
Full sample code:
#include "framework.h"
#include "WindowsProject1.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HINSTANCE mInstance;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
mInstance = hInstance;
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowExW(WS_EX_LAYERED, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_SHOWWINDOW:
{
if (!GetLayeredWindowAttributes(hWnd, NULL, NULL, NULL))
{
SetLayeredWindowAttributes(hWnd, 0, 0, LWA_ALPHA);
DefWindowProc(hWnd, WM_ERASEBKGND, (WPARAM)GetDC(hWnd), lParam);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
AnimateWindow(hWnd, 200, AW_ACTIVATE|AW_BLEND);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
ReleaseDC(hWnd, hdc);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
A more controversial answer could be that this is simply a bug in Windows.
For reference, (aside from the existing GIFs from Windows 10 that I already posted) here are recordings of the sample app running with and without background erase in Windows XP, Windows 7 and Windows 11.
Windows XP:
Windows XP: Without WM_ERASEBKGND/WM_PAINT: OK (no white background)
Windows XP: With WM_ERASEBKGND: OK (no white background)
Windows 7:
Windows 7: Without WM_ERASEBKGND/WM_PAINT: NOT OK (white background)
Windows 7: With WM_ERASEBKGND: NOT OK (white background)
Windows 7: With WM_ERASEBKGND + Sleep: NOT OK (white background)
Windows 7 with Aero disabled:
Windows 7 with Aero disabled: Without WM_ERASEBKGND/WM_PAINT: OK (no white background)
Windows 7 with Aero disabled: With WM_ERASEBKGND: OK (no white background)
Windows 7 with Aero disabled: With WM_ERASEBKGND + Sleep: OK (no white background)
Windows 11 (with Animation disabled):
Windows 11: Without WM_ERASEBKGND/WM_PAINT: NOT OK (white background)
Windows 11: With WM_ERASEBKGND: OK (no white background)
Windows 11: With WM_ERASEBKGND + Sleep: NOT OK (white background)
I've added Sleep to tests where it was hard to see the issue.
To sum up:
Windows XP: No issue. Everything seems to work as expected.
Windows 7: Issue occurs when Aero is enabled (Windows 7
theme), but not when it is disabled (Classic theme).
Windows 10: Issue occurs for all tests.
Windows 11: Issue occurs, but works without Sleep added.
Most likely since this was running on a faster machine.
So although I cannot conclude anything solid from these tests, it does look like this behavior was introduced in Windows 7 with Aero.
If someone can debunk this claim, please comment below.
I've hit this issue recently. I tried mnistic's solution using layered windows and transparency but it caused problems with rendering pane captions in the MFC app I'm working on. However, I've found a simple solution which appears to work nicely without any need for animation, changing window styles, etc.:
The Desktop Window Manager API enables a window to be "cloaked", so that it isn't shown on the screen but is still internally composited, i.e. still accumulates the results of drawing operations. You can turn "cloaking" on by:
BOOL cloak = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_CLOAK, &cloak, sizeof(cloak));
To avoid the white flash on first showing the window, do the above before calling ShowWindow(). Then do the initial UpdateWindow() to get correct content drawn. Finally, turn "cloaking" off using:
BOOL cloak = FALSE;
DwmSetWindowAttribute(hwnd, DWMWA_CLOAK, &cloak, sizeof(cloak));
to get the final window content displayed.
This should work on all Windows versions which have a Desktop Windows Manager, so Windows Vista and up.
Did some more poking, so here is a different potential answer.
I realized that even if I completely discard WM_PAINT and WM_ERASEBKGND (i.e., return 0 in WM_PAINT and return TRUE in WM_ERASEBKGND), I can still get the app to draw the red background by manually resizing the window!
Here is a clip to illustrate:
This means that Windows does indeed know and respect hbrBackground, which is great! For some odd reason it just doesn't clear it to that, but to white instead.
(Incidentally, I went through all the system colors in the registry (HKEY_CURRENT_USER\Control Panel\Colors HKEY_CURRENT_USER\Control Panel\Desktop\Colors) with a "255 255 255" setting and forcibly changed them to see if that would change the initial white background. But no luck. This makes me conclude that the white background is not a system color.)
Anyway, the above lead me to try to programmatically resize the window after ShowWindow. But since I don't want it to flicker on open, do the ShowWindow off-screen.
So here is the code that would replace regular ShowWindow(..):
int x0 = GetSystemMetrics(SM_XVIRTUALSCREEN);
int x1 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
RECT rect;
GetWindowRect(hWnd, &rect);
// resize and move off-screen
SetWindowPos(hWnd, NULL, x1-x0, 0, 0, 0, SWP_NOREDRAW );
// show window
ShowWindow(hWnd,nCmdShow);
// restore and redraw
SetWindowPos(hWnd, NULL, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0 );
Now, I would call this a hack. Yet, it does not rely on WM_ERASEBKGND nor WM_PAINT, so there should be less of a timing issue.
Also, the window shows up exactly like a regular ShowWindow(...) would, just with the correct hbrBackground, which is what I wanted.
Here is what it looks like # 25Hz:
Notice that there is no flash of white background.
Please note that I've tried to write the code to cater for virtual desktop/multi-monitor, but haven't actually tested that.
But unfortunately everything is not fine and dandy. As I was writing this answer, I did quite a few trial runs with OBSStudio recording # 60Hz, and went through the footage. There I found one that simply shows trash inside the window frame on open (apparently from Chrome), for just one frame. Here is a slowed-down replay:
I'm stumped. Perhaps that is the real issue ?
I did some more testing, and want to post a potential answer to this question.
Now, this is mainly based on the suggestion by #JonathanPotter, so full credit to him. And while it doesn't really fix the problem, it does alleviate it quite a bit.
Now, ideally, it would be great if Windows would simply render the window with the correct initial background color, but no matter how hard I've tried, I can only get it to update the background color by utilizing WM_ERASEBKGND or WM_PAINT.
So it seems that the time delay between showing the window (i.e. using ShowWindow), and the actual clearing of the background (WM_ERASEBKGND) is the crux of the problem. Hence, it makes sense to profile it. I've done so by recording the time difference between calling ShowWindow and reaching WM_ERASEBKGND using QueryPerformanceCounter.
So on an i7-4960HQ CPU # 2.60GHz running Window 10, the time between ShowWindow and WM_ERASEBKGND is between 100 - 317ms. It fluctuates quite a bit. This is with a vanilla Win32 Sample App, built in Release without any Sleeps or anything like that, but using a red hbrBackground to show the issue. This means that the white background is clearly visible for a few frames before the red background is drawn. Here is an animated gif captured # 25Hz: The white background is visible for 3 frames in that animation.
Now the potential fix is to use a combination of SetWindowPos and RedrawWindow before showing the window.
For my tests I simply added these two lines before calling ShowWindow(..):
SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);
RedrawWindow(hWnd, NULL, 0, RDW_INVALIDATE | RDW_ERASE);
Although RedrawWindow does not seem to make any difference.
Profiling again, the time between ShowWindow and WM_ERASEBKGND is now 10 - 23ms. A 10x speed-up!
Again, an animated gif captured (with SetWindowPos) # 25Hz:
This clearly shows that the flash of white background is gone, and thus the problem is fixed. It's like night and day.
Now, I would argue that this is not a fix, but rather a workaround. Since the underlying problem of Windows using a white background color is still there.
And since this is a timing issue, I can easily imagine that the white background could show up again, say if the system was sluggish or busy doing other stuff.
Similarly, having a faster system means you that you are less likely to see this in the first place, effectively hiding the issue.
But simply setting a breakpoint in WM_ERASEBKGND will still show you a white window.
Also, I have no explanation for the speed-up. I tracked the number of messages in the message pump, and they are the same in both scenarios.
Now, I'm still hoping for a better fix. I find it hard to believe that the Microsoft engineers found it cool to fill all freshly created Windows with a hardcoded 0xFFFFFF, so I'm hoping that this color is actually read from somewhere, and thus possible to change, so the initial background matches the hbrBackground.
Please feel free to post alternative answers, questions, or suggestions.
I will of course update this thread if I figure out anything else.

Win32 Edit Control Caret Placement Offset

I gave an English explanation of my problem below but it is a visual issue so if you don't want to read it all just look at the picture at the bottom).
I'm working on building a reverse polish notation calculator for my class and I just completed having the button controls on my GUI be able to append their values to the edit control which works fine, but the caret is doing something weird and I can't find any information on it.
I send a custom message to the edit control in which it finds the length of the current text in the control and then places the caret at the end of the text so I can then add what text needs to be added (it is right aligned with ES_RIGHT), which again works just fine, but when the caret is in the right most place it can be, it is placed practically right through the middle of most any number.
This only seems to happen in the right most place the caret can be (i.e. anywhere else the caret sits directly to the right of the preceding char, as it should) and I have tried replacing the caret all the way to the right using code, placing it using my keyboard/mouse, and tried adjusting the dimensions of the window in hopes that it was just an offset of the width I defined for it that caused the last place to be off slightly, but the problem persists and it makes it hard to read the last char in the text field.
Relevant Code:
LRESULT CALLBACK EditBoxClass::WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_COMMAND:
break;
case WM_APPEND_EDIT:
/* Get current length of text in the box */
index = new int( GetWindowTextLength (hWnd) );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
break;
}
return CallWindowProc( EditClassStruct.GetOldProc(), hWnd, msg, wParam, lParam );
}
Picture of problem:
After facing the same problem and presenting my first approach in this answer, I'll now provide two well working solutions. I think there is no other way to fix this glitch properly (unless you're a Microsoft programmer who is responsible for this part of the WinAPI).
I was wondering how to fix this problem on edit controls created with ES_MULTILINE but this glitch seems to be only a problem on single-line edit controls (tested on Windows 7 64-bit). Enabling Visual Styles is also helpful but the problem still remains (the offset is at least not so obvious).
Explanation
Normally, when the caret is at the farthest right position it's x value (provided by GetCaretPos ()) should be equal to the rect.right value provided by EM_GETRECT (when the edit control was created with ES_RIGHT). Due to unknown reasons this is not the case. So you have to check if the caret position is at least in the near of the rect.right value (but not farther away than the last letter is wide). So you have two possibilities to fulfill this task:
You must calculate the width of the outer right character using GetTextExtentPoint32 (), subtract it from the rect.right value provided by calling SendMessage () with EM_GETRECT and check whether the x position of the caret is bigger than the result or not OR
You must calculate the margin between the rect.right value and the outer right caret position (3 in my case) and use this value as a hardcoded offset to do a simple check.
After those steps (regardless which one you have chosen) you have to reposition the caret when necessary.
1. Approach (recommended)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
DWORD end;
SendMessage (hwnd, EM_GETSEL, (WPARAM) NULL, (LPARAM) &end);
int len = GetWindowTextLength (hwnd);
if (end < len || len <= 0)
return TRUE;
wchar_t *buffer = new wchar_t[len + 1];
GetWindowText (hwnd, buffer, len + 1);
wchar_t lastChar[] = {buffer[len - 1], '\0'};
delete[] buffer;
SIZE size;
HDC hdc = GetDC (hwnd);
if (hdc == NULL)
return TRUE;
GetTextExtentPoint32 (hdc, lastChar, 1, &size);
ReleaseDC (hwnd, hdc);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - size.cx) <= pt.x)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
2. Approach (improved original version)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - pt.x) <= 3)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
You have to subclass the edit controls. Then use this code in their window procedures and enjoy.
In both cases, tracking the mouse event is not absolutely necessary but recommended to completly avoid this glitch. Calling DefSubclassProc () will ensure that the cursor is changed on mouse over.
This may or may not be the cause, but you are misusing EM_SETSEL. You are dynamically allocating (and leaking) an int on the heap and passing a pointer to it as the message parameters, but EM_SETSEL does not expect or use pointers to begin with. So get rid of the dynamic allocation.
Also, the default window proc is not going to know how to handle your WM_APPEND_EDIT message, so there is no point in passing the message to CallWindowProc().
Try this instead:
case WM_APPEND_EDIT:
{
/* Get current length of text in the box */
int index = GetWindowTextLength( hWnd );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
return 0;
}
That being said, try using EM_GETRECT/EM_SETRECT to expand the right edge of the edit control's formatting rectangle by a few pixels. That should give the caret some extra room to work with.

How to redirect mouse wheel message to other windows?

I'm developing a Word addin for MS Word on Windows, and this addin has as 'advanced task pane' showing and docking on the left side of the Word document window (it's treeview(outline) showing a list of Word documents for fast editing multiple documents in a project).
My question is, the Word document window responds to mouse wheel message only when it's focused, but I want to be able to scroll the document with mouse wheel whenever the mouse cursor is hovering on it even the Word document window doesn't have a input focus.
Any hints in this particular case? Thank you!
Not quite sure it will work, but I'd try the following:
Implement a global low-level mouse hook using the SetWindowsHookEx function.
In the hook procedure, which should be called on mouse wheel scroll events, check if the window under mouse cursor is the Word document window. If so, set a shared flag indicating the needed scroll action.
Don't send WM_VSCROLL directly from the hook procedure! This procedure has to be really fast and simple.
Now, in your add-in's message loop check the flag and if it is set, send WM_VSCROLL to the Word document window.
Perhaps you could make use of the SetCapture(hWnd) Windows API function. This will cause all mouse events to go to your hWnd instead of whatever hWnd might normally expect to receive them. If you capture when the mouse enters the Word document window, and ReleaseCapture() when the mouse leaves or Word gains focus, it should work alright.
Disclaimer: I've used mouse capturing in C# before, but I've never done it in C++. I don't know if it behaves exactly the same way.
Try the following , this might help you.
1) Handle WM_MOUSEHOVER message.
2) In the handler , use SendMessage, with WM_VSCROLL as the message parameter .
Using Spy++ I saw that the window that gets the messages is of the class _Wwg (At least 2003 is) and it is responding to the WM_MOUSEWHEEL message. So you would send that window a WM_MOUSEWHELL message when you want it to scroll.
I've got the C++ code snipped below from a comment in https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
And I used it (and variations on it) successfully.
The user who wrote it claims it was inspired by a ms recommendation on a Windows Vista best practices guide, to forward the mouse wheel event to whatever window is hovered by the mouse cursor. The virtue of his/her implementation is that it's completely unintrusive, you just drop it and it set the hooks, referencing your main thread. It avoids forwarding the event to windows belonging to other processes, but perhaps that could actually be a good thing.
namespace {
LRESULT CALLBACK mouseInputHook(int nCode, WPARAM wParam, LPARAM lParam) {
//"if nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function
//without further processing and should return the value returned by CallNextHookEx"
if (nCode >= 0) {
MSG& msgInfo = *reinterpret_cast<MSG*>(lParam);
if (msgInfo.message == WM_MOUSEWHEEL ||
msgInfo.message == WM_MOUSEHWHEEL) {
POINT pt = {};
pt.x = ((int)(short)LOWORD(msgInfo.lParam)); //yes, there's also msgInfo.pt, but let's not take chances
pt.y = ((int)(short)HIWORD(msgInfo.lParam)); //
//visible child window directly under cursor; attention: not necessarily from our process!
//http://blogs.msdn.com/b/oldnewthing/archive/2010/12/30/10110077.aspx
if (HWND hWin = ::WindowFromPoint(pt))
if (msgInfo.hwnd != hWin && ::GetCapture() == nullptr) {
DWORD winProcessId = 0;
::GetWindowThreadProcessId(//no-fail!
hWin, //_In_ HWND hWnd,
&winProcessId); //_Out_opt_ LPDWORD lpdwProcessId
if (winProcessId == ::GetCurrentProcessId()) //no-fail!
msgInfo.hwnd = hWin; //it would be a bug to set handle from another process here
}
}
}
return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}
struct Dummy {
Dummy() {
hHook = ::SetWindowsHookEx(WH_GETMESSAGE, //__in int idHook,
mouseInputHook, //__in HOOKPROC lpfn,
nullptr, //__in HINSTANCE hMod,
::GetCurrentThreadId()); //__in DWORD dwThreadId
assert(hHook);
}
~Dummy() {
if (hHook)
::UnhookWindowsHookEx(hHook);
}
private:
HHOOK hHook;
} dummy;
}

Win32: My Application freezes while the user resizes the window

I write a win32 application. I implemented the message loop myself like this:
bool programcontinue = true;
while(programcontinue)
{
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
IdleProcess();
}
There is a resizable window in my application. Usually, IdleProcess() gets called several times per second. When the user grabs a corner or an edge of the resizable window, IdleProcess() doesn't get called anymore until the user releases the mouse button.
What happens here?
I tried exchanging the inner while with an if, but that doesn't change the behaviour. It seems like when resizing starts, the handler for that message does not return until the resizing is done?
Is there a way to change this and call IdleProcess() during resizing a few times per second?
Thanks
Marc
EDIT:
What I mean by replacing the inner while with if is:
bool programcontinue = true;
while(programcontinue)
{
if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) // <<<<
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
IdleProcess();
}
My window Proc is a bit lengthy, but I get the same behavior with a small test app. This is identical to the wndproc the VS Project Wizard creates:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
There are a number of modal operations that happen on windows. Win32 Modal operations refer to functions that put an application into a "mode" by starting their own event processing loop until the mode finishes. Common application modes include drag and drop operations, move/size operations, anytime a dialog pops up that needs input before the application can continue.
So what is happening is: Your message loop is NOT being run.
Your window recieved a WM_LBUTTONDOWN message that you passed to DefWindowProc. DefWindowProc determined that the user was trying to size or move the window interactively and entered a sizing/moving modal function. This function is in a message processing loop watching for mouse messages so that It can intercept them to provide the interactive sizing experience, and will only exit when the sizing operation completes - typically by the user releasing the held button, or by pressing escape.
You get notified of this - DefWindowProc sends a WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages as it enters and exits the modal event processing loop.
To continue to generate "idle" messages, typically create a timer (SetTimer) before calling a modal function - or when getting a message that DefWindowProc is entering a modal function - the modal loop will continue to dispatch WM_TIMER messages... and call the idle proc from the timer message handler. Destroy the timer when the modal function returns.
When DefWindowProc handles WM_SYSCOMMAND with either SC_MOVE or SC_SIZE in the wParam, it enters a loop until the user stops it by releasing the mouse button, or pressing either enter or escape. It does this because it allows the program to render both the client area (where your widgets or game or whatever is drawn) and the borders and caption area by handling WM_PAINT and WM_NCPAINT messages (you should still receive these events in your Window Procedure).
It works fine for normal Windows apps, which do most of their processing inside of their Window Procedure as a result of receiving messages. It only effects programs which do processing outside of the Window Procedure, such as games (which are usually fullscreen and not affected anyway).
However, there is a way around it: handle WM_SYSCOMMAND yourself, resize or move yourself. This requires a good deal of effort, but may prove to be worth it. Alternatively, you could use setjmp/longjmp to escape from the Window Procedure when WM_SIZING is sent, or Windows Fibers along the same lines; these are hackish solutions though.
I solved it (using the first method) this past weekend, if you're interested I have released the code to the public domain on sourceforge. Just make sure to read the README, especially the caveat section. Here it is: https://sourceforge.net/projects/win32loopl/
You can still receive the WM_PAINT message, you just gotta tell the WinAPI that you want it (seen in NeHe OpenGL tutorials):
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws The Window For Any Movement / Resizing
It will still block your while/PeekMessage-loop though! WinAPI just calls your WndProc directly.
During resize Windows sends quite a few messages to your program. I have not proved this, but the behavior you describe is familiar. I'd suggest to call your function IdleProcess() also within the while(...) loop for certain events such as WM_SIZING which your application will receive frequently during window resizing:
bool programcontinue = true;
while(programcontinue)
{
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
if(Msg.message == WM_SIZING)
IdleProcess();
}
IdleProcess();
}
Be aware though that this assumes, that IdleProcess() does not create or consume any events. If thats the case, things get much more complicated.

Why can my property sheet, shown from a system tray icon, lock up the taskbar?

Note: code samples have been simplified, but the overall structure remains intact.
I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:
WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);
Then the icon is created, referring to this message-only window:
NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)
When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):
LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case TRAYICON_MESSAGE:
switch (lParam) { // that contains the "real" message
case WM_LBUTTONDBLCLK:
showPropertySheet();
return 0;
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:
void showPropertySheet() {
PROPSHEETPAGE pages[NUM_PAGES];
pages[0].pfnDlgProc = &firstPageDialogProc;
// ...
PROPSHEETHEADER header;
header.hwndParent = NULL;
header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
header.ppsp = pages;
// ...
PropertySheet(&header);
}
Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:
BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE; // breakpoint here
}
When the program stops on the breakpoint, the entire taskbar locks up!
The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.
Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.
As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.
Am I doing something fundamentally wrong? Can anyone shed some light on this issue?
Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.
The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.
I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.

Resources