我有一个访问某些文件和系统资源的应用程序,因此可能只有一个应用程序实例处于活动状态。这是通过创建一个命名的信号量并在已经分配了信号量时停止应用程序运行来实现的。 在过去(阅读:当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
现在,当我在User1上启动应用程序时,更改为User2并尝试启动应用程序,它将无法运行(按预期)。
但是当我运行我的程序的旧版本并在同一用户会话中使用新代码启动当前版本时,新代码忽略了旧代码创建的信号量,并启动了我的应用程序的第二个实例。 (不用说它崩溃了......)
在我看来,本地信号量不在全局信号量的范围内,否则无法创建具有相同名称的第二个对象。 我的问题是:全局信号量(新代码)如何检测到已经分配了具有相同名称的本地信号量(旧代码)?
请记住,这是向后兼容的问题。我不能简单地重新编译和重新分发我的应用程序的旧版本。
答案 0 :(得分:5)
kernel object namespaces的文档解释了:
对于在客户端会话下启动的进程,系统默认使用会话命名空间。
由于旧程序未明确包含命名空间,因此使用会话命名空间Local\
。这意味着旧程序会创建一个名为Local\xxx
的信号量。现在,新程序使用名为Global\xxx
的信号量。所以你有两个不同的信号量,程序完全没有意识到自己。
Local\xxx
的对象。 Global\xxx
的对象。 这里得出的明显结论是你需要创建两个对象。一个名为Local\xxx
,一个名为Global\xxx
。
请注意,无法对现有程序进行跨会话排除。他们已经使用Local\xxx
,现在你无法改变它。
您还必须修复新代码中的错误处理。您拨打CreateSemaphore
然后继续致电GetLastError
,而无需先检查拨打CreateSemaphore
时返回的值。