How can I tell if a menu is open in VB6? - 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.

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).

Jaspersoft iReports designer 5.6.0; how to fix size of expression editor window

As I am working mainly with large expressions within iReport designer, I want to fix the window size of the expression editor of the iReport designer to a larger size compared with what it defaults to.
Each time I open the expression editor within the iReport designer, it defaults to a relatively small window and then I always have to re-arrange it. As a double click on the title bar doesn't do anything, this is using a considerable amount of time when I have to do this 100ths of time each day.
Is there a way to set the position, width and height of the iReport designer 5.6.0 (I don't use Jaspersoft Studio because of its even more time-consuming file save behavior) so that next time I open the expression editor it will use this position, width and height?
(I have already searched the internet, also here on SO - but nobody seems to have this problem)
As I could nowhere find an answer to this I have quickly developed a workaround with AutoIt.
;Here the AutoIt code:
; *** Variables ***
$FrameInPercent = 10
do
$hWindow = WinWaitActive("[CLASS:SunAwtDialog;TITLE:Expression editor]", "") ; *** Wait here until the Expression editor window is opened ***
WinActivate($hWindow)
; *** Calculate distance from screen edge for Expression editor window ***
$DeskSize = _GetWorkingArea()
$WindowWidth = $DeskSize[2]-($DeskSize[2]*(0.01*2*$FrameInPercent))
$WindowHeight = $DeskSize[3]-($DeskSize[3]*(0.01*2*$FrameInPercent))
$windowStartLeft = ($DeskSize[2]*(0.01*$FrameInPercent))
$windowStartTop = ($DeskSize[3]*(0.01*$FrameInPercent))
WinMove($hWindow, "",$windowStartLeft, $windowStartTop, $WindowWidth, $WindowHeight)
;WinMove($hWindow, "",71, 116, 1815, 863)
Do
Sleep(1000) ; calmly idle around as long as the Expression editor window is open
until NOT(WinExists($hWindow))
until False ; *** Loop forever ***
;===============================================================================
;
; Credits for this function goes to: https://www.autoitscript.com/forum/topic/53788-getting-windows-desktop-size/
; Function Name: _GetWorkingArea()
; Description: Returns the coordinates of desktop working area rectangle
; Parameter(s): None
; Return Value(s): On Success - Array containing coordinates:
; $a[0] = left
; $a[1] = top
; $a[2] = right
; $a[3] = bottom
; On Failure - 0
;
;===============================================================================
Func _GetWorkingArea()
#cs
BOOL WINAPI SystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni);
uiAction SPI_GETWORKAREA = 48
#ce
Local $dRECT = DllStructCreate("long; long; long; long")
Local $spiRet = DllCall("User32.dll", "int", "SystemParametersInfo", _
"uint", 48, "uint", 0, "ptr", DllStructGetPtr($dRECT), "uint", 0)
If #error Then Return 0
If $spiRet[0] = 0 Then Return 0
Local $aRet[4] = [DllStructGetData($dRECT, 1), DllStructGetData($dRECT, 2), DllStructGetData($dRECT, 3), DllStructGetData($dRECT, 4)]
Return $aRet
EndFunc
In order to use this you have to install AutoIt: https://www.autoitscript.com/site/autoit/downloads/
And then run this script (doubleclick it). The script will run forever but will not take much CPU power as it is event driven while waiting for the Expression editor to appear and while the expression editor is open, it only scans every 1 second if this window is still open.
Hope this helps someone.
As this is my first AutoIt script, optimizations are welcome :-)

Inconsistency with control location and control appearance

I have my "about" form looking like this on my computer (Win7 32 bits) and in the IDE
And on at least one PC (under Win7 64 bits), it looks like that :
2 questions about this :
Why is my OK button not appearing when there is nothing to hide it in my code? How can I solve that? The OK button appears when I click the other button or when I click on its location.
Why are my buttons and my labels shifted to the right, going out of the form I designed in VB6? There is nothin in my code to change their position.
A bit of information :
My buttons are inside a "Frame" control. This frame control is supposed to be 840 twips (VB6 unit...Why couldn't they use pixel?!) from the left and 525 from top. My labels are not inside anything.
Thank you.
Edit : the only code managing the windows is in the form_load. FraAbout is containing the buttons you see above, FraSplash contains a progressbar. The ZOrder make buttons always appear on top.
Private Sub Form_Load()
On Error Resume Next
Timer1.Enabled = False
LblRevision = App.FileDescription
SendMessage pb1.Hwnd, PBM_SETBARCOLOR, 0, ByVal RGB(114, 191, 68)
FraAbout.Visible = False: FraSplash.Visible = False
Me.ForeColor = 0: Me.DrawStyle = 0: Me.BorderStyle = vbFixedSingle: Me.Refresh
If AppLoaded Then
FraAbout.Visible = True
Else
t = 0: pb1.value = pb1.min: FraSplash.Visible = True: Timer1.Enabled = True
End If
End Sub
This all came from... my configuration panel. Here is how to fix the problem if you have it. I was at 125, just put it back to 100 :

Visual Basic: Increasing integer every 3 clicks

So my question is pretty much what the title says: how do I get a certain integer to increase by 1 for every three clicks of a button? For example, let's say the integer is 900. I click button1 once, then again, then again, and on the third click the integer changes to 901. How can this be accomplished?
Thanks in advance.
Use a counter that is actually three times larger behind the scene. Start at 2700 instead of 900, and increase the value by one for each click.
When you want to display the value that increases every third click, you divide the counter by three:
displayValue = counter \ 3
Try this maybe this could help or give you idea
Dim click = 0
Dim num = 900
if (click % 3 = 0)
num+=1
else
click+=1
end if

Resources