Scrolling text gets negated in static control (C++ WinAPI) - c++

I have some weird control,
CreateWindowEx(!tset&USE?WS_EX_OVERLAPPEDWINDOW:0,
tset&USE?"static":"edit",0,WS_CHILD|WS_VISIBLE|
(!tset&USE?ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_VSCROLL //when it's edit
:SS_NOTIFY|SS_EDITCONTROL),5,60,390,474,hwnd,HMENU(10),0,0); //when it's static
SetWindowSubclass(GetDlgItem(hwnd,10),tset&USE?BOR:NoMenu,0,0);
whose everything depends on whether themes are active. Here tset is an instance of enum type for bit flags. Everything is fine except scrolling when the control is in static mode. It wasn't receiving WM_MOUSEWHEEL messages, I made it receive them in the following way:
LRESULT CALLBACK BOR(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp,UINT_PTR,DWORD_PTR)
{
if(msg==WM_MOUSEMOVE&&GetDlgCtrlID(hwnd)==10)
{
POINT x{LOWORD(lp),HIWORD(lp)}; ClientToScreen(hwnd,&x);
RECT rc; GetWindowRect(hwnd,&rc); if(PtInRect(&rc,x)&&GetCapture()!=hwnd){SetCapture(hwnd); hwnd3=SetFocus(hwnd);}
if(!PtInRect(&rc,x)&&GetCapture()==hwnd){ReleaseCapture(); SetFocus(hwnd3);} //hwnd3 is an instance of HWND defined in global scope, for giving focus back
}
if(msg==WM_MOUSEWHEEL)
{
RECT rc; GetWindowRect(GetParent(hwnd),&rc);
HRGN x; GetWindowRgn(GetParent(hwnd),x);
ScrollWindowEx(hwnd,0,short(HIWORD(wp)),0,0,x,&rc,SW_INVALIDATE|SW_ERASE);
ShowWindow(GetParent(hwnd),SW_HIDE); ShowWindow(GetParent(hwnd),SW_SHOW);
SetFocus(hwnd);
return 1;
}
return DefSubclassProc(hwnd,msg,wp,lp);
}
Now when mouse enters client area of my control, if it's in static mode, it captures mouse and gets focus. So it intercepts WM_MOUSEWHEEL messages in its subclass callback function and I can scroll my control in the static mode. Here's the exact problem: few milliseconds later, right after its text is scrolled, the window somehow updates itself to its initial unscrolled state. And my efforts get negated. Is it clear why it behaves like this, and how to fix that?
#Edit: Why don't I simply add WS_VSCROLL to its static version? Because it has SS_NOTIFY style and I need it to respond to WM_COMMAND messages somehow else. This is why I tried scrolling it manually.
#Update:
Here in both screenshots the text length is the same. When the control is in edit state, its vertical scrollbar is proper. But when it's in static state the vertical scrollbar doesn't match the actual text length. Besides, the scrollbar is always at the same size and same position independent from text length and it's also unscrollable. Why may be the reason of it behaving like this?
#Update: Thanks to #enhzflep's comment, problem is easily solved some other way.

In the interests of removing the question from the Unanswered list, I'll repeat and expand upon one of the comments.
Looking at the images, we can immediately see a clear difference in the way they're each presented. The left image has each line starting with a different character, while the right image always starts with the same character. They also have very differently sized thumbs in the scroll-bar.
Since the problems seem to be stemming from trying to use two different controls to achieve essentially the same task, I'd suggest always using an edit control. (this is the one that appears to behave nicely, and is the one that doesn't wrap the text)
Rather than use two different types of control, I suggest just toggling the read-only flag. With luck (!) the only difference now will be the way mouse and keyboard messages are handled - hopefully you'll get similar behaviour and appearance with such an approach.
The message I'm thinking of that will achieve this is EM_SETREADONLY.
Although the question has been successful answered, I can't find reference in the Microsoft docs to support my theories, thus used language like 'hopefully'.

Related

Get window icon: GetClassLong VS SendMessage

