Avoiding running multiple instances of an application

Published on
24,106 Points
11 Endorsements
Last Modified:
Community Pick
Jim Dettman (Microsoft MVP/ EE MVE)
Independent consultant specializing in the writing of custom packages for businesses.
This article is part of the app development series, a series of articles on Experts-Exchange.com that explores common application development problems and their solutions.

This article presents code written in VBA executing on Windows platforms, but the techniques demonstrated here could be used with any language or application.

Level: Intermediate


One common problem in development is how to prevent users from running multiple copies of an application.  In some cases users need to be allowed to do this, but in others they need to be restricted from doing so.  Sometimes it's simply a performance issue that dictates this.   It might also be a development one (the app will not function correctly if multiple instances are run on the same station).   It may be only to help a user out.  Users will often start an application again and again only because they cannot locate an application that is already running (an application window may be hidden behind another or be off screen).

To be able to prevent multiple instances from running, we'll need to use a semaphore (a flag) to signal when our app is running.  This flag needs to be visible to every process running on a station.  Often, developers will try and create such a flag by saving a file on disk, setting an entry in the registry, or saving data in a database or file.  This flag gets set when a user starts the application and removed when the user exits.  Sounds like that would work right?

Of course in the real world all types of things can happen; impatient users issuing a Ctrl/Alt/Del, a station locking up, power loss with no UPS, etc., leaving the app to terminate abnormally and as a result; your flag still set.  Now when the user returns, their told the application is already running.  Ouch.  A reboot won't fix it either.  The only way to get this to work then is to give the user some type of utility to clear the flag and instructions on how to use it.  Problem is, this is something that might not happen very often and undoubtedly you're going to get a phone call when it does because they have forgotten all about that utility and how to use it.


The way around that problem is through the use of a Mutex (Mutually Exclusive) object.  The job of a Mutex in an OS is to prevent multiple processes from accessing the same resource at the same time.  Every OS has something that serves in this function.   By exploiting that, we can handle all of the problems above.

You see, the beauty of a Mutex is that because it is held in memory through the OS, a reboot of the machine gives you a clean slate.  And as most developers know, if a user knows to do anything, it's to try a reboot first.   Even better, a Mutex is owned by a given thread and if the thread dies then typically so does the Mutex. This means that often a reboot is not even required; they just need to restart the app.

Here's a link to a MSDN (Microsoft Developers Network) article that goes into a little more detail on a Mutex:


As the article explains, one uses the CreateMutex() API call in Windows to create a Mutex.  So with that as a starting point, let's take a look at some code!

First, since we are calling Window API functions, we need a declaration (a Declare statment) to access that function in Windows:

Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lngMutexAttributes As Long, lngInitialOwner As Long, ByVal lpName As String) As Long

Open in new window

You really don't need to know a lot about this, just that it allows you to call the CreateMutex() and pass it some arguments.  Just paste it into a general module and your set.  Now we can write some VBA code like this:

Dim lngMutexHandle as long

' Create a muxtex object
lngMutexHandle = CreateMutex(0, 1,"myApp")

Open in new window

The first argument is used to apply security attributes to the Mutex, which we don't need for this, so we just set it to 0.  The second argument is a true / false flag for indicating if the thread issuing the call should be made owner of the object.  Since this is what we want, it is set to true.  This gives us ownership and no other process can take ownership of the mutex.  The finial argument is the name for the Mutex object.  This can be any string and should be application specific.  I typically use my application name; that is the name that I want to appear on the menu's, top of reports, displayed in dialog titles, etc. i.e. "Incentive System".  Again, this can be any text string.

After we issue this call, one of two things will happen:

1.  The Mutex will be created because this is the first instance of "myApp"
2.  We will get back a handle to an already existing Mutex called "myApp".  This means that another copy of the app is already running.

But how exactly do we know that?  The second will return an error, which in VBA can be checked through the err.LastDLLError property:

' Did we get a new instance or a handle to an existing one?
   ' App is already running
   ' Message user and quit.
   MsgBox "This application is already running on this station and multiple instances are not allowed", vbOKOnly+vbCritical
   ' Call your exit routine here to quit the app.
   ' App is not running - OK to continue
End If

Open in new window

So if we do get back an error code that the Mutex already exists, then another instance of "myApp" is running, otherwise its not.  Note that ERROR_ALREADY_EXISTS is a constant, which is defined like this:

' Used for semephore check.

Open in new window

We could have used the value directly in the If check like this:

If Err.LastDllError = 183& Then

Open in new window

But using the constant makes the code easier to read and less confusing down the road when you go back to it.  Like a declaration (Declare) statement, this gets put in the declarations section of a general module in VBA.

You now have the guts of it; trying to create a Mutex, checking if that happened, and then doing something based on that.  You could stop here, but just displaying a message to the user might leave them confused, especially if the first app instance is hidden from view.   What would be really nice is if we could switch them to that other instance without them having to do anything.


How can we accomplish that?  Well you probably guessed it, we just need a few more API calls!   Now don't groan, it hasn't been that tough so far has it?   Just keep reading and we'll be done before you know it!  

The Windows API routines contain a function called GetWindow(), which lets us enumerate (loop through) all the windows that are currently running.   But how do we know which window is ours?  After all, there may be many applications running.  You could look at the .EXE name of the program itself that is executing in a thread, but what if someone runs something with the same .EXE (ie. Excel) that is un-related to your app?  We could look at the window title, but often developers like to change the window titles on the fly for various reasons.  Might there be a better way?  Yes there is.  As it turns out, Windows let's you add properties to a window.  All you need to do is give it a name and a value for the property.  With this, we can "mark" our window so we can find it again.  

