Как избежать нескольких экземпляров разных пользователей, но разрешить несколько экземпляров в одном пользовательском сеансе

У меня есть приложение для Windows. Я хочу разрешить несколько экземпляров для одного пользовательского сеанса, но мне не нужно несколько экземпляров от разных пользователей. Проще говоря, если A входит в Windows, то он может запускать приложения столько раз, сколько ему нужно, но позже, B также входит в систему, он должен ждать, пока все приложения из A не будут закрыты.

Это возможно?


person doctorlai    schedule 30.01.2015    source источник
comment
Это требование безопасности или требование бизнеса? Задействован ли сервер? Можете ли вы управлять политикой безопасности Windows?   -  person AK_    schedule 30.01.2015
comment
сервер не задействован. Я хочу, чтобы приложение проверялось исключительно на стороне клиента   -  person doctorlai    schedule 30.01.2015
comment
Вы не ответили на первый и последний вопросы? Также какое приложение вы разрабатываете на С#? Win32?   -  person AK_    schedule 30.01.2015
comment
это требование бизнеса. Я не знаю о политике безопасности Windows. мы можем использовать C# или C++.   -  person doctorlai    schedule 30.01.2015
comment
это довольно легко сделать с мьютексом. или, в худшем случае, мьютекс и файл с отображением памяти   -  person AK_    schedule 30.01.2015
comment
@AK_: Из любопытства, какова цель объекта сопоставления файлов в этом сценарии?   -  person IInspectable    schedule 31.01.2015
comment
это зависит от фактического требования. иногда вам нужно, чтобы процессы действительно передавали некоторые данные друг другу. В Windows API файла с отображением памяти также используется для общей памяти. msdn.microsoft.com/en -нас/библиотека/окна/рабочий стол/   -  person AK_    schedule 31.01.2015


Ответы (2)


Это требование может быть выполнено с помощью именованного объекта Mutex. в глобальном пространстве имен объектов ядра. Объект мьютекса создается с помощью CreateMutex. Вот небольшая программа, иллюстрирующая ее использование:

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

Первый экземпляр приложения создаст объект мьютекса и GetLastError возвращает ERROR_SUCCESS (0). Последующие экземпляры будут получать ссылку на существующий объект мьютекса, а GetLastError возвращает ERROR_ALREADY_EXISTS (183). Экземпляры, запущенные из другого клиентского сеанса, не получат ссылку на объект мьютекса, и GetLastError возвращает ERROR_ACCESS_DENIED (5).

Несколько замечаний по реализации:

  • Объект мьютекса создается в глобальном пространстве имен объектов ядра путем добавления префикса "Global\".
  • Объект мьютекса должен иметь уникальное имя, чтобы на него можно было ссылаться из любого места. Самый простой способ создать уникальное имя — использовать строковое представление GUID (идентификаторы GUID можно создать с помощью инструментальной части Visual Studio guidgen.exe). Не используйте GUID из примера кода, потому что это сделает кто-то другой.
  • Нет вызова CloseHandle для освобождения мьютекса объект. Это сделано намеренно. (См. CreateMutex: Система закрывается. дескриптор автоматически, когда процесс завершается. Объект мьютекса уничтожается, когда его последний дескриптор был закрыт.)
  • Объект мьютекса создается с использованием дескриптора безопасности по умолчанию. Списки управления доступом в дескрипторе безопасности по умолчанию исходят из основного маркера или маркера олицетворения создателя. Если процессы в разных клиентских сеансах выполняются с одним и тем же первичным маркером/токеном олицетворения, мьютекс по-прежнему можно получить из обоих сеансов. В этой ситуации предлагаемое решение потенциально не соответствует требованиям. Непонятно, что должно произойти, если пользователь запускает приложение, выдающее себя за пользователя другой клиентской сессии.
  • Объект мьютекса используется исключительно как дозорный и не участвует в синхронизации.
person IInspectable    schedule 31.01.2015
comment
спасибо... я не поверил этому коду, пока не попробовал на сервере, и он работает, как и ожидалось. - person doctorlai; 01.02.2015

Я использовал предыдущий код под win10/VS 2017/64 бит.

Неправильно тестировать if (::CreateMutex..

мы должны проверить ошибку, поэтому используйте:

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;
}
person ingconti    schedule 09.09.2018
comment
Это не соответствует требованиям, изложенным в вопросе. Примечательно, что это не позволит использовать несколько экземпляров из одного и того же пользовательского сеанса. Этот код позволит использовать только один экземпляр для всех пользовательских сеансов. Хотя это общее требование, оно не соответствует вопросу. - person IInspectable; 09.11.2018
comment
Кроме того, сообщение об ошибке неверно, если CreateMutex возвращает ненулевое значение, а код ошибки ERROR_ALREADY_EXISTS. В этом случае мьютекс захвачен, но вывод читается как Мьютекс НЕ захвачен. - person IInspectable; 09.11.2018