2

I have a windows application. I want to allow multiple instances for a single user session but I don't want multiple instances from different users. Put it simple, if A logs in to Windows, then he is able to run application as many instances as he likes but later, B logins as well, he should wait until all applications from A are closed.

Is this possible?

AK_
  • 7,981
  • 7
  • 46
  • 78
justyy
  • 5,831
  • 4
  • 40
  • 73
  • Is this a security requirement or a business requirement? Is there a server involved? Can yo manage Windows security policy? – AK_ Jan 30 '15 at 16:34
  • no server is involved. I want application checked purely on the client side – justyy Jan 30 '15 at 16:40
  • You didnt answer the first and last questions? Also what kind of app are you developing c#? Win32? – AK_ Jan 30 '15 at 16:42
  • it is a business requirement. I don't know about windows security policy. we can use C# or C++. – justyy Jan 30 '15 at 18:15
  • its pretty easy to do with a mutex. or at worst a mutex and memory mapped file – AK_ Jan 30 '15 at 18:33
  • @AK_: Out of curiosity, what's the purpose of a file mapping object in this scenario? – IInspectable Jan 31 '15 at 11:58
  • it depends on the actual requirement. sometimes you need the processes to actually pass some data to each other. In windows the memory mapped file API is also used for shared memory. https://msdn.microsoft.com/en-us/library/windows/desktop/aa366551(v=vs.85).aspx – AK_ Jan 31 '15 at 14:32

2 Answers2

1

This requirement can be accomplished using a named Mutex Object in the global Kernel Object Namespace. A mutex object is created using the CreateMutex function. Here is a small program to illustrate its usage:

int _tmain(int argc, _TCHAR* argv[]) {
    if ( ::CreateMutexW( NULL,
                         FALSE,
                         L"Global\\5BDC0675-2318-404A-96CA-DBDE9BC2F71D" ) != NULL ) {
        auto const err{ GetLastError() };
        std::wcout << L"Mutex acquired. GLE = " << err << std::endl;
        // Continue execution
    } else {
        auto const err{ GetLastError() };
        std::wcout << L"Mutex not acquired. GLE = " << err << std::endl;
        // Exit application
    }
    _getch();
    return 0;
}

The first application instance will create the mutex object, and GetLastError returns ERROR_SUCCESS (0). Subsequent instances will acquire a reference to the existing mutex object, and GetLastError returns ERROR_ALREADY_EXISTS (183). Instances started from another client session will not acquire a reference to the mutex object, and GetLastError returns ERROR_ACCESS_DENIED (5).

A few notes on the implementation:

  • A mutex object is created in the global kernel object namespace by prepending the "Global\" prefix.
  • The mutex object must have a unique name to have a means of referring to it from anywhere. The easiest way to create a unique name is to use the string representation of a GUID (GUIDs can be generated using the guidgen.exe tool part of Visual Studio). Don't use the GUID from the sample code, because someone else will, too.
  • There is no call to CloseHandle to release the mutex object. This is intentional. (See CreateMutex: The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.)
  • The mutex object is created using the default security descriptor. The ACLs in the default security descriptor come from the primary or impersonation token of the creator. If processes in different client sessions run with the same primary/impersonation token, the mutex can still be acquired from both sessions. In this situation the proposed solution potentially doesn't meet the requirements. It is unclear what should happen, if a user runs the application impersonating the user of a different client session.
  • The mutex object is used purely as a sentinel, and doesn't participate in synchronization.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
-1

I used previous code under win10/VS 2017 / 64 bits

It's wrong to test against if (::CreateMutex..

we must check error, so use:

BOOL checkUniqueInstance()
{
    if (::CreateMutexW(NULL,
        FALSE,
        L"Global\\27828F4B-5FC9-40C3-9E81-6C485020538F") != NULL) {
        auto err = GetLastError();
        if (err == 0) {
            return TRUE;
        }
    }
    CString msg;
    msg.Format(_T("err: %d -  Mutex NOT acquired"), GetLastError());
    OutputDebugString(msg);
    // Exit application
    return FALSE;
}
ingconti
  • 10,876
  • 3
  • 61
  • 48
  • This doesn't implement the requirements spelled out in the question. Notably, it will not allow multiple instances from the same user session. This code will only allow a **single** instance, across all user sessions. While a common requirement, it doesn't fit the question. – IInspectable Nov 09 '18 at 10:49
  • Besides, the error message is wrong in the case where `CreateMutex` returns a non-null value, and the error code is `ERROR_ALREADY_EXISTS`. In that case, the mutex *is* acquired, but the output reads *"Mutex NOT acquired"*. – IInspectable Nov 09 '18 at 10:55