utils.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import os
  2. import sys
  3. import time
  4. import uuid
  5. import winreg
  6. from ctypes.wintypes import RECT
  7. import psutil
  8. import win32gui
  9. import winshell
  10. import ctypes
  11. import pythoncom
  12. import win32com.client
  13. import win32api
  14. from comtypes.typeinfo import LoadTypeLibEx
  15. from ctypes import OleDLL, c_void_p, byref, WINFUNCTYPE
  16. from uuid import UUID
  17. from tools.log import logger
  18. class Utils:
  19. @staticmethod
  20. def extract_between_strings(content, start_tag, end_tag):
  21. """
  22. 提取content中从start_tag到end_tag之间的字符串。
  23. 参数:
  24. content (str): 包含要提取内容的字符串。
  25. start_tag (str): 起始标记字符串。
  26. end_tag (str): 结束标记字符串。
  27. 返回:
  28. str: start_tag和end_tag之间的内容。如果未找到匹配内容,则返回空字符串。
  29. """
  30. try:
  31. # 查找起始标记的位置
  32. start_index = content.find(start_tag)
  33. if start_index == -1:
  34. return ""
  35. # 计算实际内容的起始位置
  36. start_index += len(start_tag)
  37. # 查找结束标记的位置
  38. end_index = content.find(end_tag, start_index)
  39. if end_index == -1:
  40. return ""
  41. # 提取中间的内容
  42. return content[start_index:end_index]
  43. except Exception as e:
  44. # 错误处理,返回空字符串或抛出异常
  45. return ""
  46. @staticmethod
  47. def get_registry_value(key_path, value_name,key=winreg.HKEY_CURRENT_USER):
  48. try:
  49. with winreg.OpenKey(key, key_path) as key:
  50. value, _ = winreg.QueryValueEx(key, value_name)
  51. return value
  52. except FileNotFoundError:
  53. return None
  54. @staticmethod
  55. def get_install_dir_from_shortcut(shortcut: str):
  56. try:
  57. with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
  58. r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") as key:
  59. value, _ = winreg.QueryValueEx(key, "Desktop")
  60. shortcut = winshell.shortcut(os.path.join(value, shortcut))
  61. return shortcut.path
  62. except Exception as e:
  63. print(f"Error parsing shortcut {value + shortcut}: {e}")
  64. return None
  65. @staticmethod
  66. def makelong(low, high):
  67. return low | (high << 16)
  68. @staticmethod
  69. def set_window_position_and_size(hwnd, x, y, width, height):
  70. """
  71. 设置指定窗口句柄的窗口的位置和大小。
  72. :param hwnd: 窗口句柄
  73. :param y: 窗口的新 y 坐标
  74. :param x: 窗口的新 x 坐标
  75. :param width: 窗口的新宽度
  76. :param height: 窗口的新高度
  77. """
  78. if hwnd:
  79. win32gui.MoveWindow(hwnd, x, y, width, height, True)
  80. else:
  81. logger.error("无效的窗口句柄")
  82. print("无效的窗口句柄")
  83. @staticmethod
  84. def get_window_position_and_size(hwnd):
  85. """
  86. 获取指定窗口句柄的窗口的位置和大小。
  87. :param hwnd: 窗口句柄
  88. :return: 窗口的位置和大小
  89. """
  90. if hwnd:
  91. rect = win32gui.GetWindowRect(hwnd)
  92. x, y, width, height = rect
  93. return x, y, width-x, height-y
  94. else:
  95. raise Exception("无效的窗口句柄")
  96. @staticmethod
  97. def get_mac_address():
  98. mac_address = ':'.join(hex(uuid.getnode())[2:].zfill(12)[i:i + 2] for i in range(0, 12, 2))
  99. return mac_address
  100. @staticmethod
  101. def is_admin():
  102. try:
  103. return ctypes.windll.shell32.IsUserAnAdmin()
  104. except Exception:
  105. return False
  106. @staticmethod
  107. def run_as_admin():
  108. # 以管理员权限重新运行当前脚本
  109. script = sys.argv[0]
  110. params = ' '.join([f'"{param}"' for param in sys.argv[1:]])
  111. try:
  112. ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f'"{script}" {params}', None, 1)
  113. except Exception as e:
  114. print(f"Failed to elevate privileges: {e}")
  115. sys.exit(1)
  116. class TimedLock:
  117. def __init__(self, lock, timeout):
  118. self.lock = lock
  119. self.timeout = timeout
  120. def __enter__(self):
  121. """尝试获取锁,如果在超时内无法获取,抛出 TimeoutError"""
  122. if not self.lock.acquire(timeout=self.timeout):
  123. raise TimeoutError(f"Could not acquire lock within {self.timeout} seconds.")
  124. return self
  125. def __exit__(self, exc_type, exc_val, exc_tb):
  126. """确保锁被释放"""
  127. self.lock.release()
  128. @staticmethod
  129. class Timer:
  130. def __init__(self):
  131. self._time_signs = {}
  132. def time_sign(self, sign):
  133. self._time_signs[sign] = time.time()
  134. def timer(self, sign, t):
  135. if sign not in self._time_signs:
  136. self._time_signs[sign] = time.time()
  137. return True
  138. if time.time() - self._time_signs[sign] > t:
  139. self._time_signs[sign] = time.time()
  140. return True
  141. return False
  142. @staticmethod
  143. def create_instance_from_com_dll(dll_path):
  144. """从 DLL 中创建 COM 对象实例"""
  145. com_classfactory = c_void_p(0)
  146. try:
  147. # 加载 DLL 和类型库
  148. dll = OleDLL(dll_path)
  149. typelib = LoadTypeLibEx(dll_path)
  150. # 获取 COM 对象的 CLSID
  151. co_class_info = typelib.GetTypeInfo(1)
  152. co_class_attr = co_class_info.GetTypeAttr()
  153. clsid = UUID(str(co_class_attr.guid)).bytes_le
  154. # 获取 IClassFactory 接口
  155. iclassfactory = UUID(str(pythoncom.IID_IClassFactory)).bytes_le
  156. dll.DllGetClassObject(clsid, iclassfactory, byref(com_classfactory))
  157. # 从 IClassFactory 接口创建 COM 对象实例
  158. class_factory = pythoncom.ObjectFromAddress(com_classfactory.value, pythoncom.IID_IClassFactory)
  159. iptr = class_factory.CreateInstance(None, pythoncom.IID_IDispatch)
  160. return win32com.client.Dispatch(iptr, resultCLSID=None)
  161. except Exception as e:
  162. raise RuntimeError(f"Failed to create COM object from DLL: {e}")
  163. finally:
  164. # 释放 IClassFactory 接口
  165. if com_classfactory:
  166. IUnknown_Release = WINFUNCTYPE(c_void_p)(2, 'Release', (), pythoncom.IID_IUnknown)
  167. IUnknown_Release(com_classfactory)
  168. @staticmethod
  169. def draw_text_on_window(hwnd, text):
  170. print(text)
  171. # 获取窗口的设备上下文
  172. hdc = win32gui.GetDC(hwnd)
  173. # 设置文本颜色
  174. color = win32api.RGB(255, 0, 0) # 红色
  175. ctypes.windll.gdi32.SetTextColor(hdc, color)
  176. ctypes.windll.gdi32.SetBkMode(hdc, 1) # 透明背景
  177. # 在指定位置绘制文本
  178. x, y = 50, 50 # 文本的起始坐标
  179. ctypes.windll.gdi32.ExtTextOutW(hdc, x, y, 0, None, text, len(text), None)
  180. # 释放设备上下文
  181. win32gui.ReleaseDC(hwnd, hdc)
  182. @staticmethod
  183. def kill_process(pid):
  184. try:
  185. p = psutil.Process(pid)
  186. p.kill() # 强制终止进程
  187. # 可选:等待进程结束,设置超时
  188. p.wait(timeout=3) # 等待最多3秒
  189. except psutil.NoSuchProcess:
  190. logger.warning(f"Process {pid} does not exist.")
  191. except psutil.AccessDenied:
  192. logger.warning(f"Access denied to terminate process {pid}.")
  193. except psutil.TimeoutExpired:
  194. logger.warning(f"Process {pid} could not be terminated in time.")
  195. except Exception as e:
  196. logger.exception(f"Error: {e}")