C ++ мьютекс не блокирует учетные записи Windows - PullRequest
0 голосов
/ 08 ноября 2018

Когда я запускаю свою программу на C ++, мьютекс winAPI корректно блокируется, поэтому, если я пытаюсь запустить второй экземпляр, он проверяет, могу ли я открыть заблокированный мьютекс: OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());

Я получаю ожидаемую ошибку: mutexName << " is already running on this machine! Aborting!";

пока все хорошо

Проблема в том, что у меня запущен 1 экземпляр программы и я переключаюсь на другую учетную запись Windows. Мьютекс должен быть заблокирован, но когда я запускаю второй экземпляр, он игнорирует, что мьютекс заблокирован и запускается в любом случае!

поэтому мой вопрос к этой строке:

 m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());

Когда он запускается во второй раз, почему он устанавливает m_hMutex! = NULL, когда второй экземпляр запускается на той же учетной записи Windows ...

но устанавливает m_hMutex == NULL, когда второй экземпляр запускается для новой учетной записи?

В обоих случаях параметры функции в точности совпадают, и этот код запускается на D: \

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Проблема в том, что Windows поддерживает отдельные пространства имен для различных объектов ядра. Объекты в одном пространстве имен могут иметь то же имя, что и объекты в другом пространстве имен, не мешая друг другу. Если вы хотите, чтобы объект был общим для всех сеансов, вы должны создать его в глобальном пространстве имен.

От MSDN

Отдельные пространства имен сеансов клиентов позволяют нескольким клиентам запускать одни и те же приложения, не мешая друг другу. Для процессов, запущенных в сеансе клиента, система по умолчанию использует пространство имен сеанса. Однако эти процессы могут использовать глобальное пространство имен, добавляя префикс «Global \» к имени объекта.

Дальнейшее чтение: Пространства имен объектов ядра

0 голосов
/ 08 ноября 2018

Каждый вошедший в систему пользователь работает в своем собственном сеансе рабочего стола. Чтобы получить доступ к объекту ядра с именем , например, к мьютексу, между сеансами пользователя, необходимо добавить к имени объекта префикс с префиксом пространства имен Global\, в противном случае вы в конечном итоге создадите отдельные объекты мьютекса локально для каждого пользователя. сессия.

По документам CreateMutex() и OpenMutex():

Имя может иметь префикс «Global» или «Local» для явного создания объекта в глобальном или сеансовом пространстве имен. Остальная часть имени может содержать любой символ, кроме символа обратной косой черты. Для получения дополнительной информации см. Пространства имен объектов ядра .

И согласно Пространствам имен объекта ядра Документация:

Отдельные пространства имен сеансов клиентов позволяют нескольким клиентам запускать одни и те же приложения, не мешая друг другу. Для процессов, запущенных в сеансе клиента, система по умолчанию использует пространство имен сеанса. Однако эти процессы могут использовать глобальное пространство имен, добавляя префикс "Global\" к имени объекта. Например, следующий код вызывает `CreateEvent и создает объект события с именем CSAPP в глобальном пространстве имен:

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

...

Другое использование глобального пространства имен - для приложений, которые используют именованные объекты, чтобы обнаружить, что во всех сеансах уже есть экземпляр приложения, работающего в системе. Этот именованный объект должен быть создан или открыт в глобальном пространстве имен вместо пространства имен для сеанса . Более распространенный случай запуска приложения один раз за сеанс поддерживается по умолчанию, поскольку именованный объект создается в пространстве имен для каждого сеанса.

Кроме того, вы должны использовать CreateMutex() вместо OpenMutex(), чтобы избежать условия гонки, которое позволило бы другому экземпляру вашего приложения создать мьютекс до того, как текущий экземпляр сможет его создать. GetLastError() сообщит ERROR_ALREADY_EXISTS, если CreateMutex() успешно и мьютекс уже существует. Не звоните OpenMutex(), если CreateMutex() не сообщит об ошибке ERROR_ACCESS_DENIED, согласно CreateMutex документации :

Если lpName соответствует имени существующего именованного объекта мьютекса, эта функция запрашивает MUTEX_ALL_ACCESS право доступа . В этом случае параметр bInitialOwner игнорируется, поскольку он уже был установлен процессом создания.

...

Если мьютекс является именованным мьютексом и объект существовал до этого вызова функции, возвращаемое значение является дескриптором существующего объекта, GetLastError возвращает ERROR_ALREADY_EXISTS, bInitialOwner игнорируется, а вызывающий поток не предоставлено право собственности. Однако, если у вызывающего абонента ограниченные права доступа, функция завершится ошибкой с ERROR_ACCESS_DENIED, и вызывающий абонент должен использовать функцию OpenMutex .

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

mutexName = "Global\\MyMutexName";

m_hMutex = ::CreateMutex(NULL, FALSE, mutexName.c_str());
if ((!m_hMutex) && (GetLastError() == ERROR_ACCESS_DENIED)) {
    m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
}

if (!m_hMutex) {
    ... << mutexName << " cannot be accessed! Aborting!";
    //...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
    ... << mutexName << " is already running on this machine! Aborting!";
    //...
}
else {
    //...
}

Или в Vista и более поздних версиях вместо нее можно использовать CreateMutexEx():

mutexName = "Global\\MyMutexName";

m_hMutex = ::CreateMutexEx(NULL, mutexName.c_str(), 0, SYNCHRONIZE);
if (!m_hMutex) {
    ... << mutexName << " cannot be accessed! Aborting!";
    //...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
    ... << mutexName << " is already running on this machine! Aborting!";
    //...
}
else {
    //...
}
...