There are two methods to get window icon if i know his handle:
SendMessage(HWND,0x7F lParam, wParam)
and
GetClassLong(HWND, -14|-34)
But even if I get icon from window with GetClassLong i can't set new icon with SetClassLong, but successfully set new icon with SendMessage.
I need to know: why in some times work second get method, but doen't work first method. And why always from SendMessage(WN_SETICON) and doen't work SetClassLong(HWND, -14|-34, HICON)?
GetClassLong retrieves longs from the window class.
A window class is a blueprint for creating a window of a specified type, not the window itself.
It can contain quite a lot of defaults, like the default icon, and the default small icon.
But a window is only based on it, it can override everything.
Thus, setting the windowclass' icon does not modify any already created classes, you must send a message to the window instead.
And reading the windowclass-data gets you stale data, which might or might not still be relevant to the window.
As an aside, -14|-34 would be GCL_HICON|GCL_HICONSM, or -2, which does not actually make sense.
Explicitly say that you read with both indices one after the other.
-14 is GCL_HICON which is the (optional resource) icon for the class specified when its registered.
A specific window can subsequently specify its own icon (WM_SETICON), subsequent changes to
GCL_HICON won't affect it.
I also doubt you can attempt to pull GCL_HICON | GCL_HICONSM, if they were different what would the value be?
A few additional things:
1) Don't use magic numbers for Windows API constant values. Use WM_GETICON, not 0x7F. Use the GCL_ names as mentioned by the other answers.
2) You switched wParam and lParam in your SendMessage(). wParam comes first.
3) Don't use GetClassLong(); it is not 64-bit safe (and icon handles are pointers). Use GetClassLongPtr() instead. Also substitute GCLP_ for GCL_ in your named constants.
4) Don't try to replace | in your GetClassLong() with || or with an array of indices and expect that to work either. You don't have a choice but to make two calls.

Windows Aero Rendering Bug

