What Windows Message is causing a partial region redraw - winapi

I have some old VB6 code which draws squiggles onto a simple windows textbox using win32api and double-buffering to highlight errors/mispelled words etc.
Here it is before ...
and if I select a single letter using the mouse down, mouse move left and mouse up I get a small region redrawn - it looks like about 2 lines. Here it is after ...
I'm struggling to work out what is causing the redraw and how to subclass stuff to stop that happening - if I indeed even can.
If I look at spy++, it looks like it may be something to do with the IME, but I'm not sufficiently well versed to know for sure. I have tried stopping the default editor window from displaying following my limited understanding of what MS Docs was suggesting.
Private Function ProcessBeforeMsg(ByVal p_nMsg As Long, wp As Long, lp As Long, Result As Long) As Boolean
...
Case WM_IME_SETCONTEXT
lp = lp And Not ISC_SHOWUICOMPOSITIONWINDOW
...
to no avail.
So I'd be grateful if anyone could point me in the right direction.
EDIT-1 - I probably should add that I am not seeing a WM_ERASEBKGND in this scenario
EDIT-2 - Here's the spy++ trace for the messages which I had thought were relevant. Messages 14-19 were the ones being recorded when this behaviour happens.
EDIT-3 So now I'm wondering if my problem is the way I am drawing the squiggles because when the cursor selects the text, the squiggles are removed on that line only. I
blit the contents of the textbox device context into a memoryDC
draw the squiggle with a polyline on that memoryDC
blit the contents of the memoryDC back into textboxDC
Here's the code for full disclosure
Private Sub PaintSquiggles(ByVal p_cMistakes As CMistakes)
Dim l_tRect As RECT
Dim l_hFont As Long
Dim l_hFontOld As Long
Dim l_nWidth As Long
Dim l_nHeight As Long
Dim l_hVisibleDC As Long
Dim l_hHiddenDC As Long
Dim l_hHiddenBMP As Long
SendMessage m_hWnd, EM_GETRECT, ByVal CLng(0), l_tRect
l_nWidth = l_tRect.Right - l_tRect.left
l_nHeight = l_tRect.Bottom - l_tRect.top + 2
l_hVisibleDC = GetDC(m_hWnd) '
l_hHiddenDC = CreateCompatibleDC(l_hVisibleDC)
l_hHiddenBMP = CreateCompatibleBitmap(l_hHiddenDC, l_nWidth, l_nHeight)
SelectObject l_hHiddenDC, l_hHiddenBMP
BitBlt l_hHiddenDC, 0, 0, l_nWidth, l_nHeight, l_hVisibleDC, 0, 0, vbSrcCopy
l_hFont = SendMessage(m_hWnd, WM_GETFONT, 0, ByVal 0&)
l_hFontOld = SelectObject(l_hHiddenDC, l_hFont)
m_nLineOffset = CalculateWordExtents(m_hWnd, l_hHiddenDC, p_cMistakes, l_tRect)
DrawSquiggles l_hHiddenDC, p_cMistakes, m_nInverseBackColour
BitBlt l_hVisibleDC, 0, 0, l_nWidth, l_nHeight, l_hHiddenDC, 0, 0, vbSrcCopy
SelectObject l_hHiddenDC, l_hFontOld
DeleteObject l_hHiddenBMP
ReleaseDC m_hWnd, l_hHiddenDC
DeleteObject l_hHiddenDC
ReleaseDC m_hWnd, l_hVisibleDC
DeleteObject l_hVisibleDC
End Sub
Subsequently, when the text is then selected with the mouse, it picks up the text ok but the squiggle disappears and it makes me wonder if I haven't drawn the squiggles onto the same plane (or something like that) as the text. Here's another couple of screen shots to show this. I've shifted the squiggles so that they go through the middle of the text simply for demonstration.
After drawing squiggles
After mouse-selecting text, the text remains but the squiggles disappear as if the selection area isn't picking up the plane on which the squiggles are drawn within the DC - but I'm guessing??
So are there planes within the device context which I need to merge in some way prior to blitting back to the textboxDC? Or is there something else going on.
Many thx again

Related

Window is invisible, but IsWindow + IsWindowVisible + IsWindowEnabled return true and GetWindowRect returns plausible values

