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
Published:
Updated:
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 SystemParametersInfo() API

The SystemParametersInfo() API can be used to retrieve and set quite a number of system-wide windows settings in the following categories: Accessibility, Desktop, Icon, Input, Menu, Power, Screen Saver, Time-out, UI Effects, and Window parameters.  Full documentation can be found on MSDN here:
The SystemParametersInfo() API: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx

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

Open in new window


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

Open in new window


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 Boolean
                      GetSystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING, 0, tracking, 0)
                      MessageBox.Show("tracking = " & tracking)

Open in new window


Changing the current state of XMouse behavior looks pretty similar:
Dim newState As Boolean = True ' True = On, False = Off
                      SetSystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING, 0, newState, 0)

Open in new window


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:
SetSystemParametersInfo(SPI_SETACTIVEWNDTRKTIMEOUT, 0, 350, 0)

Open in new window


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 Property
                      
                      End Class

Open in new window


Now we can turn XMouse on or off using syntax like this:
XMouse.IsTracking = True ' Turn it on
                      XMouse.IsTracking = False ' Turn it off

Open in new window


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.IsTracking
                      End Sub

Open in new window


Changing the delay now becomes:
XMouse.DelayInMilliseconds = 350

Open in new window


And lastly, we can set the "bring to top" option using:
XMouse.BringToFront = True ' Turn it on
                      XMouse.BringToFront = False ' Turn it off

Open in new window


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 Sub
                      
                      End Class

Open in new window


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!
3
5,848 Views
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.