Metric Panda Games

One pixel at a time.

Single Instance Games On Windows With C/C++

Rival Fortress Update #46

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 Steam or 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:

#ifdef _MSC_VER
#pragma comment(linker, "/SECTION:.oneinst,RWS")
#pragma data_seg(".oneinst")
int IntancesRunning = 0;
#pragma data_seg()

#else

//NOTE: GCC
int InstancesRunning __attribute__((section(".oneinst"), shared)) = 0;
#endif

int WinMain(...)
{
    if (InstancesRunning++ == 0)
    {
      // NOTE: Normal code path
    }
    else
    {
      // NOTE: Handle second instance running
    }
    return 0;
}

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 link.exe using: -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:

int WinMain(...)
{
    CreateMutexA(0, 1, "Global\\MyGameMutex");
    if (GetLastError() != ERROR_ALREADY_EXISTS)
    {
      // NOTE: Normal code path
    }
    else
    {
      // NOTE: Handle second instance running
    }
    return 0;
}

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.