I'm iterating through all windows on the Windows desktop in order to allow disable patients select them easily in order to type into them.
I get all windows using
hw = GetDesktopWindow()
' It's first child is the 1st top level window
hw = GetWindow(hw, GW_CHILD)
'Now loop through all windows
etc.
There are many windows which are invisible or disabled, and I simply skip them because the user should not be able to select them. He wouldn't be able to type into them anyway.
Now I am confronted with a strange window.
Its title is "Groove Music".
It's a window from the Window Groove music app, and I can see it in the taskmanager.
I use the following API calls to check if this is a valid window that the user should be able to select:
IsWindow
IsWindowEnabled
IsWindowVisible
The declarations of these API calls are 100% perfect, I use them since years already.
And last, I even check its GetWindowRect values to see if it's in the screen at all.
And then I even check if it's a layered window (like an overlay that some apps use for non-clickable messages):
Public Function IsWindowLayered(ByVal uHwnd As Long) As Boolean
Dim lret&
lret = GetWindowLong(uHwnd, GWL_EXSTYLE)
If (lret And WS_EX_LAYERED) = WS_EX_LAYERED Then
IsWindowLayered = True
End If
End Function
It returns false.
The funny thing how is that the window is actually invisible.
So the user should not be able to select it.
Which function may I have missed to check if the window is actually visible?
Thank you!
Edit: It's also the calculator window (calc.exe) after I had opened it and closed it again.
Edit 2:
I also check for WS_VISIBLE like that, but even that return true for that window:
Public Function IsWindowVisibleEx(ByVal uHwnd As Long) As Boolean
Dim lret&
lret = GetWindowLong(uHwnd, GWL_STYLE)
If (lret And WS_VISIBLE) = WS_VISIBLE Then
IsWindowVisibleEx = True
End If
End Function
Edit 3: The taskmanager says that Calculator.exe and Groove have the status "Suspended".

VB6 Don't Auto-Activate Child Form on Form-Resize Windows 10

A window in this VB6 legacy system that I'm supporting hosts a Word 2007 instance as a child window. On Windows 7, the parent window can be resized without any issues: the window doesn't update until the mouse is released after resizing. On Windows 10, however, the window updates dynamically while being resized.
The issue I'm encountering in Windows 10 is that the child Word window is getting activated/focused upon the first update: you can only drag to resize the parent window by a couple pixels at a time, before the child window gets focused and the resize event on the parent window is canceled (the cursor is still on the resize icon, but continued dragging has no effect). Maximizing, minimizing, and restoring the parent window all work normally. Once the child Word window is closed (through the file menu in Word), the parent window can be resized normally, because there's no child window to activate/focus. The same automatic-child-window-activation-after-parent-window-resizing occurs in Windows 7, but because the resize event doesn't fire until after the parent window has actually updated, it's not an issue there.
My conundrum is that I don't see anything in the code that suggests why the child window is getting automatically activated/focused, unless that's just default Windows behavior. In either case, I'm pretty sure I need a way to make that not happen.
All that this code is explicitly doing (primarily the ResizeControls() sub; the rest is here mostly for context) is resizing/positioning the Word window to correspond to the new size of the container in the parent window, which is consistent with the behavior in Windows 7.
From what I can tell, I don't believe that GetWindow() actually activates the window it gets a handle to, but if it does, then that's likely the cause of the issue, in which case I need to be able to get a handle to the window without activating it.
PDFView.frm:
Begin VB.Form frmPDFView
Caption = "Untitled"
ClientHeight = 8655
ClientLeft = 1320
ClientTop = 1665
ClientWidth = 9270
' ...
Begin VB.PictureBox picContainer
BackColor = &H00FFFFFF&
Height = 4215
Left = 1080
ScaleHeight = 4155
ScaleWidth = 4995
TabIndex = 0
Top = 120
Width = 5055
End
End
Private Sub ResizeControls()
On Error Resume Next
Dim pWndChild As Long
Dim r As RECT
Dim rtn As Long
picContainer.Left = 100
picContainer.Height = Me.Height - 1300
picContainer.Width = Me.Width - 350
picContainer.Top = 300
pWndChild = GetWindow(picContainer.hWnd, GW_CHILD)
rtn = GetLastError
If (pWndChild) Then
rtn = GetClientRect(picContainer.hWnd, r)
rtn = SetWindowPos(pWndChild, 0, 0, 0, r.Right - r.Left, r.Bottom - r.Top, SWP_NOZORDER Or SWP_NOMOVE)
Else
rtn = GetLastError
End If
End Sub
Private Sub Form_Resize()
On Error GoTo ERROR_HANDLER
Call ResizeControls
Exit Sub
ERROR_HANDLER:
Err.Clear
Resume Next
End Sub
Turns out I'm blind and/or didn't read the documentation thoroughly enough for all the Windows functions used in the code. Found the solution the next day and forgot to come back and answer this, but as confirmed by #wqw's comment, the issue was with SetWindowPos(). The SWP_NOACTIVATE flag needed to be passed in to SetWindowPos() to prevent activation of the target window (in my case, the child Word window).

