<

Configuring XMouse Behavior in Windows via VB.Net and the SystemParametersInfo() API

Published on
14,672 Points
4,972 Views
2 Endorsements
Last Modified:
Awarded
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!
2
Comment
0 Comments

Featured Post

Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

Join & Write a Comment

Did you know PowerShell can save you time with SaaS platforms? Simply leverage RESTfulAPIs to build your own PowerShell modules. These will kill repetitive tickets and tabs, using the command Invoke-RestMethod. Tune into this webinar to learn how…
Discover the basics of using Outlook 2016 from office 365.

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month