Synchronizing PowerShell scripts – allow only one instance at a time

Introduction

This is a first article from a short series about synchronizing PowerShell scripts in Windows environment. Today I’ll explain how to allow only one instance of a script at a time.

The idea is to ensure that your script won’t be launched in multiple instances simultaneously. The reason behind it might be to reduce usage of CPU, avoid locking the same resources, pro-actively help user to avoid multiple executions or anything else you can imagine.

This series will be based on thread and process synchronization mechanisms available in Windows. Usually, you can find them in desktop applications and services created in C, C++ or C#. However, it’s easy to implement them in PowerShell scripts, making them more robust.

Single instance on Notepad++ example

But what am a writing about? Let’s look at a well-known behaviour of some desktop applications. Notepad++ is one of them. When you try to launch it twice, it will keep only one instance. You can observe it with simple code:

& "C:\Program Files\Notepad++\notepad++.exe"
Get-Process notepad++
& "C:\Program Files\Notepad++\notepad++.exe"
Get-Process notepad++

When you run it, there will still be one instance of Notepad++. When checking processes, only the first one exists. It looks like the second launch is somehow ignored. In reality, it’s not. When you run those 4 lines together there is a chance to observe that there are 3 processes running at the same time for really short while. Then when you execute Get-Process again – the second and third ones disappear.

What’s happening? Notepad++ implements a check for existence of a Mutex. The Mutex is created by the first instance and kept until the original process is gone. All new processes check for its existence and in case they find it – they close itself and bring the first Notepad++ window to the top. That’s the reason why for a really short while you can observe new processes and then they disappear – the exist as long as it’s necessary to check for the mutex, bring the first window to top and close itself.

Mutex?

According to Microsoft (see the documentation here), Mutex is “a synchronization primitive that can also be used for interprocess synchronization”. In simple words, it’s just an object which allows very basic communication between threads. Any thread can create it. Any can access it as well, even in a separate process – that makes the synchronization “interprocess”. You don’t need to have administrator rights to create it. And it lives as long as the process which owns it.

How to observe it in “nature”?

You can force Notepad++ to launch in two instances simultaneously. Based on what I already wrote you probably know how – delete the Mutex checked by the application at start time. But how to do it? Mutex is a kernel object that you cannot dispose from another process. However, you can take the advantage of Process Explorer from SysInternals. It creates a kernel driver with access to close handles for other processes. In result of closing the Notepad++’s mutex handle, the kernel cleans up an orphaned mutex. Remember to run Process Explorer as administrator to have enough access rights.

Once started, find the Notepad++ process and look carefully at all its handles. Among them you should see something that look like the one we want to identify. There is a Mutant (it’s kernel’s name of a Mutex) with nppInstance in name. Sounds like a good shot? Let’s try to close the handle with right click.

Confirm that you know what you are doing (closing handle to this Mutex will make no harm).

And now you can launch the second instance of Notepad++, success!

Use it in PowerShell

This post is about Synchronizing PowerShell scripts – allow only one instance at a time. It’s a good time to focus on the PowerShell part of it.

Mutex is a kernel object that you can access and maintain through Windows API. The good thing is that .NET provides us a wrapper class called System.Threading.Mutex. You can use it and make your code clean and easy.

To create a new Mutex, use the New method with two parameters, first is just $true to set the ownership and second is mutex name – it can be any string you like. You can access the mutex from a different process based on its name.

$mutex = [System.Threading.Mutex]::new($true,"NewMutex")

After executing the above line, you can observe your new Mutex in Process Explorer.

To check if a Mutex exists, use the method TryOpenExisting. Providing the name of the Mutex and a null reference (because you don’t need to use it, just check if it exists). This method returns $true or $false depending on the existance of the object we are looking for. You can call it from a any process when you know the name (within the same process but from different thread you don’t even need to know the name).

[System.Threading.Mutex]::TryOpenExisting("NewMutex",[ref]$null)

In the end, when you don’t need it no more, it’s always good to clean-up and remove what you created. It will be disposed anyway when the process quits but maybe you would like to clean-up before the end of the script. To do it, call the Dispose method on the handle object.

$mutex.Dispose()

The script

Joining all the pieces together gives you something like:

# check for mutex
if ([System.Threading.Mutex]::TryOpenExisting("NewMutex",[ref]$null))
{
    # mutex is found! inform user and close the script
    Read-Host "Another instance of this script is already running, press Enter to exit"
    return
}
else {
    # mutex not found! Create it and continue
    $mutex = [System.Threading.Mutex]::new($true,"NewMutex")
}

# do your things...
Write-Output "Script started!"
Start-Sleep -Seconds 120

# clean-up the mutex in the end
$mutex.Dispose()

Now, when you ran the script one after another, only the first one will execute to the end, the other one will just exit itself at the begging.

Do not exit, wait

You can also set the script to wait for the first instance to finish and then execute.  It requires to use the Mutex in a slightly different way. Instead of just checking for its existence and remove it at the end, release the object and change its owner. In simple words, the process (or thread) that wants to execute, checks if the Mutex is free. If not, it waits for it. If yes, it takes the ownership and locks the Mutex.

You still need to check for the existence and create a new Mutex if it doesn’t exist. The new flow is implemented within the if statement for found Mutex. Use the WaitOne method which accepts a timeout parameter (in milliseconds). It returns $true when succeeds (Mutex was released in the other process, so it’s taken over) and false if it fails (timeout reached before the Mutex is released).

The other change is to Release the Mutex at the end (instead of Dispose) so another process can take it over.

# we need the variable to be initialized, otherwise reference to it in next line cannot be passed
$mutex = $null

# check if mutex already exist and assing a handle to variable
if ([System.Threading.Mutex]::TryOpenExisting("NewMutex",[ref]$mutex))
{
    # mutex is found! wait with further execution
    (Get-Date).ToString("HH:mm:ss:ffff") + " - An instance of this script is already running, waiting until it's completed..."
    # timeout is set in miliseconds
    if (!$mutex.WaitOne(15000)){
        Write-Output "Error: timeout..."
        return
    }
}
else {
    # mutex not found! Create it and continue
    (Get-Date).ToString("HH:mm:ss:ffff") + " - This is a 1st instance of this script"
    $mutex = [System.Threading.Mutex]::New($true, "NewMutex")
}

# do your things...
(Get-Date).ToString("HH:mm:ss:ffff") + " - Script started!"
Start-Sleep -Seconds 10
(Get-Date).ToString("HH:mm:ss:ffff") + " - Script completed!"

# release the mutex so another instance can run
$mutex.ReleaseMutex()

I added timestamps so you can easily observe what’s going on. The second instance of the script waits for the first one. Then, right after its completion, it starts.

End

Thank you for reading this article about Synchronizing PowerShell scripts – allow only one instance at a time. You can use Mutexes to synchronize threads or processes, so scripts as well. There are more possibilities and other kernel objects allowing more advanced synchronization between scripts. I will describe how to use them in next articles.

Go to Part 2 – Synchronizing PowerShell scripts – control the flow.

Wiktor Mrówczyński

1 thought on “Synchronizing PowerShell scripts – allow only one instance at a time”

  1. Pingback: Synchronizing PowerShell scripts – control the flow - IT Constructors

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top