Using the MouseEnter and MouseLeave Events in PowerShell Studio

Default Winforms on their own can sometimes look a bit bland, but there are some things we we can do, which do not require much coding, that can really enrich the GUI experience.

The MouseEnter and MouseLeave events can be easily overlooked when writing a forms application. A MouseEnter event is raised when the mouse pointer moves over an area, or to be more correct, a specific controls region. The MouseLeave event is the opposite. It is raised when the pointer is moved outside of the region of a control.

You can take advantage of these events in many ways. In this post, we’ll use them to highlight the control over which the pointer is, and display descriptive text associated with that control. The style is meant to be not unsimilar to some of the Microsoft installation dialogs. 🙂

NB If you wish, you can skip the steps below by downloading the source files from my GitHub repository.

Create the Forms Project

First of all, create a new forms project. Let’s call it MouseEnter. Now add the following controls, setting their properties as mentioned below.

Control Type Name Text BackColor Font
Form frmMouseEnterDemo MouseEnter demo White default
Panel panel1 na 1,36,86 default
Label labelDescription Description na Size 10, Bold
Label LabelOptions Options na Size 10, Bold
Label LabelOverview Overview na default
Label LabelMouseEnterApplication MouseEnter Application na default

Position labelDescription so that it occupies the left half of the form, underneath the blue panel.

Position these last three labels on the right hand side of the form, with the Options label at the top, followed by the other two directly under it.

Your form layout should now look something like this :

MouseOver - Initial Form Setup

With the labelDescription control positioned as we want it, clear its the text property, so that it is blank.

Now we need to add code for five events :

  • form loading
  • mouse enters overview label region
  • mouse leaves overview label region
  • mouse enters mouse enter application region
  • mouse leaves mouse leave application region

Writing the Event Code

We’ll register event handlers for the above actions by the process below, and then paste in the event code that is further down this page.

  • Select the properties panel, and the events (lightening) button
  • Double click on the form
  • Go back to the form view, select the overview label, double click on MouseEnter.
  • Do this also for the MouseLeave event
  • Repeat the above for the mouseenterapplication label.

Once you have completed, this your form should show a set of entries for the events we have selected. These will be empty. Select the text, and clear it.

Select the text below, copy it into the clipboard, and then paste it into the code window.

	$labelOverview.ForeColor = 'Blue'
	$labelDescription.Text = $overviewText

	$labelOverview.ForeColor = 'Black'
	$labelDescription.Text = $defaultDescriptionText

	$labelMouseEnterApplication.ForeColor = 'Blue'
	$labelDescription.Text = $applicationText

	$labelMouseEnterApplication.ForeColor = 'Black'
	$labelDescription.Text = $defaultDescriptionText

	$script:defaultDescriptionText = 'Welcome to the MouseEnter example! This example demonstrates how we can use this.'
	$script:overviewText = 'When the mouse pointer enters an area on which a control resides, a MouseEnter is raised. After the mouse departs from that specific controls area, a MouseLeave event occurs'
	$script:applicationText = 'This application provides an example of how we can use the MouseEnter and MouseLeave events to enrich the GUI experience on a form'
	$labelDescription.Text = $script:defaultDescriptionText

This sets the text that  to be shown when a control’s region is entered or left by the mouse pointer, and also other actions to be performed in these circumstances. When the pointer enters a control’s region, we set its text is set blue, and the label on the left hand of the screen displays some descriptive text. On startup, or when a pointer leaves a controls region, this description is reset back to the default welcome text.

mouseover - part 2 - form code

Rearrange your form as you wish.


Now let’s run our project to see the results.

Overview - MouseEnter

Overview Mouse Enter

Overview - MouseLeave/Not in any region

Overview MouseLeave

When the pointer is positioned over one of the options, the font changes to blue, and the descriptive text on the left hand side also changes. When the pointer moves outside the controls region, the default text is displayed, and the font set back to black.

And a video which highlights what’s happening a bit better. Apologies for the poor quality, I’m still wrestling with uploads to YouTube at the moment :

As always, comments and feedback welcome. Thanks for reading.




Using Local Functions on Remote Computers

30/12/2015 – UPDATE : Scriptblock code changed to better llustrate use of multiple local functions used in combination with other code on the remote system, after feedback from Aleksandar Nikolić &  Tommy Maynard. Thanks guys. 🙂

I came across a situation recently where I needed my remote computers to use some functions that were part of a project I was working on. I wanted to use them in various parts of a scriptblock passed from Invoke-Command. Options with Invoke-Command did not allow for this though. You have the mutial exclusive -file and -scriptblock  parameters.

I prefer to keep my functions as separate files as opposed to their inclusion in a module.Loaded functions are stored within the FUNCTION PowerShell provider, and because of this, cmdlets such as Get-ChildItem can be used with them.

I noticed the Name property of items in FUNCTION: always have the name of the function, and the Definition property the actual code.  To convert this to the exact text of the function as we normally see it, should just require some string concatentation  All we were missing was the word Function, and surrounding braces to create a string equivalent of the function. We’d also add new line characters as well for formatting purposes.

This could be done using the format:

Function {`r`n item.definition `r`n}`r`n

This is the function developed below:

function Get-FunctionString
[Parameter(Mandatory = $true)][System.Array] $Function

[string]$strFunctions = $null
$items = Get-ChildItem Function:\ | Where Name -in $Function

ForEach ($item in $items)
$strFunctions += "Function $($item.Name) { `r`n $($item.Definition) `r`n }`r`n"



It takes as a parameter an array, which is filtered against items provided by the Function PowerShell provider.  Then, the function text itself is comprised and concatenated to a string variable, $strFunctions.

