2014-12-01 5 views
2

У меня есть приложение, которое обращается к некоторым файлам и системным ресурсам, поэтому может быть только один экземпляр приложения активным. Это достигается путем создания имени семафора и остановки приложения, когда семафор уже назначен. В прошлом (читал: когда Windows XP была самой распространенной операционной системой), которая работала хорошо, но теперь мы заметили, что старый код не работал с несколькими сеансами пользователя.Глобальный Семафор игнорирует локальный Семафор

Вот старый код:

hInstanceSem := CreateSemaphore(nil, 0, 1, PChar(GetProductName(Application.ExeName))); 
if (hInstanceSem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then 
// do not run the Application 

Так что я сделал некоторые исследования, узнали о глобальных семафоров и изменил код для этого:

function CreateGlobalSemaphor(SemaphorName: String): Cardinal; 
var 
    desc: SECURITY_DESCRIPTOR; 
    att : TSecurityAttributes; 
    sem : Cardinal; 
begin 
    att.nLength := SizeOf(TSecurityAttributes); 
    att.bInheritHandle := true; 
    att.lpSecurityDescriptor := @desc; 

    InitializeSecurityDescriptor(att.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); 
    SetSecurityDescriptorDacl(att.lpSecurityDescriptor, True, nil, False); 

    sem := CreateSemaphore(@att, 0, 1, PChar('Global\' + SemaphorName)); 
    if (sem <> 0) and (GetLastError() <> ERROR_ALREADY_EXISTS) then begin 
    Result := sem; 
    end else begin 
    Result := 0; 
    CloseHandle(sem); 
    end; 
end; 

if CreateGlobalSemaphor(GetProductName(Application.ExeName)) = 0 then 
// do not run the Application 

Теперь, когда я запустить приложение на Пользователя1, измените на User2 и попробуйте запустить приложение, оно не будет запускаться (как предполагалось).

Но когда я запускаю старую версию своей программы и запускаю текущую версию с новым кодом в том же сеансе пользователя, новый код игнорирует Семафор, созданный старым кодом, и запускается второй экземпляр моего приложения. (Само собой разумеется, что он сбой ...)

Мне кажется, что локальный Семафор выходит за рамки глобального Семафора, иначе не может быть создан второй объект с тем же именем. Мой вопрос: как глобальный Семафор (новый код) обнаруживает, что уже назначен локальный Семафор (старый код) с тем же именем?

Обратите внимание, что это проблема обратной совместимости. Я не могу просто перекомпилировать и перераспределить старые версии моего приложения.

+0

Позвольте мне предположить, что вызов функции GetLastError возвращает 5 ('ERROR_ACCESS_DENIED'). Нет, серьезно, что он возвращается? – TLama

+1

Почему бы вам не попробовать * и создать локальный семафор (через старый код) в своем новом коде и сначала проверить результат, прежде чем обращаться с глобальным семафором? – kobik

+0

@TLama: 'GetLastError' возвращает 0 (NO_ERROR) – YElm

ответ

5

Документация kernel object namespaces объясняет:

Для процессов, запущенных в клиентской сессии, система использует пространство имен сеанса по умолчанию.

Поскольку в старой программе явно не указано пространство имен, используется пространство имен сеанса, Local\. Это означает, что старая программа создает семафор с именем Local\xxx. Теперь новая программа использует семафор с именем Global\xxx. Таким образом, у вас есть два разных семафора, и программы полностью не знают о себе.

  • Если вы хотите, чтобы новая программа взаимодействовала со старой программой, вы должны использовать объект с именем Local\xxx.
  • Если вы хотите, чтобы новая программа блокировала другие новые программы в разных сеансах, вы должны использовать объект с именем Global\xxx.

Очевидный вывод здесь состоит в том, что вам нужно создать два объекта. Один по имени Local\xxx и один по имени Global\xxx.

Обратите внимание, что невозможно использовать межсезовое исключение для существующих программ. Они уже используют Local\xxx, и теперь у вас нет возможности изменить это.

Вы также должны исправить ошибку в своем новом коде.Вы вызываете CreateSemaphore, а затем переходите к вызову GetLastError без предварительной проверки значения, возвращаемого вызовом CreateSemaphore.

+0

Спасибо за разъяснение. Я был настолько погружен в свои мысли, что не видел простого решения - создать два семафора с тем же именем – YElm

+0

Ну, одно и то же имя, другой префикс пространства имен –

Смежные вопросы