How to compute the actual height of the text in a static control

My simple Win32 DialogBox contains two static text controls (IDC_STATIC_TITLE and IDC_STATIC_SECONDARY), here's what it looks like in the resource editor:
At run time, the text first string is updated dynamically. Also, the font of the that text string is replaced such that it's bigger than the IDC_STATIC_SECONDARY string below it. The resulting text string might span a single line, two lines, or more.
I want the other static control holding the secondary text to be placed directly underneath the title string at run time. However, my resulting attempt to re-position this control in the WM_INITDIALOG callback isn't working very well. The second string is overlapping the first. I thought I could use DrawText with DT_CALCRECT to compute the height of the primary text string and then move the secondary text string based on the result. My code is coming up a bit short as seen here:
DrawText returns a RECT with coordinates {top=42 bottom=74 left=19 right=461} Subtracting bottom from top is "32". That seems a little short. I suspect I'm not invoking the API correctly and/or an issue with the different mappings between logical and pixel units.
Here's the relevant ATL code. The CMainWindow class just inherits from ATL's CDialogImpl class.
CMainWindow::CMainWindow():
_titleFont(NULL),
_secondaryFont(NULL)
{
LOGFONT logfont = {};
logfont.lfHeight = 30;
_titleFont = CreateFontIndirect(&logfont);
}
LRESULT CMainWindow::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CString strTitle;
RECT rectDrawText = {}, rectTitle={}, rectSecondary={};
CWindow wndTitle = GetDlgItem(IDC_STATIC_TITLE);
CWindow wndSecondary = GetDlgItem(IDC_STATIC_SECONDARY);
this->GetDlgItemText(IDC_STATIC_TITLE, strTitle);
wndTitle.SetFont(_titleFont); // font created with Create
wndTitle.GetWindowRect(&rectTitle);
wndSecondary.GetWindowRect(&rectSecondary);
ScreenToClient(&rectTitle);
ScreenToClient(&rectSecondary);
rectDrawText = rectTitle;
DrawText(wndTitle.GetDC(), strTitle, strTitle.GetLength(), &rectDrawText, DT_CALCRECT|DT_WORDBREAK); // compute the actual size of the text
UINT height = rectSecondary.bottom - rectSecondary.top; // get the original height of the secondary text control
rectSecondary.top = rectDrawText.bottom; // position it to be directly below the bottom of the title control
rectSecondary.bottom = rectSecondary.top + height; // add the height back
wndSecondary.MoveWindow(&rectSecondary);
return 0;
}
What am I doing wrong?
Despite what its name may make it sound like, wndTitle.GetDC() doesn't return some pointer/reference that's part of the CWindow and that's the same every call. Instead, it retrieves a brand new device context for the window each time. (It's basically a thin wrapper for the GetDC() Windows API call, right down to returning an HDC instead of the MFC equivalent.)
This device context, despite being associated with the window, is loaded with default parameters, including the default font (which IIRC is that old "System" font from the 16-bit days (most of this screenshot)).
So what you need to do is:
Call wndTitle.GetDC() to get the HDC.
Call SelectObject() to select the correct window font in (you can use WM_GETFONT to get this; not sure if MFC has a wrapper function for it), saving the return value, the previous font, for step 4
Call DrawText()
Call SelectObject() to select the previous font back in
Call wndTitle.ReleaseDC() to state that you are finished using the HDC
More details are on the MSDN page for CWindow::GetDC().

How can I tell if a menu is open in VB6?

