#!/usr/bin/env python3
"""
db_lock_detector.py - 数据库锁定检测模块

提供函数来检测 SQLite 数据库是否被锁定，并找出锁定进程
"""

import os
import subprocess
import ctypes
from ctypes import wintypes

# 定义 Windows API 常量
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004
OPEN_EXISTING = 3
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
ERROR_SHARING_VIOLATION = 32
ERROR_LOCK_VIOLATION = 33

# 定义 Windows API 结构
class SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = [
        ("nLength", wintypes.DWORD),
        ("lpSecurityDescriptor", wintypes.LPVOID),
        ("bInheritHandle", wintypes.BOOL)
    ]

# 加载 Windows API 函数
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
CreateFile = kernel32.CreateFileW
CreateFile.argtypes = [
    wintypes.LPCWSTR,  # lpFileName
    wintypes.DWORD,     # dwDesiredAccess
    wintypes.DWORD,     # dwShareMode
    ctypes.POINTER(SECURITY_ATTRIBUTES),  # lpSecurityAttributes
    wintypes.DWORD,     # dwCreationDisposition
    wintypes.DWORD,     # dwFlagsAndAttributes
    wintypes.HANDLE     # hTemplateFile
]
CreateFile.restype = wintypes.HANDLE

CloseHandle = kernel32.CloseHandle
CloseHandle.argtypes = [wintypes.HANDLE]
CloseHandle.restype = wintypes.BOOL


def is_db_locked(db_path):
    """
    检查数据库文件是否被锁定
    
    参数:
        db_path (str): 数据库文件路径
    
    返回:
        bool: True 如果被锁定，False 否则
    """
    # 构建安全属性
    sa = SECURITY_ATTRIBUTES()
    sa.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
    sa.lpSecurityDescriptor = None
    sa.bInheritHandle = False
    
    # 尝试以独占方式打开文件
    hFile = CreateFile(
        db_path,
        GENERIC_READ | GENERIC_WRITE,
        0,  # 不共享
        ctypes.byref(sa),
        OPEN_EXISTING,
        0,
        None
    )
    
    if hFile == wintypes.HANDLE(-1):
        error_code = ctypes.get_last_error()
        if error_code in (ERROR_SHARING_VIOLATION, ERROR_LOCK_VIOLATION):
            return True
        return False
    else:
        # 成功打开，文件未被锁定
        CloseHandle(hFile)
        return False


def get_locking_processes(file_path):
    """
    查找锁定指定文件的进程
    
    参数:
        file_path (str): 文件路径
    
    返回:
        list: 锁定进程列表，每个元素为 "进程名 (PID: 进程ID)" 格式
    """
    processes = []
    
    try:
        # 构建 PowerShell 命令
        ps_command = f"Get-Process | ForEach-Object {{ $process = $_; try {{ $_.Modules | ForEach-Object {{ if ($_.FileName -eq '{file_path.replace(chr(92), chr(92)*2)}') {{ $process.Name + ' (PID: ' + $process.Id + ')' }} }} }} catch {{}} }}"
        
        # 执行 PowerShell 命令
        result = subprocess.run(
            ['powershell', '-Command', ps_command],
            capture_output=True,
            text=True,
            timeout=10
        )
        
        if result.stdout.strip():
            processes = [line.strip() for line in result.stdout.strip().split('\n') if line.strip()]
        else:
            # 尝试另一种方法
            ps_command2 = f"Handle.exe '{file_path}' 2>&1 | Select-String -Pattern 'PID:'"
            try:
                result2 = subprocess.run(
                    ['powershell', '-Command', ps_command2],
                    capture_output=True,
                    text=True,
                    timeout=10
                )
                if result2.stdout.strip():
                    # 解析 Handle.exe 输出
                    for line in result2.stdout.strip().split('\n'):
                        if 'PID:' in line:
                            # 提取进程信息
                            parts = line.split()
                            if len(parts) >= 3:
                                pid = parts[1]
                                process_name = parts[2].strip('()')
                                processes.append(f"{process_name} (PID: {pid})")
            except Exception:
                pass
                
    except Exception:
        pass
    
    return processes


def check_db_lock_status(db_path):
    """
    检查数据库锁定状态并返回锁定进程
    
    参数:
        db_path (str): 数据库文件路径
    
    返回:
        dict: 包含锁定状态和锁定进程的字典
              {"is_locked": bool, "processes": list}
    """
    is_locked = is_db_locked(db_path)
    processes = []
    
    if is_locked:
        processes = get_locking_processes(db_path)
    
    return {
        "is_locked": is_locked,
        "processes": processes
    }


if __name__ == "__main__":
    # 测试代码
    db_path = r"\\ll\D\xtrssvjj\db\dpd.db"
    result = check_db_lock_status(db_path)
    
    print(f"数据库是否被锁定: {result['is_locked']}")
    if result['is_locked']:
        print("锁定进程:")
        for process in result['processes']:
            print(f"  - {process}")
    else:
        print("数据库未被锁定")