Initial SyncVito release with installer updater
This commit is contained in:
commit
b9e4c09f49
41 files changed
+4331
No files matched your search
@@ -0,0 +1,114 @@
|
||||
"""Ermittlung von Prozessen, die Dateien unter Windows sperren."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
from ctypes import wintypes
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = ["LockingProcess", "find_locking_processes", "is_file_locked"]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class LockingProcess:
|
||||
"""Ein vom Windows Restart Manager gemeldeter Prozess."""
|
||||
|
||||
pid: int
|
||||
name: str
|
||||
|
||||
|
||||
def is_file_locked(path: Path) -> bool:
|
||||
"""Prüft eine vorhandene Datei durch exklusives Öffnen ohne Schreibzugriff."""
|
||||
if not path.exists() or os.name != "nt":
|
||||
return False
|
||||
handle = ctypes.windll.kernel32.CreateFileW(
|
||||
str(path),
|
||||
0x80000000,
|
||||
0,
|
||||
None,
|
||||
3,
|
||||
0x80,
|
||||
None,
|
||||
)
|
||||
invalid_handle = ctypes.c_void_p(-1).value
|
||||
if handle == invalid_handle:
|
||||
return ctypes.get_last_error() in {5, 32, 33}
|
||||
ctypes.windll.kernel32.CloseHandle(handle)
|
||||
return False
|
||||
|
||||
|
||||
def find_locking_processes(path: Path) -> list[LockingProcess]:
|
||||
"""Liefert sperrende Prozesse über die Windows-Restart-Manager-API."""
|
||||
if os.name != "nt" or not path.exists():
|
||||
return []
|
||||
try:
|
||||
return _restart_manager_processes(path)
|
||||
except (OSError, AttributeError, ctypes.ArgumentError):
|
||||
return []
|
||||
|
||||
|
||||
def _restart_manager_processes(path: Path) -> list[LockingProcess]:
|
||||
rstrtmgr = ctypes.WinDLL("Rstrtmgr")
|
||||
session_handle = wintypes.DWORD()
|
||||
session_key = ctypes.create_unicode_buffer(33)
|
||||
if rstrtmgr.RmStartSession(ctypes.byref(session_handle), 0, session_key) != 0:
|
||||
return []
|
||||
try:
|
||||
resources = (wintypes.LPCWSTR * 1)(str(path))
|
||||
if rstrtmgr.RmRegisterResources(session_handle, 1, resources, 0, None, 0, None) != 0:
|
||||
return []
|
||||
needed = wintypes.UINT(0)
|
||||
count = wintypes.UINT(0)
|
||||
reboot_reasons = wintypes.DWORD(0)
|
||||
result = rstrtmgr.RmGetList(
|
||||
session_handle,
|
||||
ctypes.byref(needed),
|
||||
ctypes.byref(count),
|
||||
None,
|
||||
ctypes.byref(reboot_reasons),
|
||||
)
|
||||
if result not in {0, 234} or needed.value == 0:
|
||||
return []
|
||||
process_info_type = _rm_process_info_type()
|
||||
process_array = (process_info_type * needed.value)()
|
||||
count.value = needed.value
|
||||
result = rstrtmgr.RmGetList(
|
||||
session_handle,
|
||||
ctypes.byref(needed),
|
||||
ctypes.byref(count),
|
||||
process_array,
|
||||
ctypes.byref(reboot_reasons),
|
||||
)
|
||||
if result != 0:
|
||||
return []
|
||||
return [
|
||||
LockingProcess(
|
||||
pid=int(item.Process.dwProcessId), name=item.strAppName or f"Prozess {item.Process.dwProcessId}"
|
||||
)
|
||||
for item in process_array[: count.value]
|
||||
]
|
||||
finally:
|
||||
rstrtmgr.RmEndSession(session_handle)
|
||||
|
||||
|
||||
def _rm_process_info_type() -> type[ctypes.Structure]:
|
||||
class FileTime(ctypes.Structure):
|
||||
_fields_ = [("dwLowDateTime", wintypes.DWORD), ("dwHighDateTime", wintypes.DWORD)]
|
||||
|
||||
class UniqueProcess(ctypes.Structure):
|
||||
_fields_ = [("dwProcessId", wintypes.DWORD), ("ProcessStartTime", FileTime)]
|
||||
|
||||
class ProcessInfo(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Process", UniqueProcess),
|
||||
("strAppName", wintypes.WCHAR * 256),
|
||||
("strServiceShortName", wintypes.WCHAR * 64),
|
||||
("ApplicationType", wintypes.UINT),
|
||||
("AppStatus", wintypes.ULONG),
|
||||
("TSSessionId", wintypes.DWORD),
|
||||
("bRestartable", wintypes.BOOL),
|
||||
]
|
||||
|
||||
return ProcessInfo
|
||||
Reference in new issue
Block a user