Use PowerShell to Change the Mouse Pointer Scheme

Doctor Scripto

Summary: Guest blogger and Honorary Scripting Guy Brian Wilhite talks about using Windows PowerShell to change a user’s mouse scheme.

Microsoft Scripting Guy, Ed Wilson, is here. Today, we have a guest blogger—Brian Wilhite is an Honorary Scripting Guy and member of the Charlotte PowerShell Users Group. See previous blogs by Brian here.

Take it away, Brian …

Can Windows PowerShell change the mouse pointer scheme?

Have you ever encountered a problem where you almost gave up and thought, “Well … I guess it can’t be done.” I ran into that exact situation just the other day. One of my co-workers asked if I could automate, with Windows PowerShell, of course, changing a user’s mouse pointer scheme. To make a long story short, he wanted to have this script run for a specific user when launching a published application from Citrix. Without any thought I said, “Sure it can, Windows PowerShell can practically do anything.” I began to search the “interwebs” for ideas on how I was going to accomplish this task. To my shocking surprise, there wasn’t a lot of information available. I did what any system administrator should do, start the “how does this actually work” process. 

Based on the research up to this point, I know the mouse pointer scheme settings are stored in the registry, specifically the “HKCU\Control Panel\Cursors” key. There are several registry values for each item in the Cursors key relative to the mouse pointer scheme. The settings are referenced in the following screen shot.

I was able to script the registry updates by using the following code:

$RegConnect = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]”CurrentUser”,”$env:COMPUTERNAME”)

$RegCursors = $RegConnect.OpenSubKey(“Control Panel\Cursors”,$true)

$RegCursors.SetValue(“”,”Windows Black”)

$RegCursors.SetValue(“AppStarting”,”%SystemRoot%\cursors\wait_r.cur”)

$RegCursors.SetValue(“Arrow”,”%SystemRoot%\cursors\arrow_r.cur”)

$RegCursors.SetValue(“Crosshair”,”%SystemRoot%\cursors\cross_r.cur”)

$RegCursors.SetValue(“Hand”,””)

$RegCursors.SetValue(“Help”,”%SystemRoot%\cursors\help_r.cur”)

$RegCursors.SetValue(“IBeam”,”%SystemRoot%\cursors\beam_r.cur”)

$RegCursors.SetValue(“No”,”%SystemRoot%\cursors\no_r.cur”)

$RegCursors.SetValue(“NWPen”,”%SystemRoot%\cursors\pen_r.cur”)

$RegCursors.SetValue(“SizeAll”,”%SystemRoot%\cursors\move_r.cur”)

$RegCursors.SetValue(“SizeNESW”,”%SystemRoot%\cursors\size1_r.cur”)

$RegCursors.SetValue(“SizeNS”,”%SystemRoot%\cursors\size4_r.cur”)

$RegCursors.SetValue(“SizeNWSE”,”%SystemRoot%\cursors\size2_r.cur”)

$RegCursors.SetValue(“SizeWE”,”%SystemRoot%\cursors\size3_r.cur”)

$RegCursors.SetValue(“UpArrow”,”%SystemRoot%\cursors\up_r.cur”)

$RegCursors.SetValue(“Wait”,”%SystemRoot%\cursors\busy_r.cur”)

$RegCursors.Close()

$RegConnect.Close()

A problem with just updating registry values

The problem arises when you just update the values, doing so, as you may expect, will not magically change the mouse cursors. “Something” has to happen to reread the values from the registry and update the pointer scheme. So, what is that “something”? Well, to find out, I launched one of my favorite troubleshooting tools, Sysinternal’s Process Monitor (procmon). I started capturing events in procmon and performed the mouse pointer scheme change to evaluate what occurred on the system.

I knew the operation that performs changes to the registry would be RegSetValue, so I filtered to find the events where those operations occurred. Then, immediately afterward, unfiltered, I found where rundll32.exe started opening the registry key to query the settings that it just made. The line selected below is where we look for information on what occurred to cause the Mouse Pointers to update.

If you view the “Stack” for this particular operation, you’ll see the following calls for each module. I first reviewed the selected frame in the stack and it is a kernel mode win32k.sys call for xxxUpdateSystemCursorsFromRegistry. This looks interesting and probably is ultimately what updated my pointer scheme. However, I do know that I should read a stack from the bottom up, so there could be something lower in the stack that occurred to make the xxxUpdateSystemCursorsFromRegistry call.

Just above the main.cpl frame, which is the mouse and keyboard control panel applet, you’ll notice a SystemParametersInfoW call from the USER32.dll. Since that is directly after the main.cpl Mouse Pointer Dialog call, I focused my research efforts there.

Researching the API call

In the MSDN library, I found the SystemParametersInfo function topic. I searched the topic for “cursors” and noticed the following Desktop Parameter:

This information will be used in the code below to make the call. Notice the description in the screen shot above and how it dictates the parameter must be used.

So, all this talk about procmon, and I’ve yet to discuss much about Windows PowerShell. Just wait, the PowerShell “awesomeness”’ is just around the corner and it’s amazingly simple. 

At this point, I need to figure out a way to make the SystemParametersInfo call via Windows PowerShell. To a small degree, I know that PowerShell can leverage C# code in scripts. More research uncovered an article written by Lee Holmes that explains how to access Win32 API calls via Windows PowerShell. With what I learned from this article, I came up with the following:

First, we’ll grab the C# Signature from MSDN and store it as a here-string within my script:

$CSharpSig = @’

[DllImport(“user32.dll”, EntryPoint = “SystemParametersInfo”)]

public static extern bool SystemParametersInfo(

                 uint uiAction,

                 uint uiParam,

                 uint pvParam,

                 uint fWinIni);

‘@

Next, we’ll use Add-Type with the C# signature as the value for the MemberDefinition parameter, and give it a name and a namespace.

$CursorRefresh = Add-Type -MemberDefinition $CSharpSig -Name WinAPICall -Namespace SystemParamInfo –PassThru

Finally, we’ll call the method SystemParametersInfo with the arguments dictated by the MSDN reference page:

            $CursorRefresh::SystemParametersInfo(0x0057,0,$null,0)

If all is well, when this is executed, the mouse pointer scheme will be updated and Windows PowerShell will return True. Thanks for listening to me ramble about my PowerShell experiences …

~Brian

Thank you, Brian, for this great article.

Join me tomorrow when we begin a series of five articles written by Microsoft PFE Adam Haynes as he talks about using .NET Framework classes in Windows PowerShell scripts.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

0 comments

Discussion is closed.

Feedback usabilla icon