-1

I have the following code. It prevents multiple instances of same application under the same user. However, I also want to prevent a different user on the same machine to run the application when another user is already running it. The code successfully creates a mutex even though the application is already running by a different user. Not able to figure out what i'm doing wrong here. Any help is appreciated.

m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
if (::GetLastError() == ERROR_ALREADY_EXISTS)
{
    if (m_hMutex)
        ::CloseHandle(m_hMutex);

    //in case the previous instance of APP is still in the process of closing
    ::Sleep(5*1000);
    m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
    if (::GetLastError() == ERROR_ALREADY_EXISTS)
    {
        EndSplashWindow();
        AfxMessageBox( _T("An instance of APPis already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
        return FALSE;
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vinny
  • 35
  • 6

1 Answers1

2

You did not show the actual value of PREDEFINED_UNIQUE_ID, but I'm guessing that it is not creating the mutex in the Global kernel namespace, but in per-session namespaces instead. Using the Global namespace is required for spanning user sessions:

The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the "Global\" prefix to the object name. For example, the following code calls CreateEvent and creates an event object named CSAPP in the global namespace:

CreateEvent( NULL, FALSE, FALSE, "Global\\CSAPP" );

...

Another use of the global namespace is for applications that use named objects to detect that there is already an instance of the application running in the system across all sessions. This named object must be created or opened in the global namespace instead of the per-session namespace. The more common case of running the application once per session is supported by default because the named object is created in a per session namespace.

UPDATE: you also need to handle the case where CreateMutex() can't access a mutex in another session due to security permissions. You need to handle the case where GetLastError() returns ERROR_ACCESS_DENIED, which in of itself means the mutex exists.

Try something more like this:

#define PREDEFINED_UNIQUE_ID _T("Global\\MyUniqueMutexName");

m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
// or: m_hMutex = ::CreateMutexEx(&sa, PREDEFINED_UNIQUE_ID, 0, SYNCHRONIZE);
DWORD dwErrorCode = ::GetLastError();

if ((m_hMutex) && (dwErrorCode == ERROR_ALREADY_EXISTS))
{
    ::CloseHandle(m_hMutex);

    //in case the previous instance of APP is still in the process of closing
    ::Sleep(5*1000);
    // consider using WaitForSingleObject() instead, in case the app takes less than 5 seconds to close...

    m_hMutex = ::CreateMutex(NULL, FALSE, PREDEFINED_UNIQUE_ID);
    // or: m_hMutex = ::CreateMutexEx(&sa, PREDEFINED_UNIQUE_ID, 0, SYNCHRONIZE);
    dwErrorCode = ::GetLastError();
}

if (dwErrorCode != 0)
{
    EndSplashWindow();

    switch (dwErrorCode)
    {
        case ERROR_ALREADY_EXISTS:
        case ERROR_ACCESS_DENIED:
            AfxMessageBox( _T("An instance of APP is already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
            break;

        default:
            AfxMessageBox( _T("Error checking if an instance of APP is already running."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
            break;
    }

    return FALSE;
}

// OK to run now!
return TRUE;

Or, to minimize the chances of ERROR_ACCESS_DENIED being reported, you can provide a NULL DACL to CreateMutex():

PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); 
if (!pSD) 
{ 
    EndSplashWindow();
    AfxMessageBox( _T("Error allocating security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;
}

if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) 
{ 
    LocalFree(pSD); 
    EndSplashWindow();
    AfxMessageBox( _T("Error initializing security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;
}

if (!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))
{ 
    LocalFree(pSD); 
    EndSplashWindow();
    AfxMessageBox( _T("Error setting access for security descriptor."), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
    return FALSE;
}

SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;

m_hMutex = ::CreateMutex(&sa, FALSE, PREDEFINED_UNIQUE_ID);
...

LocalFree(pSD);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • i Changed the value of **PREDEFINED_UNIQUE_ID to "Global\\CSAPP"** and i'm still having the same issue?! – Vinny Apr 03 '19 at 22:11
  • The point was to simply add the `Global\ ` prefix to whatever name you were already using, you didn't have to change the name to `CSAPP`, that is simply the name used in MSDN's example. But either way, you can't be having the same issue once the mutex is in the global namespace. – Remy Lebeau Apr 03 '19 at 22:16
  • You need to provide `CreateMutex()` with a `SECURITY_ATTRIBUTES` containing a NULL DACL so processes in different sessions can share the same mutex object, otherwise `CreateMutex()` may fail with an `ERROR_ACCESS_DENIED` error, which you are not handling, as you don't handle the possibility of `CreateMutex()` returning a NULL mutex handle. Although, an `ERROR_ACCESS_DENIED` error by itself will tell you the mutex already exists, even though you don't have rights to access it. – Remy Lebeau Apr 03 '19 at 22:20