I've got a timer set up to detect if the mouse is over a certain area on my form, which you can imagine to be a rectangle starting at 50,50 (pixels) and ending at 1000,500. If the mouse is inside that rectangle, a second window pops up that acts somewhat like a tooltip, following the mouse around. The problem is that the menus at the top drape over this rectangle, and if you try to use a menu, the second window pops up (the timer sets its visible property to true) as soon as you move down the menu, which ends up closing the menu (I guess due to a loss of focus or something.)
If I can detect when one of the menus is open, I can disable the showing of the tooltip window with an if statement, but I don't know how to do that.
I think I've figured out how to do this by searching WIN32API.txt for "menu" and a little bit of googling, but I'm not really sure. Perhaps this solution only works on my machine.
Putting this code...
Dim hMenu As Long
hMenu = GetMenu(Form1.hwnd)
MsgBox GetMenuState(hMenu, 0, MF_BYPOSITION)
on a timer with an interval of 5000 allows you to the view the menu's state. In a closed state, the number appears to be random (1552, 1296, etc.,) but when the menu is opened, it is offset by 128 from this base value. A menu whose state is 1552 when closed is 1680 when open.
I'm not sure why it's offset by 128 or if this works on all machines (just to be safe, I will program it to check for inequality, not offset by 128), but it appears to be working for me.
If there is a problem with this solution or if there is a better way, please respond with another answer, and I'll be glad to give you credit for the answer instead.
Written by SBEIH Iyad - Syria - Damascus. 4/1/2021.
Using VB6.0, we can.
We check all menus windows if one is opened, it means: menu is opened.
we use API "GetMenu" for hWnd of main-menu,
then with the API "GetMenuState" we check if one of menus is opened.
Vb6.0 CODE:
Private Function GetBit_I(ByVal X As Long, ByVal i As Integer) As Integer
' Get the bit number i of X.
GetBit_I = (X And (2 ^ i)) / (2 ^ i)
End Function
Private Function MainMenuIsOpened(FRM As Form) As Boolean
Const MF_BYPOSITION = 1024
Dim H As Long, i As Integer, L As Long, MCount As Long
MainMenuIsOpened = False
On Error GoTo MainMenuIsOpenedError
H = GetMenu(FRM.HWnd)
MCount = GetMenuItemCount(H)
' MCount is the number of main-menus.
Do While (i < MCount)
L = GetMenuState(H, i, MF_BYPOSITION)
If ((L > -1) And (GetBit_I(L, 7) = 1)) Then
MainMenuIsOpened = True
Exit Do
End If
i = i + 1
Loop
Exit Function
MainMenuIsOpenedError:
MainMenuIsOpened = False
End Function
Good luck.

Capturing a Window that is hidden or minimized

I followed this tutorial (there's a bit more than what's listed here because in my code I get a window via mouse click) for grabbing a window as a bitmap and then rendering that bitmap in a different window.
My question:
When that window is minimized or hidden (SW_HIDE) my screen capture doesn't work, so is it possible to capture a window when it is minimized or hidden?
The PrintWindow api works well, I use it for capturing thumbnails for hidden windows. Despite the name, it is different than WM_PRINT and WM_PRINTCLIENT, it works with pretty much every window except for Direct X / WPF windows.
I added some code (C#) but after reviewing how I used the code, I realized that the window isn't actually hidden when I capture its bitmap, its just off screen so this may not work for your case. Could you show the window off screen, do a print and then hide it again?
public static Bitmap PrintWindow(IntPtr hwnd)
{
RECT rc;
WinUserApi.GetWindowRect(hwnd, out rc);
Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap = gfxBmp.GetHdc();
bool succeeded = WinUserApi.PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
if (!succeeded)
{
gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
}
IntPtr hRgn = WinGdiApi.CreateRectRgn(0, 0, 0, 0);
WinUserApi.GetWindowRgn(hwnd, hRgn);
Region region = Region.FromHrgn(hRgn);
if (!region.IsEmpty(gfxBmp))
{
gfxBmp.ExcludeClip(region);
gfxBmp.Clear(Color.Transparent);
}
gfxBmp.Dispose();
return bmp;
}
There are WM_PRINT and WM_PRINTCLIENT messages you can send to the window, which cause its contents to be rendered into the HDC of your choice.
However, these aren't perfect: while the standard Win32 controls handle these correctly, any custom controls in the app might not.
I am trying to get the bitmap of partially hidden controls.
I used code before that did the drawing, but included windows overlapping it. So.. maybe you want to try this.
The WM_PRINTCLIENT should (in my understanding) redraw all inside the control, even if it is not really visible.
const int WM_PRINT = 0x317, WM_PRINTCLIENT = 0x318, PRF_CLIENT = 4,
PRF_CHILDREN = 0x10, PRF_NON_CLIENT = 2,
COMBINED_PRINTFLAGS = PRF_CLIENT | PRF_CHILDREN | PRF_NON_CLIENT;
SendMessage(handle, WM_PRINTCLIENT, (int)hdc, COMBINED_PRINTFLAGS);
//GDIStuff.BitBlt(hdc, 0, 0, width, height, hdcControl, 0, 0, (int)GDIStuff.TernaryRasterOperations.SRCCOPY);
The before code is commented out now. It is based on the code found here: Pocket PC: Draw control to bitmap (accepted answer). It is basically the same as Tim Robinson suggests in this thread.
Also, have a look here
http://www.tcx.be/blog/2004/paint-control-onto-graphics/

Resources