天天看点

python封装Windows Event实现全局跨进程同步锁

#coding=utf-8
import ctypes
from ctypes import wintypes


#某些结构体可能会在Windows各个版本中有所差异,所以MSDN中并没有详述这些结构体。
#这里的定义都是在VS2008下找到的
class ACLStruct(ctypes.Structure):
    _fields_ = [("AclRevision", wintypes.BYTE), 
                ("Sbz1", wintypes.BYTE), 
                ("AclSize", wintypes.WORD), 
                ("AceCount", wintypes.WORD), 
                ("Sbz2", wintypes.WORD)]


class SID_IDENTIFIER_AUTHORITYStruct(ctypes.Structure):
    _fields_ = [("Value", wintypes.BYTE * 6)]


class SIDStruct(ctypes.Structure):
    _fields_ = [("Revision", wintypes.BYTE), 
                ("SubAuthorityCount", wintypes.BYTE), 
                ("IdentifierAuthority", SID_IDENTIFIER_AUTHORITYStruct), 
                ("SubAuthority", wintypes.DWORD * 1)]


class SECURITY_DESCRIPTORStruct(ctypes.Structure):
    _fields_ = [("Revision", wintypes.BYTE), 
                ("Sbz1", wintypes.BYTE), 
                ("Control", wintypes.WORD), 
                ("Owner", ctypes.POINTER(SIDStruct)), 
                ("Group", ctypes.POINTER(SIDStruct)), 
                ("Sacl", ctypes.POINTER(ACLStruct)), 
                ("Dacl", ctypes.POINTER(ACLStruct))]


class SECURITY_ATTRIBUTESStruct(ctypes.Structure):
    _fields_ = [("nLength", wintypes.DWORD), 
                ("lpSecurityDescriptor", ctypes.POINTER(SECURITY_DESCRIPTORStruct)), 
                ("bInheritHandle", wintypes.BOOL)]




try:
    Dll_Advapi32 = ctypes.windll.LoadLibrary("Advapi32.dll")


    _InitializeSecurityDescriptor = Dll_Advapi32.InitializeSecurityDescriptor
    _InitializeSecurityDescriptor.argtypes = [ctypes.c_void_p, wintypes.DWORD]
    _InitializeSecurityDescriptor.restype = wintypes.BOOL


    _SetSecurityDescriptorDacl = Dll_Advapi32.SetSecurityDescriptorDacl
    _SetSecurityDescriptorDacl.argtypes = [ctypes.c_void_p, wintypes.BOOL, ctypes.c_void_p, wintypes.BOOL]
    _SetSecurityDescriptorDacl.restype = wintypes.BOOL


    #如果name要是unicode,可以把这里改成CreateEventW,还要改参数类型
    _CreateEvent = ctypes.windll.kernel32.CreateEventA
    _CreateEvent.argtypes = [ctypes.c_void_p, wintypes.BOOL, wintypes.BOOL, wintypes.LPCSTR]
    _CreateEvent.restype = wintypes.HANDLE


    _WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
    _WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD]
    _WaitForSingleObject.restype = wintypes.DWORD


    _SetEvent = ctypes.windll.kernel32.SetEvent
    _SetEvent.argtypes = [wintypes.HANDLE]
    _SetEvent.restype = wintypes.BOOL


    _CloseHandle = ctypes.windll.kernel32.CloseHandle
    _CloseHandle.argtypes = [wintypes.HANDLE]
    _CloseHandle.restype = wintypes.BOOL
except Exception, e:
    logger.error("init win32 sync object interface fail, error[%s]", str(e))
    raise




#Win32 Event的封装。name须为ANSI字符串。不可重入
class Win32SyncObject(object):
    def __init__(self, name):
        self.handle = None
        try:
            #设置安全属性是为了使服务中创建的锁也能在用户进程中访问,如果使用默认属性,会拒绝访问
            #如果不需要考虑这个问题,则可以去掉上述相关结构体的定义和这段设置安全属性的代码,直接使用默认值None
            sd = SECURITY_DESCRIPTORStruct()
            if not _InitializeSecurityDescriptor(ctypes.byref(sd), 1):
                err_code = ctypes.GetLastError()
                err_msg = ctypes.FormatError(err_code)
                raise ctypes.WinError(err_code, err_msg)
            if not _SetSecurityDescriptorDacl(ctypes.byref(sd), True, None, False):
                err_code = ctypes.GetLastError()
                err_msg = ctypes.FormatError(err_code)
                raise ctypes.WinError(err_code, err_msg)
            
            sa = SECURITY_ATTRIBUTESStruct()
            sa.nLength = 12
            sa.lpSecurityDescriptor = ctypes.pointer(sd)
            sa.bInheritHandle = False
            
            self.handle = _CreateEvent(ctypes.byref(sa), False, False, name)
        except Exception, e:
            self.handle = None
            raise
        
        if not self.handle:
            err_code = ctypes.GetLastError()
            err_msg = ctypes.FormatError(err_code)
            raise ctypes.WinError(err_code, err_msg)
        else:
            if 0 == ctypes.GetLastError():#此时是创建
                _SetEvent(self.handle)
    
    def __del__(self):
        pass
        #应该关闭句柄,但是这个析构执行的时机有点奇怪,此时self.handle已经被释放了
        #哪位大神能解释一下吗
        #_CloseHandle(self.handle)
        #self.handle = None
    
    def acquire(self):
        try:
            ret = _WaitForSingleObject(self.handle, 0xFFFFFFFF)#永久等待
        except Exception, e:
            raise
        
        if ret in (0, 0x80):
            return True
        else:
            err_code = ctypes.GetLastError()
            err_msg = ctypes.FormatError(err_code)
            return False
    
    def release(self):
        try:
            ret = _SetEvent(self.handle)
        except Exception, e:
            raise
        
        if not ret:
            err_code = ctypes.GetLastError()
            err_msg = ctypes.FormatError(err_code)
            return False
        else:
            return True




#使用示例
#全局跨进程访问必须是Global命名空间下的
if_lock = Win32SyncObject(name = "Global\\CAA_{6A6B3EE2-8F6C-41f0-AA08-763AAE0C1D98}")
if_lock.acquire()
if_lock.release()
print "OK"