How can it be possible to steal focus so easily? - windows

Using Outlook 2013 on Windows 7, I created this Macro, just to test it:
Private Sub Application_Reminder(ByVal Item As Object)
Activeexplorer.Activate
End Sub
Whenever I am working on a different application and an Outlook reminder fires, Outlook becomes the active window stealing focus from the application I am working at.
How can it be possible??
I mean, I think that the “Activeexplorer.Activate” method uses some Windows api like “SetForegroundWindow” or maybe “SetActiveWindow” or some other api.
All these apis forbid to steal focus, so my question is how the Outlook vba method can so easily and horrifyingly able to steal focus?
The alarm is partially disarmed, as stated at https://msdn.microsoft.com/it-it/library/windows/desktop/ms633539(v=vs.85).aspx, SetForegroundWindow can be used by other processes not in the foreground if “The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).”
I changed the value of the registry key 'ForegroundLockTimeout' at 'HKCU:\Control Panel\Desktop' from zero to 20000 and now, in my specific case, Outlook won’t steal the focus.
I wonder which is the maximum value for the 'ForegroundLockTimeout' registry key, or, in other words, if it is possible to permanently disable any other process not in the foreground stealing focus from the active application.

Here's the reason Windows was not behaving as expected:
I changed the value of the registry key 'ForegroundLockTimeout' at 'HKCU:\Control Panel\Desktop' from zero to 20000
The value shouldn't have been zero in the first place. Something on your system, possibly a long time ago, must have explicitly changed this setting in order to disable the foreground lock. This has nothing to do with Outlook per se.
I recommend you set it back to the default, which is 200,000, i.e., 200 seconds.
As for the maximum, well, it has to fit into a DWORD, so probably about 49 days. If it is treated as a signed value internally, about 24 days. There's probably little point in setting it longer than a day.

Yes, SetForegroundWindow is supposed to respect the foreground window and just flash the taskbar for background applications but there are various hacks people use to trick Windows and steal the focus.
My preferred method of notifying the user about something important is to bring the window to the top without stealing the keyboard focus. The tricky bit would be to figure out which HWND to pass, I could not really find the HWND property of the ActiveExplorer form just by looking on MSDN.
Const HWND_TOP = 0
Const SWP_NOSIZE = &H1
Const SWP_NOMOVE = &H2
Const SWP_NOACTIVATE = &H10
Const SWP_SHOWWINDOW = &H40
Private Declare Sub SetWindowPos Lib "User32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long)
...
SetWindowPos ??.hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE
Another alternative is to call FlashWindow to flash the taskbar button which is what SetForegroundWindow would do if you don't have the right to grab focus.

Related

VB6 Label max string length?

I have a project for fun in VB6, I'm using quite long labels and would like them to stretch the entire length of my monitor however they seem to be capped at 256 chars per line. It let's me set their caption to as long as I like but after the 256th character, the rest does not appear on the screen.
If I change it to multiline however, it will display the full text but again will automatically take a new line at the 256th character meaning it doesn't utilise the full width of my monitor.
I was wondering if anyone knows why this is, a way around it or what my options are?
Edit: After testing, using a text box and making it look like a label is an alright work around, as text boxes don't seem to have this same restriction.
Edit 2: Text boxes lack an autosize function which is crucial to my project so any further advice is appreciated.
According to the MSDN documentation:
A Label control’s caption size is unlimited. For forms and all other
controls that have captions, the limit is 255 characters.
However, as you have seen this statement may not be correct. It appears to apply to Label controls as well, and the limit is actually 256 characters in my experimentation.
I think your idea of a TextBox control should work for what you need. Since there is no AutoSize property, simply change the width of the control in the Form Resize event.
Private Sub Form_Resize()
Text1.Width = Me.ScaleWidth
End Sub
Adding onto Brian's answer, yes, you will need to use a TextBox if you want your "label" to be more than 255 characters. You can make a TextBox look and act like a label if you set a few things.
First, set the BorderStyle property to vbBSNone (or 0, if you prefer). Then, you don't want users entering text into and otherwise changing your "label." If you're not fussy, you can set the Locked property to true. This isn't perfect, because setting the Locked property still allows users to click on the text and move around in it.
If you're really not fussy, you can set Enabled to false. This can confuse users, because everything gets greyed out and users are conditioned to understand that to mean that something is disabled. However, a disabled control can't be landed on or tabbed to, which is the behavior you want for a label.
If you want to get disabled behavior without altering the appearance, you need to use the API:
Private Const WS_DISABLED = &H8000000
Private Const GWL_STYLE = -16
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Sub Form_Load()
Dim theStyle As Long
theStyle = GetWindowLong(myTextBox.hwnd, GWL_STYLE) Or WS_DISABLED
Call SetWindowLong(myTextBox.hwnd, GWL_STYLE, theStyle)
End Sub
Pretty straightforward. GWL_STYLE is the index for the window's style properties. It's a hex value that amounts to a series of flags. If you Or the WS_DISABLED hex value with it, the result is to set the disabled flag. Which gets set when you set the window with the new value for GWL_STYLE.
Here are the different settings handled by GW_STYLE.

