This week, among other Windows related things, I added a constrain to allow only a single instance of Rival Fortress to be run. This comes for free for games on
GOG Galaxy, but it is a consideration when you are also distributing your game through other means.
The naive approach
A naive approach is to use some sort of lock file that gets written by the first instance to a known location and checked for existence by subsequent instances. The problem is that if your application fails to do its cleanup, you are left in a broken state where the user cannot open your app, unless they manually delete your super secret lock file.
I’ve seen this done quite a few times in other code bases and the amount of code required to handle all possible failure cases is cringe-worthy.
The lightweight approach
A interesting approach is based on leveraging the shared sections of Portable Executables.
Shared sections are neat little feature that allow multiple instances of the same executable or DLL to have a shared address space.
This can be exploited for the following super-lightweight solution to single instance applications:
This snippet creates the variable
InstancesRunning in the section
.oneinst (that I made up) and marks it as shared using the
#pragma statement on MSVC (
RWS: Readable Writable Shared) and the
__attribute__ on GCC. (You can also specify the command directly to
-section:.oneinst,RWS, just as the #pragma statement does).
When the last instance of an application exists, the shared memory is unmapped and the
InstanceRunning variable is “reset”.
A downside to this approach is that it only works for exact instances of the executable, so if the user makes a copy of the executable, windows will not map the section in the same address space as the first one.
You can read more about this approach in the MSDN article How do I share data in my DLL with an application or with other DLLs?. It’s intended for DLLs, but works just as fine with normal executables.
Also, if you are writing an application with security concerns read Raymond Chen’s Why .shared sections are a security hole before using this approach to shuttle data back and forth between instances.
The best approach
The ideal approach is to use a named
mutex using a call to CreateMutex during startup. This works well and, to my knowledge, has no downsides.
This is how you would implement one:
A global named mutex is created by the first instance of your application, and cleaned up automatically by Windows when your application shuts down. If a second instance of the application starts, the call to
GetLastError() will return
ERROR_ALREADY_EXISTS, so you can check that.