At the beginning of our script, we define a string array containing the names of the functions that we wish to used for use remotely. In the below case, I had functions called Get-ServerList and Confirm-ValidSource.

$functions = @('Get-ServerList','Confirm-ValidSource')

Then, the function we created earlier is invoked to return a string of the text from these functions.

$FunctionString = Get-FunctionString -Function $functions

With this variable defined, I can create a new scriptblock of the string of functions above within the remote session, and then dot source it to make all these functions available.


Invoke-Command -ComputerName $item -ScriptBlock {
    $scriptblock = [System.Management.Automation.ScriptBlock]::Create($using:FunctionString)
    . $scriptblock
    $x = Get-ServerList
    If ($x) 
        ForEach ($obj in $x) 
            $y = Confirm-ValidSource($obj)
            If ($y) 
                Write-Output -InputObject "The server $obj has a valid source"
                Write-Output -InputObject "The server $obj does not have a valid source"

For those of using PowerShell Studio, ensure the array of strings variable ($functions in this case) is defined in globals.ps1, and that for each function part of your project, the property CommandExtension is set to True.

Thanks for reading, and feel free to provide feedback.




Regain sysadmin rights to a SQL instance without stopping its service

I came across a situation a few weeks ago where there was a set of SQL servers, some with single and others with multiple instances, which were missing the required domain groups for management.

Worse, I had no permissions to the server, and without these, I was unable to login to the SQL instance. Another issue was that under no circumstances could any of the instances be stopped.

After a bit of head bashing I remembered that by default installation, the local system admin account is a system administrator in SQL Server, and it’s not one I remove after installation.

If I could write a script to add the required groups in, and run a script in a local system context then maybe i could restore the required permissions.

I came up with the script below. The only caveat is that Local System must be resident within the instance level users, and have sysadmin rights.

It’s a bit of a hack, but it served its purpose. 🙂

$task = @'
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="">
    <Principal id="Author">
  <Actions Context="Author">
      <Arguments>-file d:\addSysadmin.ps1</Arguments>

$code = @'
function Invoke-SQLQuery

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Query')] [string[]] $Query,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database')] [string] $Database = 'master',
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Instance')] [string] $SQLServer
            $ErrorAction = 'Stop' 
            $SqlConnection = New-Object  -TypeName System.Data.SqlClient.SqlConnection
            $SqlConnection.ConnectionString = "Server=$($SQLServer);Database=`'$($Database)`';Integrated Security=SSPI;"
            ForEach ($qry in $Query) {
            $SqlCmd = New-Object  -TypeName System.Data.SqlClient.SqlCommand
            $SqlCmd.CommandText = $qry
            $sqlcmd.Connection = $SqlConnection
            $SqlAdapter = New-Object  -TypeName System.Data.SqlClient.SqlDataAdapter
            $SqlAdapter.SelectCommand = $SqlCmd
            $DataSet = New-Object  -TypeName System.Data.DataSet
            Try {
            $nSet = $SqlAdapter.Fill($DataSet)
            Catch {
            $Tables = $DataSet.Tables

$query = @"
EXEC master..sp_addsrvrolemember @loginame = N'mydomain\mygroup', @rolename = N'sysadmin'

$instances = Get-Service | Where {($_.Name -like 'mssql$*')} | Select -ExpandProperty Name | ForEach {$_ -replace 'MSSQL\$',"$env:computername`\"}
ForEach ($instance in $instances) {
Invoke-SQLQuery -Query $query -SQLServer $instance

$instances = Get-Service | Where  {($_.Name -eq 'MSSQLSERVER')} | % {"$env:computername"}
ForEach ($instance in $instances) {
Invoke-SQLQuery -Query $query -SQLServer $instance
$ErrorActionPreference = 'Stop'
    Remove-Item -Path d:\addSysadmin.ps1 -Force
    Remove-Item -Path d:\task.xml -Force


$code | Out-File -FilePath d:\addSysadmin.ps1 -Force
$task | Out-File -FilePath d:\task.xml -Force
schtasks.exe /Create /XML d:\task.xml /TN addperms
schtasks.exe /Run /TN 'addperms'

$taskStatus = schtasks.exe /query /tn addperms
While ($taskStatus[4] -like '*runn*')
    Start-Sleep -Milliseconds 100
    $taskStatus = schtasks.exe /query /tn addperms
schtasks.exe /delete /tn addperms /f
Remove-Item -Path d:\addSysadmin.ps1 -Force
Remove-Item -Path d:\task.xml -Force

In the script, two text blocks are created.

The first one, assigned to the $task variable, is the XML for the scheduled task we are going to create.

The second one, assigned to $code, is the actual powershell code that the scheduled task would run. This code contains a function for executing the required sql commands, and additionally there is code which iterates through each instance on the server and runs the function with the required SQL commands to add the required permissions to each instance. The script writes these text blocks out to new files (in my case on the D drive).

Then, schtasks.exe is used to create the scheduled task, using the new XML file we’ve just created as the input source. Amongst other settings, the XML specifies that the tasks runs in local system context, and also the command and arguments to be executed. The arguments point to the second file just created, the .ps1 file. schtasks.exe is then used again to trigger the job and we wait until the status of the job changes from running to anything else. Once the task has completed, or failed, it is removed from Task Scheduler. Finally, the script removes the files it has just created so no footprint is left on the system at all. Sounds a bit ‘naughtyish’, but it’s really just to keep things tidy!

The code can be run remotely by specifying it as a scriptblock in an Invoke-Command against a remote system, if so desired.

NB You should also be able to use this for also adding single domain accounts, as the formatting of the sql command will be exactly the same. Just replace or add the required details.

You can find the source code for this at my repo