So, I have stumbled upon an interesting bug with the Windows API and I'm wondering if anyone has some insight on how to work around it. It seems that even Google has struggled with it. It should be noted that while I will be fixing this in Qt source itself, the problem is with Windows default message handling, not Qt. All of the files that I will mention can be found online as they are all open source libraries. Below is somewhat of a complex problem and I will try and give as much context as possible. I've put a lot of time and effort into fixing this myself, but being that I've only been an engineer for about 8 months, I'm still quite inexperienced and could very well have missed something obvious.
The context:
I have written a program that uses Qt to skin my windows with custom skins. These skins go over the system default non-client UI skins. In other words, I use custom painted frames (supported by Qt). Since Qt5 I've been having issues with my program when it is run on any pre-Windows Aero OS ( less than XP and greater than Vista with Windows Aero disabled). Unfortunately, Qt devs have all but confirmed that they do not really support XP anymore, so I will not rely on them to fix the bug.
The Bug:
Clicking anywhere in the non-client area while running a machine with composition disabled (Windows Aero disabled or not existing) will cause Windows to repaint its system default non-client UI on top of my custom skin.
My Research
A bit of debugging and investigation led me to qWindowsProc in qwindowscontext.cpp. I was able to determine that the last windows message to be handled before my window's skin was painted over was WM_NCLBUTTONDOWN. This seemed strange, so I took to the internets.
Sure enough, I found a file called hwnd_message_Handler.cc that comes from Google's Chromium Embedded Framework (CEF). In that file are many comments about how various windows messages, for some insane reason, cause repaints of the system default non-client frames over custom frames. The following is one such comment.
// A scoping class that prevents a window from being able to redraw in response
// to invalidations that may occur within it for the lifetime of the object.
//
// Why would we want such a thing? Well, it turns out Windows has some
// "unorthodox" behavior when it comes to painting its non-client areas.
// Occasionally, Windows will paint portions of the default non-client area
// right over the top of the custom frame. This is not simply fixed by handling
// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
// rendering is being done *inside* the default implementation of some message
// handlers and functions:
// . **WM_SETTEXT**
// . **WM_SETICON**
// . **WM_NCLBUTTONDOWN**
// . EnableMenuItem, called from our WM_INITMENU handler
// The solution is to handle these messages and **call DefWindowProc ourselves**,
// but prevent the window from being able to update itself for the duration of
// the call. We do this with this class, which automatically calls its
// associated Window's lock and unlock functions as it is created and destroyed.
// See documentation in those methods for the technique used.
//
// The lock only has an effect if the window was visible upon lock creation, as
// it doesn't guard against direct visiblility changes, and multiple locks may
// exist simultaneously to handle certain nested Windows messages.
//
// IMPORTANT: Do not use this scoping object for large scopes or periods of
// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
//
// I would love to hear Raymond Chen's explanation for all this. And maybe a
// list of other messages that this applies to ;-)
Also in that file exists several custom message handlers to prevent this bug from occurring. For example, another message I found that causes this bug is WM_SETCURSOR. Sure enough, they have a handler for that which, when ported to my program, worked wonderfully.
One of the common ways they handle these messages is with a ScopedRedrawLock. Essentially, this just locks redrawing at the beginning of the hostile message's default handling (via DefWindowProc) and remains locked for the duration of the call, unlocking itself when it comes out of scope (hence, ScopedRedrawLock). This will not work for WM_NCLBUTTONDOWN for the following reason:
Stepping through qWindowsWndProc during the default handling of WM_NCLBUTTONDOWN, I saw that WM_SYSCOMMAND is handled in the same call stack directly after WM_NCLBUTTONDOWN. The wParam for this particular WM_SYSCOMMAND is 0xf012 - another officially undocumented value**. Luckily in the remarks section of the MSDN WM_SYSCOMMAND page somebody commented about it. Turns out, it is the SC_DRAGMOVE code.
For reasons that may seem obvious, we cannot simply lock redrawing for the handling of WM_NCLBUTTONDOWN because Windows automatically assumes that the user is trying to drag the window if he clicks on a non-client area (in this case, HTCAPTION). Locking here will cause the window to never redraw for the duration of the drag- until Windows receives a button up message(WM_NCLBUTTONUP or WM_LBUTTONUP).
And sure enough, I find this comment in their code,
if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
delegate_->IsUsingCustomFrame()) {
// TODO(msw): Eliminate undesired painting, or re-evaluate this workaround.
// DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we
// need to call it inside a ScopedRedrawLock. This may cause other negative
// side-effects (ex/ stifling non-client mouse releases).
DefWindowProcWithRedrawLock(message, w_param, l_param);
handled = true;
}
This makes it seem as though they had the same problem, but didn't quite get around to solving it.
The only other place CEF handles WM_NCLBUTTONDOWN in the same scope as this problem is here:
else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) {
switch (w_param) {
case HTCLOSE:
case HTMINBUTTON:
case HTMAXBUTTON: {
// When the mouse is pressed down in these specific non-client areas,
// we need to tell the RootView to send the mouse pressed event (which
// sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
// WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
// sent by the applicable button's ButtonListener. We _have_ to do this
// way rather than letting Windows just send the syscommand itself (as
// would happen if we never did this dance) because for some insane
// reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
// window control button appearance, in the Windows classic style, over
// our view! Ick! By handling this message we prevent Windows from
// doing this undesirable thing, but that means we need to roll the
// sys-command handling ourselves.
// Combine |w_param| with common key state message flags.
w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0;
w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0;
}
}
And while that handler addresses a similar problem, its not quite the same.
The Question
So at this point I'm stuck. I'm not quite sure where to look. Maybe I'm reading the code incorrectly? Maybe the answer is there in CEF and I'm just overlooking it? It seems like CEF engineers encountered this problem and have yet to come up with the solution, given the TODO: comment. Does anybody have any idea what else I could do? Where do I go from here? Not solving this bug is not an option. I'm willing to dig deeper but at this point I'm contemplating actually handling Windows drag events myself rather than having the DefWindowProc handle it. Though, that might still cause the bug in the case where the user is actually dragging the window.
Links
I have included a list of links that I have been using in my research. Personally, I downloaded CEF source myself so that I could better navigate the code. If you are truly interested in solving this problem, you might need to do the same.
WM_NCLBUTTONDOWN
WM_NCHITTEST
WM_SYSCOMMAND
DefWindowProc
hwnd_message_handler.cc
hwnd_message_handler.h
qwindowscontext.cpp
Tangent
Just to bring validation to CEF's code, if you look in the header of hwnd_message_handler, you will also notice that there are two undocumented windows messages of value 0xAE and 0xAF. I was seeing 0xAE during the default handling of WM_SETICON that was causing problems, and this code helped confirm that what I was seeing was indeed real.
I found this page which suggests hiding your window by removing WS_VISIBLE immediately before calling DefWindowProc(), then showing it immediately after. I haven't tried it, but it's something to look at.
So, the actual way this fix was achieved was by removing the WS_CAPTION flag during NC_LBUTTONDOWN and adding it back during NC_LBUTTONUP message handling. However, because of the way Windows calculates its size before rendering, it could miscalculate since it removes the caption area from consideration. So, you will need to offset this while handling the WM_NCCALCSIZE message.
Keep in mind that the amount of pixels you will need to offset will vary depending on which windows theme or OS you are in. i.e. Vista has a different theme than XP. So you will need to decide on a scale factor to keep it clean.

Windows.h - Notification when focus enters a text input

I'm trying to come up with a solution for setting up a notification when focus enters a text field. The end goal in mind is to recreate the type of functionality you see on mobile devices with on screen keyboards.
So far I've been exploring SetWinEventHook with EVENT_OBJECT_FOCUS and GetGUIThreadInfo with GUI_CARETBLINKING.
From the docs:
EVENT_OBJECT_FOCUS
An object has received the keyboard focus. The system sends this event
for the following user interface elements: list-view control, menu
bar, pop-up menu, switch window, tab control, tree view control, and
window object.
GUI_CARETBLINKING The caret's blink state. This bit is set if the
caret is visible.
Using these methods I've come up with this solution:
void TextInputHelper::setupEventHook(FREContext iCtx)
{
ctx = iCtx;
CoInitialize(NULL);
evHook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_END, NULL,
handleEventObjectFocus, 0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
void CALLBACK handleEventObjectFocus(HWINEVENTHOOK hook, DWORD evt, HWND hwnd,
LONG idObj, LONG idChild, DWORD thread, DWORD time)
{
GUITHREADINFO threadInfo;
threadInfo.cbSize = sizeof(GUITHREADINFO);
BOOL result = GetGUIThreadInfo(thread, &threadInfo);
if(threadInfo.flags & GUI_CARETBLINKING)
{
//text field focus
}
}
This does seem to work in some cases but its definitely not reliable. Programs like Notepad an IE seem to work fine but others like Firefox do not. This also will not work for things like text fields on websites because it doesn't look like handleEventObjectFocus will get called.
Does anyone know of another way to approach this problem? I've been searching around and it seems like I might be looking for something in the Accessibility APIs but I haven't been able to dig up to much on it.
Thanks!
edit
To clarify, I'm looking to receive a notification when focus enters any text field. This application is a win32 dll and will never have focus its self.
If you're using standard Windows controls WM_SETFOCUS should do the trick. No need to get fancy with hooking etc.
EDIT: For system wide behavior you can check out SetWindowsHookEx. To catch events system-wide you need to use it from within a DLL. You can use a combination of hooks including one that catches WM_SETFOCUS.
If you're trying to supply an alternative text input method, you should look into "IME" - "Input Method Editor". These are directly supported by the OS.
You can catch the entrance into a text field using the EN_SETFOCUS notification.
WM_FOCUS is for the window itself, not for controls that are in it. Otherwise said, if you want to use WM_FOCUS, you'll have to subclass your EDIT field. It's not necessary here.
EDIT: it wasn't completely clear that you wanted a system-wide behavior. IN that case you have to use an hook (cf. SetWindowsHookEx) as explained in the answer above. Sorry.

A single MFC/Win32 control seems to be making my whole desktop repaint

I have a custom control, which owns an edit box and moves it around, etc. The edit-box is typically modified with a wodge of code like this:
edit.MoveWindow( &rc );
edit.SetWindowText( text );
edit.SetLimitText( N );
edit.ShowWindow(SW_SHOW);
edit.SetFocus();
edit.SetSel(0, CB_ERR);
RECT rc is in coordinates local to the custom control, edit is created with the custom control as parent. I'm not even sure this is definitely the problem, but when triggering this code sometimes it is nice and smooth, other times my entire desktop appears to flicker like it's being redrawn. I can't see I'm explicitly calling Invalidate(Rect) anywhere.
Any ideas?
It's not going to be any of the code that you are showing us. A whole desktop flash is nearly always somewhere in your code that is calling InvalidateRect(NULL,...) so keep digging.
Several of these calls will result in messages being sent to the parent window of the edit, most likely the InvalidateRect is happening while handling that message.
If I was a betting man, I'd bet on the SetFocus() call as the one that's triggering the repaint.

button keyboard focus issues

How would one prevent the little dotted square that appears on a button when it has the keyboard focus in a dialog. (w/ apologies for the technical jargon). At one point I hacked together a solution by subclassing a button WindowProc and subverting some windows messages, but wanted to know the correct way.
There's actually a problem with another control in the dialog also involving the keyboard. This other control is actually also a button, but being used as a group box or panel, not as a functioning button. But when I hit the tab key in the dialog, this group box "button" comes to the foreground obscuring the static controls on top of it, so I wanted to prevent that.
For both of the above, I tried turning off WS_TABSTOP - didn't help.)
Both of my problems mentioned above were solved by subclassing the WndProcs and returning 0 in response to message 0x128 and discarding it. Even Spy++ could not identify this message 0x128, and I don't have it in any header. But its sent to every control in the dialog the first time tab is hit in the dialog.
(I did try BN_SETFOCUS as described above and also WM_SETFOCUS but it didn't help.)
So if anyone knows where to find what windows message 0x128 is...
The correct way is to write your own button control instead of using the default Windows one.
Alternatively, you can prevent if from ever getting keyboard focus.