互斥量Mutex的使用
我们知道,Windows提供了很多内核对象可以给大家使用,如Event,Mutex,ATom,临界区,信号量等等,但使用这些的时候,需要注意一个问题:用户和终端服务。Windows提供了终端服务,你的程序在终端服务中运行,其信号量,互斥量等都是独立和分开的。
以检测程序重复运行来说,我们可以使用ATom,是一个非常有效的方法,但是我们要考虑到你的程序可能崩溃,那么就不能正常删除一个ATOM,这样可能出现你的程序没有运行,却能检测到ATOM存在,出现误判。即便你能保证你的程序绝对正确无误也不能保证你的程序不被用户强行杀进程杀死!所以用ATOM还是不可靠的。其他的内核对象是会随着进程的消亡而消亡的,所以不存在和ATOM类似的情况。考虑起来,Mutex是一个很好的东东,难怪大家都用它。
使用Mutex的时候,Windows默认会检测SYSTEM,LOCAL SERVICE,NETWORK SERVICE等等系统帐户中是否有你创建的Mutex,你可以检测和查找到,但是当以终端服务方式(远程桌面)运行你的程序,你会发现,你的程序无法检测到Mutex是否已经创建的。
用下面的代码可以做到在终端服务中也可以正常检测在整个系统中是否是唯一的Mutex:
const
// 用于检测重复性的互斥信号量定义,请勿本地化本字符串
CS_SVR_DESK_MUTEX = '_SVR_DESK_KINGRON_';
var
AMutex: THandle;
// 查找信号量是否已经存在
// 查找两次是为了避免在终端服务中运行的时候无法检测的问题
AMutex := OpenMutex(MUTEX_MODIFY_STATE, True, CS_SVR_DESK_MUTEX);
if AMutex = 0 then
begin
AMutex := OpenMutex(MUTEX_MODIFY_STATE, True, 'Global\' + CS_SVR_DESK_MUTEX);
// 如果返回的错误是权限问题,说明程序也已经运行了,附一个假值
// 原因是CreateMutex的时候没有指定权限令牌为Everyone可以访问了
// CreateMutex默认创建的内核对象只有自己和一些系统帐户可以访问,其他用户拒绝
if GetLastError = ERROR_ACCESS_DENIED then AMutex := INVALID_HANDLE_VALUE;
end;
if AMutex <> 0 then // 找到说明程序已经运行了,退出即可
begin
if not IsService then // 桌面方式提示用户一下
MessageBox(0, PChar(CS_AlreadyRunning), PChar(CS_Error),
MB_OK + MB_ICONSTOP + MB_TOPMOST);
CloseHandle(AMutex);
Exit;
end
else // 没有运行,创建互斥量
AMutex := CreateMutex(nil, True, 'Global\' + CS_SVR_DESK_MUTEX);