I can't subclass MS Access form if the window of IDE has been opened

What I am doing is by using SetWindowLong I change an address of an original window procedure of a window(a form in my case) to an address of my own WindowProc, then I process one of system messages and finally I pass the intercepted message to the original window procedure.
I tested intercepting messages subclassing:
A window of MS Access instance
A window of a form in an overlapping interface
Both of the tests go well only if I run the main Access window without opening an IDE window before or during the test. The trouble happens if at the time of testing not only MS Access main window is open but a window of IDE too.
The problem doesn’t arise in a first case (intercepting messages of MS Access window). While as the second case is a pain in the neck indeed (intercepting messages of a form in an overlapping interface). What happens is MS Access stops responding to user’s input. It looks like it gets looping and looping endlessly. I’d like to stress once more that such behavior takes place only when MS Access window and window of IDE are open at the same time.
I made a test file so that you can see it for yourself. I uploaded it here:
https://www.dropbox.com/s/8lnqfgb1hn3zqy8/SetWindowLong.accdb?dl=0
So, the first case(when there is no problem):
Open the file (DO NOT open IDE window till the end of the test)
Open Form1(on opening the system messages to MS Access window will start being intercepted)
Close Form1(on closing intercepting will be stopped)
Open Form 2(on opening the system messages to Form2 window will get being intercepted)
Close Form2(on closing intercepting will be stopped)
So far so good.
Now the second case (the pain in the neck):
Open the file
Open IDE window (ALT+F11)
Open Form1(on opening the system messages to MS Access window will get being intercepted)
Close Form1(on closing intercepting will be stopped)
Open Form 2(on opening the system messages to Form2 window are intended to get being intercepted, but you will witness an endless looping and no response from Access)
And now are my questions:
How come an open window of IDE can interfere in my subclassing of Form2 and does not affect the subclassing of MS Access window.
What can be done so that I was able to subclass Form2 while the window of IDE stayed open
I will be very grateful for your advice.
Edited on 2017/08/25:
Sorry for not providing my code at the beginning.
In a standard module of MS Access:
Option Compare Database
Option Explicit
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public lngPrevWndProc As Long
Public Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WndProc = CallWindowProc(lngPrevWndProc, hWnd, uMsg, wParam, lParam)
End Function
In a code module of Form2:
Option Compare Database
Option Explicit
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_WNDPROC = -4
Private Sub Form_Open(Cancel As Integer)
lngPrevWndProc = SetWindowLong(Me.hWnd, GWL_WNDPROC, AddressOf WndProc)
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetWindowLong Me.hWnd, GWL_WNDPROC, lngPrevWndProc
End Sub
This sample intended to work on a 32bit Windows and in 32bit MS Access. I tested it on:
Windows XP Pro + MS Access 2000 (the file was converted to mdb); Windows 7 32bit + MS Access 2010 32bit (accdb); indows 10 32bit + MS Access 2016 32bit (accdb)
The result was the same as I described before.
Sergiy Vakshul