To do that, add the following:

Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hdata As Long) As Long

Open in new window

And now our code will look like this:

 Public Function AppAlreadyUp(bAllowMultipleInstances As Boolean) As Long

      Dim lngMutexHandle as long
      Dim lngReturn as long

      ' Create a muxtex object
      lngMutexHandle = CreateMutex(0, 1, myApp)

      ' Did we get a new instance or a handle to an existing one?
      If Err.LastDllError = ERROR_ALREADY_EXISTS Then
        ' App is already running
        If bAllowMultipleInstances = False Then
          ' Message user and quit.
          MsgBox "This application is already running on this station and multiple instances are not allowed", vbOKOnly+vbCritical
          ' Call your exit routine here to quit the app.
           MsgBox "Warning, this application is already running on this station."
        ' App is not running - OK to continue
        ' Mark the window with a property so we can find it again if we need to.
        lngReturn = SetProp(Application.hWndAccessApp, "myApp", 1)
      End If

End Function

Open in new window

What SetProp() is doing here is adding the property "myApp" to the current threads window and assigning it a value of 1.  For what we are trying to do, the value is not important, just the property name itself.  Now this window is marked and we can easily locate it again outside of anything else.    Also note that I've added an argument, bAllowMultipleinstances for a little more flexibility in the procedure.  If this is set to True, a user just gets a warning when there is a copy of the application already running and they are allowed to continue.  If False they get a different message and cannot.


Now the last bit to take care of switching to the first copy if multiple instances are not allowed:

Add these declarations:

Private Declare Function BringWindowToTop Lib "user32" (ByVal lngHWnd As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal lnghObject As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal lngHWnd As Long, ByVal lpString As String) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal nRelationship As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdSHow As Long) As Long

Open in new window

And these constants:

' For GetWindow
Const GW_HWNDChild = 5
' For ShowWindow

Open in new window

And finally, the complete function:

Public Function AppAlreadyUp(bAllowMultipleInstances As Boolean, bDisplayMsg As Boolean) As Long
      ' Function checks for multiple instances of an application
      ' by creating a mutex object.  If no error, then this is only
      ' instance running.
      Const RoutineName = "AppAlreadyUp"
      Const Version = "1.0"
      Dim lngMutexHandle As Long
      Dim lngHWnd As Long
      Dim lngReturn As Long
      Dim strMsg As String
      On Error GoTo AppAlreadyUp_Error
      ' Create a muxtex object
      lngMutexHandle = CreateMutex(0, 1, "myApp")
      ' Did we get a new instance or a handle to an existing one?
      If Err.LastDllError = ERROR_ALREADY_EXISTS Then
        ' App is already running
        If bDisplayMsg = True Then
          ' Close the handle just created as
          ' it only points to the existing muxtex
          lngReturn = CloseHandle(lngMutexHandle)
          If bAllowMultipleInstances = False Then
            strMsg = "This application is already running on this workstation.  You cannot start another copy."
            strMsg = strMsg & vbCrLf & "You will be switched to the existing copy."
            strMsg = "Warning: This application is already running on this workstation."
          End If
          MsgBox strMsg, vbOKOnly + vbCritical
        End If
        If bAllowMultipleInstances = False Then
          ' Find the existing instance, switch to it, then close this instance
          lngHWnd = GetWindow(GetDesktopWindow(), GW_HWNDChild)
          Do While lngHWnd > 0
            If GetProp(lngHWnd, "myApp") = 1 Then
              BringWindowToTop (lngHWnd)
              lngReturn = ShowWindow(lngHWnd, SW_MAXIMIZE)
              Exit Do
            End If
            lngHWnd = GetWindow(lngHWnd, GW_HWNDNEXT)
          ' Call application exit routine here or just quit your app.
        End If
        lngReturn = SetProp(Application.hWndAccessApp, "myApp", 1)
      End If
      On Error Resume Next
      ' Always return false just to pass something back
      AppAlreadyUp = False
      Exit Function
      UnexpectedError ModuleName, RoutineName, Version, Err.Number, Err.Description, Err.Source, VBA.Erl
      Resume AppAlreadyUp_Exit
End Function

Open in new window

A few notes on the final code:
I added the argument bDisplayMsg, which controls if the user is messaged or not.  That combined with bAllowMultipleInstances will handle all the situations I outlined at the start of the artice.
Note the code in lines 37 through 48.  This is the code to loop through all the windows, find our marked window, and switch to it.
On line 44, the found window is maximized.  You can use the SW_SHOWNORMAL or SW_ SHOWMINIMIZED  constants if you wish instead of SW_SHOWMAXIMIZED.
The code on line 26 is a cleanup task.  When you attempt to create a mutex object, if it is not created, a Windows handle is returned to you along with the error.  This handle points to the already existing Mutex and because we are quitting, it will not be needed.  So we close the handle.
The UnexpectedError call on line 66 is a call to an error handling routine that I use.  Make sure you change this to either a Msgbox call or your own error handling routine.
Constants RoutineName and Version are used by my error handling routine to identify where problems occur because VBA does not expose through any property what procedure is currently executing.
I wrote this as a function, but there is no reason it could not be written as a sub.

  I have been using this code for a number of years and have not found any issues with it running on any Windows platform.   I hope you find it useful in your own applications.
Enjoy this complimentary article view.

Get unlimited access to our entire library of technical procedures, guides, and tutorials written by certified industry professionals.

Get 7 days free
Click here to view the full article

Using this article for work? Experts Exchange can benefit your whole team.

Learn More
Experts Exchange is a tech solutions provider where users receive personalized tech help from vetted certified professionals. These industry professionals also write and publish relevant articles on our site.
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Learn from the best.