import os import sys import time import uuid import winreg from ctypes.wintypes import RECT import psutil import win32gui import winshell import ctypes import pythoncom import win32com.client import win32api from comtypes.typeinfo import LoadTypeLibEx from ctypes import OleDLL, c_void_p, byref, WINFUNCTYPE from uuid import UUID from tools.log import logger class Utils: @staticmethod def extract_between_strings(content, start_tag, end_tag): """ 提取content中从start_tag到end_tag之间的字符串。 参数: content (str): 包含要提取内容的字符串。 start_tag (str): 起始标记字符串。 end_tag (str): 结束标记字符串。 返回: str: start_tag和end_tag之间的内容。如果未找到匹配内容,则返回空字符串。 """ try: # 查找起始标记的位置 start_index = content.find(start_tag) if start_index == -1: return "" # 计算实际内容的起始位置 start_index += len(start_tag) # 查找结束标记的位置 end_index = content.find(end_tag, start_index) if end_index == -1: return "" # 提取中间的内容 return content[start_index:end_index] except Exception as e: # 错误处理,返回空字符串或抛出异常 return "" @staticmethod def get_registry_value(key_path, value_name,key=winreg.HKEY_CURRENT_USER): try: with winreg.OpenKey(key, key_path) as key: value, _ = winreg.QueryValueEx(key, value_name) return value except FileNotFoundError: return None @staticmethod def get_install_dir_from_shortcut(shortcut: str): try: with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") as key: value, _ = winreg.QueryValueEx(key, "Desktop") shortcut = winshell.shortcut(os.path.join(value, shortcut)) return shortcut.path except Exception as e: print(f"Error parsing shortcut {value + shortcut}: {e}") return None @staticmethod def makelong(low, high): return low | (high << 16) @staticmethod def set_window_position_and_size(hwnd, x, y, width, height): """ 设置指定窗口句柄的窗口的位置和大小。 :param hwnd: 窗口句柄 :param y: 窗口的新 y 坐标 :param x: 窗口的新 x 坐标 :param width: 窗口的新宽度 :param height: 窗口的新高度 """ if hwnd: win32gui.MoveWindow(hwnd, x, y, width, height, True) else: logger.error("无效的窗口句柄") print("无效的窗口句柄") @staticmethod def get_window_position_and_size(hwnd): """ 获取指定窗口句柄的窗口的位置和大小。 :param hwnd: 窗口句柄 :return: 窗口的位置和大小 """ if hwnd: rect = win32gui.GetWindowRect(hwnd) x, y, width, height = rect return x, y, width-x, height-y else: raise Exception("无效的窗口句柄") @staticmethod def get_mac_address(): mac_address = ':'.join(hex(uuid.getnode())[2:].zfill(12)[i:i + 2] for i in range(0, 12, 2)) return mac_address @staticmethod def is_admin(): try: return ctypes.windll.shell32.IsUserAnAdmin() except Exception: return False @staticmethod def run_as_admin(): # 以管理员权限重新运行当前脚本 script = sys.argv[0] params = ' '.join([f'"{param}"' for param in sys.argv[1:]]) try: ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f'"{script}" {params}', None, 1) except Exception as e: print(f"Failed to elevate privileges: {e}") sys.exit(1) class TimedLock: def __init__(self, lock, timeout): self.lock = lock self.timeout = timeout def __enter__(self): """尝试获取锁,如果在超时内无法获取,抛出 TimeoutError""" if not self.lock.acquire(timeout=self.timeout): raise TimeoutError(f"Could not acquire lock within {self.timeout} seconds.") return self def __exit__(self, exc_type, exc_val, exc_tb): """确保锁被释放""" self.lock.release() @staticmethod class Timer: def __init__(self): self._time_signs = {} def time_sign(self, sign): self._time_signs[sign] = time.time() def timer(self, sign, t): if sign not in self._time_signs: self._time_signs[sign] = time.time() return True if time.time() - self._time_signs[sign] > t: self._time_signs[sign] = time.time() return True return False @staticmethod def create_instance_from_com_dll(dll_path): """从 DLL 中创建 COM 对象实例""" com_classfactory = c_void_p(0) try: # 加载 DLL 和类型库 dll = OleDLL(dll_path) typelib = LoadTypeLibEx(dll_path) # 获取 COM 对象的 CLSID co_class_info = typelib.GetTypeInfo(1) co_class_attr = co_class_info.GetTypeAttr() clsid = UUID(str(co_class_attr.guid)).bytes_le # 获取 IClassFactory 接口 iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le dll.DllGetClassObject(clsid, iclassfactory, byref(com_classfactory)) # 从 IClassFactory 接口创建 COM 对象实例 class_factory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory) iptr = class_factory.CreateInstance(None, pythoncom.IID_IDispatch) return win32com.client.Dispatch(iptr, resultCLSID=None) except Exception as e: raise RuntimeError(f"Failed to create COM object from DLL: {e}") finally: # 释放 IClassFactory 接口 if com_classfactory: IUnknown_Release = WINFUNCTYPE(c_void_p)(2, 'Release', (), pythoncom.IID_IUnknown) IUnknown_Release(com_classfactory) @staticmethod def draw_text_on_window(hwnd, text): print(text) # 获取窗口的设备上下文 hdc = win32gui.GetDC(hwnd) # 设置文本颜色 color = win32api.RGB(255, 0, 0) # 红色 ctypes.windll.gdi32.SetTextColor(hdc, color) ctypes.windll.gdi32.SetBkMode(hdc, 1) # 透明背景 # 在指定位置绘制文本 x, y = 50, 50 # 文本的起始坐标 ctypes.windll.gdi32.ExtTextOutW(hdc, x, y, 0, None, text, len(text), None) # 释放设备上下文 win32gui.ReleaseDC(hwnd, hdc) @staticmethod def kill_process(pid): try: p = psutil.Process(pid) p.kill() # 强制终止进程 # 可选:等待进程结束,设置超时 p.wait(timeout=3) # 等待最多3秒 except psutil.NoSuchProcess: logger.warning(f"Process {pid} does not exist.") except psutil.AccessDenied: logger.warning(f"Access denied to terminate process {pid}.") except psutil.TimeoutExpired: logger.warning(f"Process {pid} could not be terminated in time.") except Exception as e: logger.exception(f"Error: {e}")