Невозможно предотвратить несколько экземпляров одной и той же программы от разных пользователей

У меня есть следующий код. Это предотвращает несколько экземпляров одного и того же приложения под одним и тем же пользователем. Однако я также хочу запретить другому пользователю на том же компьютере запускать приложение, когда оно уже запущено другим пользователем. Код успешно создает мьютекс, хотя приложение уже запущено другим пользователем. Не могу понять, что я делаю неправильно здесь. Любая помощь приветствуется.

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;
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
510
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не показали фактическое значение PREDEFINED_UNIQUE_ID, но я предполагаю, что он создает мьютекс не в Global пространство имен ядра, а вместо этого в пространствах имен для каждого сеанса. Использование пространства имен Global требуется для охвата пользовательских сеансов:

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.

Обновлено: вам также необходимо обработать случай, когда CreateMutex() не может получить доступ к мьютексу в другом сеансе из-за разрешений безопасности. Вам нужно обработать случай, когда GetLastError() возвращает ERROR_ACCESS_DENIED, что само по себе означает, что мьютекс существует.

Попробуйте еще что-нибудь вроде этого:

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

Или, чтобы свести к минимуму вероятность того, что ERROR_ACCESS_DENIED будет сообщено, вы можете предоставить NULL DACL для 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);

я изменил значение PREDEFINED_UNIQUE_ID в "Глобальный\\CSAPP", и у меня все та же проблема?!

Vinny 04.04.2019 00:11

Суть заключалась в том, чтобы просто добавить префикс Global\ к любому имени, которое вы уже использовали, вам не нужно было менять имя на CSAPP, это просто имя, используемое в примере MSDN. Но в любом случае у вас не может быть такой же проблемы, когда мьютекс находится в глобальном пространстве имен.

Remy Lebeau 04.04.2019 00:16

Вам необходимо предоставить CreateMutex()SECURITY_ATTRIBUTES, содержащий NULL DACL, чтобы процессы в разных сеансах могли совместно использовать один и тот же объект мьютекса, иначе CreateMutex() может завершиться ошибкой ERROR_ACCESS_DENIED, которую вы не обрабатываете, поскольку вы не обрабатываете возможность CreateMutex() возврата дескриптор мьютекса NULL. Хотя ошибка ERROR_ACCESS_DENIED сама по себе сообщит вам, что мьютекс уже существует, даже если у вас нет прав на доступ к нему.

Remy Lebeau 04.04.2019 00:20

Другие вопросы по теме