How to Avoid Multiple Instances of Different Users

2020-02-15 02:35发布

问题:

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?

回答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.


回答2:

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;
}