How can I force a vb6 control to redraw itself?

I've got a VB6 program. I'm using the Mainfest to apply "XP Themes" and give it the modern (as of 8 years ago!) look.
However, for graphical style Command buttons, I have to use some special code to redraw the button. Therein lies the problem.
When I click on one of this Graphical buttons it gets the proper "highlighting" of the background, but when another button gets the focus or mouseover, etc. that former button keeps the background highlighting.
If I move another window in front of it, the form redraws itself and this "residual" background color disappears.
I'm trying to figure out how to force that to happen.
What I've tried:
button.refresh
form.refresh
Doevents
Here is a video demo of the problem
I don't have anything like your setup to try this in, but you can try using the API call InvalidateRect. I've shown the declarations and created a Sub that uses it. It should be a simple copy and paste to try.
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Sub RefreshMe()
Dim udtRect As RECT
Call GetClientRect(Me.hwnd, udtRect)
InvalidateRect Me.hwnd, udtRect, 1
DoEvents 'give windows a chance to handle the event
End Sub

Running an invisible form on the foreground

I'm wondering how I would be able to run an invisible form in the foreground (meaning it runs wherever the heck you might be in your computer). I've tried playing around with formless applications using modules but unfortunately nothing happens. I've also tried doing
Form1.Visible = False
Form1.WindowState = 2 (Maximized)
But it WILL NOT run in the foreground (It will if it's focused on) and also the Windows "warning error" sort of sound (the "ehh." sort of sound in monotone) keeps playing when you hit a key. The coding I tried out is this in case you want to know:
Private Sub Form1_KeyPress(KeyAscii As Integer, Shift As Integer)
If KeyAscii <> 0 Then PlaySound App.Path & "\sounds\ding.wav", ByVal 0&, SND_FILENAME Or SND_ASYNC
End Sub
And of course using the Windows APIs for playing wav files (winnm.dll or something I believe)
My main question would be (which means THIS is what I want answered) is how I could possibly make it run ANYWHERE on the computer and not make "ehh." monotone sounds. Because That is obviously not what I wanted to achieve.
Oh, and quick update, I've made it play the proper sound (HOORAY!). Just disregard the latter part of the question.
Please tell us why you want to do this?
What are you trying to accomplish?
Have a look at the following API:
'api for keeping form on top
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
For example call it as follows:
'put form always on top
With Screen
SetWindowPos Me.hwnd, -1, 0, 0, .Width, .Height, 3
End With 'screen

How do I programmatically change the "underline keyboard shortcuts" Control Panel setting?

In the "Control Panel > Ease of Access Centre > Make the keyboard easier to use" is an option to "Underline keyboard shortcuts and access keys."
Is there a way of programmatically switching this on and off?
I'm using Visual Basic Scripts, but can use .NET.
Run Registry Editor and go to HKEY_CURRENT_USER\Control Panel\Accessibility\Keyboard Preference
Now create or modify a String Value (REG_SZ) called On and set its value to 1
Information is comming from:
http://www.windowsvalley.com/get-underlined-keyboard-shortcuts-and-access-keys-permanently/
AFAIK, there's no way to toggle this option programmatically except for automating the approproate GUI actions (opening the Control Panel, switching the option on/off and applying the changes). In this case, I'd recommend using AutoIt to automate the option switching.
It turns out you CAN programatically change the “underline keyboard shortcuts” option in your own application. You need to send the WM_UPDATEUISTATE message to your main form according to the documentation found at: https://learn.microsoft.com/en-us/windows/win32/menurc/wm-updateuistate
Since you mentioned Visual Basic, here's how to do it:
Private Const WM_UPDATEUISTATE = &H128
Private Const UIS_CLEAR = &H2
Private Const UISF_HIDEACCEL = &H2
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hWnd As
Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Then in the "Form_Load" event send the message and it will activate keyboard shortcuts underlining for all controls and menus present on that form:
Private Sub Form_Load()
PostMessage Me.hWnd, WM_UPDATEUISTATE, UIS_CLEAR + UISF_HIDEACCEL * 65536, 0
End Sub

Resources