Community Pick: Many members of our community have endorsed this article.
Editor's Choice: This article has been selected by our editors as an exceptional contribution.
Configuring XMouse Behavior in Windows via VB.Net and the SystemParametersInfo() API
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Our community of experts have been thoroughly vetted for their expertise and industry experience.
Published:
Updated:
Browse All Articles > Configuring XMouse Behavior in Windows via VB.Net and the SystemParametersInfo() API
What is XMouse behavior?
"XMouse" behavior is a system whereby windows receive focus when the mouse enters them, as opposed to requiring the user to physically click on them. Focus is automatically given to a window when the mouse enters it, and stays within for longer than the pre-defined threshold delay. When the window receives focus, it can also optionally be brought to the top of the z-order, in front of all other windows.
Why would I want to use XMouse?
Using an XMouse setup can reduce the number of clicks and amount of time needed to accomplish a task. For example, when I reconcile my checkbook, I have my bank statement open in my primary monitor, and MS Money open in my secondary monitor. Balancing the accounts requires frequent scrolling in both windows to find matches, but clicks are only necessary in the secondary monitor to place a checkmark next to records in MS Money that appear in the bank statement. Thus the only interaction required in the primary monitor is scrolling that window with the mousewheel on my mouse. With default mouse operations in Windows, though, one must click the bank statement window before it can be scrolled. Therefore each and every time I want to scroll the opposite screen, I must first execute a click on it. More often than I'd like to admit, I actually forget to click on that opposite screen, resulting in the wrong window being scrolled when I use the mousewheel! With XMouse behavior activated, however, that missed click becomes moot since all I have to do is move my mouse into that window, wait a split second, and then scroll away. This saves a lot of time and clicking over a long period of time.
Why isn't it XMouse on all the time by default?
XMouse isn't enabled all the time, by default, since some applications and features of Windows itself don't always work well with it. Some types of windows are displayed as popups that immediately hide when something else gets focus. In Vista and above, the calendar that gets displayed when single clicking the clock in the tray is one example of this type of popup window. Thus when attempting to move the mouse to those popups, the space in between gets focus causing the popup to disappear before you can even reach it. When this occurs, either the activation delay needs to be increased, or Xmouse needs to be turned off completely. Unfortunately, with a default installation of Windows, the only way to increase the delay is by manually changing the registry. (*The activation and bring to the front features can also be set via the Registry.)
How do I turn XMouse on or off?
Turning Xmouse activation on and off has actually been available in Windows since Windows 95. In Win95/98/ME/XP, it could be set via either PowerToys or TweakUI, two free tools provided by Microsoft. In Vista/Win7/Win8, an external tool isn't required as it's available through the "Activate a window by hovering over it with the mouse" option located in the Control Panel's Ease of Access Center. Opening up the control panel up isn't very convenient though. It would be nice to have an icon in the tray that can be clicked to change the XMouse settings. Thankfully, it is possible to modify these settings from a VB.Net application using the SystemParametersInfo() API.
The parameters relevant to XMouse settings fall under the Windows category, and are as follows:
' Get or Set whether active window tracking is enabled:
SPI_GETACTIVEWINDOWTRACKING, SPI_SETACTIVEWINDOWTRACKING
' Get or Set the delay before a window receives focus:
SPI_GETACTIVEWNDTRKTIMEOUT, SPI_SETACTIVEWNDTRKTIMEOUT
' Get or Set whether the window is brought to the front:
SPI_GETACTIVEWNDTRKZORDER, SPI_SETACTIVEWNDTRKZORDER
Importing the SystemParametersInfo() API
To utilize the SystemParametersInfo() API in our VB.Net application, we first need to import it. Here is the declaration of SystemParametersInfo() used for setting a value:
Private Declare Function SetSystemParametersInfo Lib "user32" Alias _ "SystemParametersInfoA" (ByVal uAction As Integer, ByVal uParam As Integer, _ ByVal lpvParam As Integer, ByVal fuWinIni As Integer) As Integer
The "SetSystemParametersInfo" portion immediately after the word "Function" indicates the name that will be used locally in the application to access the API. This name can be anything you want, and can even match the name of the API you are importing. The value of "user32" after the word "Lib" indicates the actual Windows Library DLL that contains the API you want to import. Note that we don't have to specify a path if its located in the standard windows folders. The "SystemParametersInfoA" value after the term "Alias" indicates the actual function name we are using inside the previously specified library (user32).
The values listed after the Alias portion indicate the parameters that SystemParametersInfo() expects to be passed in. The first parameter, "uAction", tells SystemParametersInfo() what particular setting you are accessing, such as SPI_SETACTIVEWINDOWTRACKING. The second parameter called "uParam" is not used for these settings and will just be passed zero. The third parameter of "lpvParam" is used to pass in a value when setting something, or to receive a value when retrieving a setting. The last parameter "fuWinIni" is used to specify if the user profile should be updated with the new setting, and whether a WM_SETTINGCHANGE message should be sent to all top level windows with the SPIF_SENDCHANGE flag. This last value can be set to zero if the user profile should not be updated and you do not want to notify other windows.
Note that I said the above declaration of SystemParametersInfo() was used for SETTING a value. When setting a value, the third parameter called "lpvParam" is passed BYVAL. When you want to retrieve a value such as with SPI_GETACTIVEWINDOWTRACKING, however, the third parameter of "lpvParam" is passed BYREF instead of ByVal so that the local variable in your application can be updated with the desired setting:
Private Declare Function GetSystemParametersInfo Lib "user32" Alias _ "SystemParametersInfoA" (ByVal uAction As Integer, ByVal uParam As Integer, _ ByRef lpvParam As Integer, ByVal fuWinIni As Integer) As Integer
This declaration is exactly the same as the previous one, except that "SetSystemParametersInfo" has been changed to "GetSystemParametersInfo", and "ByVal" has been changed to "ByRef" for third parameter "lpvParam". Thus within the application we will use two different names to access the exact same API. This is necessary to differentiate if we want the third parameter to be passed ByVal or ByRef.
Using the SystemParametersInfo() API
It's time to start demonstrating some actual code. Here's a snippet that shows how to retrieve whether XMouse behavior (active window tracking) is currently enabled or not:
Dim tracking As BooleanGetSystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING, 0, tracking, 0)MessageBox.Show("tracking = " & tracking)
The SPI_SETACTIVEWNDTRKZORDER option allows you to toggle whether the window that gets active focus is also brought to the front of the z-order. When this value is set to false, moving the mouse into a window will give it focus, but it will stay in its current z-order and not come to the front. This may mean that a partially obscured window gets focus while it is still behind another window, which can take quite awhile to get used to! Conversely, when this value is set to True, the window receiving XMouse focus is also brought to the top of other windows as one would expect it to. The use of SPI_GETACTIVEWNDTRKZORDER / SPI_SETACTIVEWNDTRKZORDER is exactly the same as the already demonstrated SPI_GETACTIVEWINDOWTRACKING / SPI_SETACTIVEWINDOWTRACKING settings above.
The last setting of SPI_SETACTIVEWNDTRKTIMEOUT allows you to set how long the mouse must be in a window before triggering XMouse behavior. Again, it works exactly like the other two settings, but instead of passing True/False you'd pass an integer value representing the delay in milliseconds. Thus setting this value to 500 would mean the mouse would need to be in the window for a 1/2 second before activating it. Finding the "sweet spot" for this delay is tricky. If you make it too large then XMouse becomes a hindrance. Imagine needing to hover for a full two seconds before a window is activated. This would most likely slow down a process instead of making it more efficient. At the opposite end of the spectrum, setting the delay to 0 (zero) makes whatever window you are in immediately get focus. This makes it impossible, though, to interact with certain types of windows that "popup" and go away when something else gets focus, since they disappear before you ever get a chance to interact with them. Values between 250 and 500 seem to work for most people. I personally like the delay set at 350:
Putting it all together in an XMouse Utility Class
While the SystemParametersInfo() API is indeed easy to use, it would still be nice to neatly wrap all those calls in a utility class. Here is what I came up with:
Public Class XMouse Private Const SPI_GETACTIVEWINDOWTRACKING As Integer = &H1000 Private Const SPI_SETACTIVEWINDOWTRACKING As Integer = &H1001 Private Const SPI_GETACTIVEWNDTRKTIMEOUT As Integer = &H2002 Private Const SPI_SETACTIVEWNDTRKTIMEOUT As Integer = &H2003 Private Const SPI_GETACTIVEWNDTRKZORDER As Integer = &H100C Private Const SPI_SETACTIVEWNDTRKZORDER As Integer = &H100D Private Const SPIF_SENDCHANGE As Integer = &H2 Private Const SPIF_UPDATEINIFILE As Integer = &H1 ' Used for Retrieving values, note though "ByRef" for lpvParam! Private Declare Function GetSystemParametersInfo Lib "user32" Alias _ "SystemParametersInfoA" (ByVal uAction As Integer, ByVal uParam As Integer, _ ByRef lpvParam As Integer, ByVal fuWinIni As Integer) As Integer ' Used for Setting values, note though "ByVal" for lpvParam! Private Declare Function SetSystemParametersInfo Lib "user32" Alias _ "SystemParametersInfoA" (ByVal uAction As Integer, ByVal uParam As Integer, _ ByVal lpvParam As Integer, ByVal fuWinIni As Integer) As Integer Public Class XSettings Public IsTracking As Boolean Public DelayInMilliseconds As Integer Public BringToFront As Boolean Public Sub New() End Sub Public Sub New(ByVal IsTracking As Boolean, ByVal DelayInMilliseconds As Integer, ByVal BringToFront As Boolean) Me.IsTracking = IsTracking Me.DelayInMilliseconds = DelayInMilliseconds Me.BringToFront = BringToFront End Sub Public Overrides Function ToString() As String Return "IsTracking = " & IsTracking & ", DelayInMilliseconds = " & DelayInMilliseconds & ", BringToFront = " & BringToFront End Function End Class Public Shared Property IsTracking() As Boolean Get Dim tracking As Boolean GetSystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING, 0, tracking, 0) Return tracking End Get Set(value As Boolean) SetSystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, value, SPIF_UPDATEINIFILE Or SPIF_SENDCHANGE) End Set End Property Public Shared Property DelayInMilliseconds() As Integer Get Dim timeout As Integer GetSystemParametersInfo(SPI_GETACTIVEWNDTRKTIMEOUT, 0, timeout, 0) Return timeout End Get Set(value As Integer) If value >= 0 AndAlso value <= 2000 Then SetSystemParametersInfo(SPI_SETACTIVEWNDTRKTIMEOUT, 0, value, SPIF_UPDATEINIFILE Or SPIF_SENDCHANGE) End If End Set End Property Public Shared Property BringToFront() As Boolean Get Dim raise As Boolean GetSystemParametersInfo(SPI_GETACTIVEWNDTRKZORDER, 0, raise, 0) Return raise End Get Set(value As Boolean) SetSystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, value, SPIF_UPDATEINIFILE Or SPIF_SENDCHANGE) End Set End Property Public Shared Property Settings() As XSettings Get Dim values As New XSettings values.IsTracking = XMouse.IsTracking values.DelayInMilliseconds = XMouse.DelayInMilliseconds values.BringToFront = XMouse.BringToFront Return values End Get Set(value As XSettings) XMouse.IsTracking = value.IsTracking XMouse.DelayInMilliseconds = value.DelayInMilliseconds XMouse.BringToFront = value.BringToFront End Set End PropertyEnd Class
Conversely, we can read the setting using simply "Xmouse.IsTracking". This could be useful for instance in a ContextMenuStrip with a ToolStripMenuItem that has the CheckOnClick option set. We could set the checkmark of the menu item in the Opening() event of the ContextMenuStrip to make sure it reflects the current status:
Private Sub ContextMenuStrip1_Opening(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles ContextMenuStrip1.Opening XMouseActiveTrackingToolStripMenuItem.Checked = XMouse.IsTrackingEnd Sub
I also added in a class called "XSettings" that holds all three values together, which allows us to set or retrieve all three values at the same time. This could be useful if you want to save particular combinations of settings as a profile to facilitate fast switching between different configurations.
The SPIF_SENDCHANGE flag and WM_SETTINGCHANGE
I briefly mentioned the SPIF_SENDCHANGE flag when describing the parameters used when setting a value through SetSystemParametersInfo(). When this option is used, a WM_SETTINGCHANGE message is sent to all top level windows. In a .Net Winforms appliction, this message can be intercepted in the main forms WndProc() method. If your application displays windows settings, it may be necessary to update them automatically when another application changes them. If those other applications are well behaved, they will use SendMessageTimeout() with the HWND_BROADCAST flag to notify other applications that a change has been made. Here is how to trap the WM_SETTINGCHANGE message in WndProc():
Public Class Form1 Private Const WM_SETTINGCHANGE As Integer = &H1A Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) Select Case m.Msg Case WM_SETTINGCHANGE Me.RefreshSettings() End Select MyBase.WndProc(m) End Sub Private Sub RefreshSettings() Dim xSettings As XMouse.XSettings = XMouse.Settings Me.cbActiveTracking.Checked = xSettings.IsTracking Me.nudDelay.Value = xSettings.DelayInMilliseconds Me.cbBringToFront.Checked = xSettings.BringToFront End SubEnd Class
Hopefully I've given you enough information to fully understand how to use the SetSystemParametersInfo() API to configure XMouse settings, and possibly build your own utility to do just that. Happy XMousing!
Comments (0)