feat: 优化硬件ID生成方案,增加计算机名作为备选方案,确保ID生成的稳定性 v3.5.2

This commit is contained in:
huangzhenpc
2025-02-14 17:35:27 +08:00
parent b11452aea8
commit 26e159d71c
5 changed files with 337 additions and 250 deletions

View File

@@ -13,7 +13,12 @@ from pathlib import Path
from utils.config import Config
from utils.cursor_registry import CursorRegistry
from cursor_auth_manager import CursorAuthManager
from utils.cursor_resetter import CursorResetter # 添加导入
from utils.cursor_resetter import CursorResetter
from datetime import datetime
# 添加缓存文件路径常量
CACHE_DIR = Path(os.path.expanduser("~")) / ".cursor_cache"
HARDWARE_ID_CACHE = CACHE_DIR / "hardware_id.json"
def is_admin() -> bool:
"""检查是否具有管理员权限
@@ -56,7 +61,11 @@ def run_as_admin():
return False
def get_hardware_id() -> str:
"""获取硬件唯一标识"""
"""获取硬件唯一标识
方案1: CPU ID + 主板序列号 + BIOS序列号
方案2: 系统盘序列号 + Windows安装时间
方案3: 计算机名(最后的备选方案)
"""
try:
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = None
@@ -64,26 +73,70 @@ def get_hardware_id() -> str:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 获取CPU信息
cpu_info = subprocess.check_output('wmic cpu get ProcessorId', startupinfo=startupinfo).decode()
cpu_id = cpu_info.split('\n')[1].strip()
# 获取主板序列号
board_info = subprocess.check_output('wmic baseboard get SerialNumber', startupinfo=startupinfo).decode()
board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号
bios_info = subprocess.check_output('wmic bios get SerialNumber', startupinfo=startupinfo).decode()
bios_id = bios_info.split('\n')[1].strip()
# 组合信息并生成哈希
combined = f"{cpu_id}:{board_id}:{bios_id}"
return hashlib.md5(combined.encode()).hexdigest()
# 方案1: 尝试获取硬件信息
try:
# 获取CPU ID
cpu_info = subprocess.check_output('wmic cpu get ProcessorId', startupinfo=startupinfo).decode()
cpu_id = cpu_info.split('\n')[1].strip()
# 获取主板序列号
board_info = subprocess.check_output('wmic baseboard get SerialNumber', startupinfo=startupinfo).decode()
board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号
bios_info = subprocess.check_output('wmic bios get SerialNumber', startupinfo=startupinfo).decode()
bios_id = bios_info.split('\n')[1].strip()
# 如果所有信息都获取成功且有效
if all([cpu_id, board_id, bios_id]) and not all(x in ['', '0', 'None', 'To be filled by O.E.M.'] for x in [cpu_id, board_id, bios_id]):
combined = f"{cpu_id}:{board_id}:{bios_id}"
hardware_id = hashlib.md5(combined.encode()).hexdigest()
logging.info("使用硬件信息生成ID成功")
return hardware_id
except Exception as e:
logging.warning(f"方案1失败: {str(e)}")
# 方案2: 系统盘序列号 + Windows安装时间
try:
backup_info = []
# 获取系统盘序列号
volume_info = subprocess.check_output('wmic logicaldisk where "DeviceID=\'C:\'" get VolumeSerialNumber', startupinfo=startupinfo).decode()
volume_serial = volume_info.split('\n')[1].strip()
if volume_serial and volume_serial not in ['', '0']:
backup_info.append(("volume", volume_serial))
# 获取Windows安装时间
os_info = subprocess.check_output('wmic os get InstallDate', startupinfo=startupinfo).decode()
install_date = os_info.split('\n')[1].strip()
if install_date:
backup_info.append(("install", install_date))
if backup_info:
combined = "|".join(f"{k}:{v}" for k, v in sorted(backup_info))
hardware_id = hashlib.md5(combined.encode()).hexdigest()
logging.info("使用系统信息生成ID成功")
return hardware_id
except Exception as e:
logging.warning(f"方案2失败: {str(e)}")
# 方案3: 使用计算机名(最后的备选方案)
import platform
computer_name = platform.node()
if computer_name:
hardware_id = hashlib.md5(computer_name.encode()).hexdigest()
logging.info("使用计算机名生成ID成功")
return hardware_id
raise ValueError("无法获取任何可用信息来生成硬件ID")
except Exception as e:
logging.error(f"获取硬件ID失败: {str(e)}")
# 如果获取失败使用UUID作为备选方案
return str(uuid.uuid4())
error_msg = f"生成硬件ID失败: {str(e)}"
logging.error(error_msg)
raise RuntimeError(error_msg)
class AccountSwitcher:
def __init__(self):
@@ -101,9 +154,9 @@ class AccountSwitcher:
self.package_json = self.app_path / "package.json"
self.auth_manager = CursorAuthManager()
self.config = Config()
self.hardware_id = self.get_hardware_id() # 先获取硬件ID
self.registry = CursorRegistry() # 添加注册表操作工具类
self.resetter = CursorResetter() # 添加重置工具类
self.hardware_id = get_hardware_id() # 使用新的硬件ID获取函数
self.registry = CursorRegistry()
self.resetter = CursorResetter()
self.max_retries = 5
self.wait_time = 1
@@ -111,33 +164,7 @@ class AccountSwitcher:
def get_hardware_id(self) -> str:
"""获取硬件唯一标识"""
try:
# 创建startupinfo对象来隐藏命令行窗口
startupinfo = None
if sys.platform == "win32":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
# 获取CPU信息
cpu_info = subprocess.check_output('wmic cpu get ProcessorId', startupinfo=startupinfo).decode()
cpu_id = cpu_info.split('\n')[1].strip()
# 获取主板序列号
board_info = subprocess.check_output('wmic baseboard get SerialNumber', startupinfo=startupinfo).decode()
board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号
bios_info = subprocess.check_output('wmic bios get SerialNumber', startupinfo=startupinfo).decode()
bios_id = bios_info.split('\n')[1].strip()
# 组合信息并生成哈希
combined = f"{cpu_id}:{board_id}:{bios_id}"
return hashlib.md5(combined.encode()).hexdigest()
except Exception as e:
logging.error(f"获取硬件ID失败: {str(e)}")
# 如果获取失败使用UUID作为备选方案
return str(uuid.uuid4())
return get_hardware_id() # 使用全局函数
def get_cursor_version(self) -> str:
"""获取Cursor版本号"""
@@ -387,24 +414,40 @@ class AccountSwitcher:
shell=True
)
# 等待进程关闭
# 等待进程关闭,增加重试次数和等待时间
retry_count = 0
while retry_count < self.max_retries:
if not self.get_process_details("Cursor.exe"):
max_retries = 10 # 增加最大重试次数
wait_time = 2 # 增加每次等待时间
while retry_count < max_retries:
remaining_processes = self.get_process_details("Cursor.exe")
if not remaining_processes:
logging.info("所有Cursor进程已关闭")
# 额外等待一段时间确保系统资源完全释放
time.sleep(2)
return True
retry_count += 1
if retry_count >= self.max_retries:
if retry_count >= max_retries:
processes = self.get_process_details("Cursor.exe")
if processes:
logging.error(f"无法关闭以下进程:")
for p in processes:
logging.error(f"PID={p['pid']}, 路径={p['name']}")
# 最后一次尝试强制结束
try:
subprocess.run(
"taskkill /f /im Cursor.exe /t >nul 2>&1",
startupinfo=startupinfo,
shell=True
)
time.sleep(2)
except:
pass
return False
logging.warning(f"等待进程关闭, 尝试 {retry_count}/{self.max_retries}...")
time.sleep(self.wait_time)
logging.warning(f"等待进程关闭, 尝试 {retry_count}/{max_retries}...")
time.sleep(wait_time)
return True
else:
@@ -456,61 +499,74 @@ class AccountSwitcher:
logging.error("无法关闭Cursor进程")
return False
# 等待进程完全关闭
time.sleep(2)
# 等待系统资源释放
time.sleep(3)
# 启动Cursor
if sys.platform == "win32":
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
try:
# 使用subprocess启动
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen(
str(cursor_exe),
startupinfo=startupinfo,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
# 等待进程启动
time.sleep(3)
# 验证进程是否启动
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info("Cursor启动成功")
return True
else:
logging.error("Cursor进程未找到")
# 尝试使用 os.startfile 作为备选方案
max_retries = 3
for attempt in range(max_retries):
try:
# 使用subprocess启动
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen(
str(cursor_exe),
startupinfo=startupinfo,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
# 等待进程启动
time.sleep(5) # 增加等待时间
# 验证进程是否启动
processes = self.get_process_details("Cursor.exe")
if processes:
logging.info("Cursor启动成功")
return True
else:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败,准备重试...")
time.sleep(3)
continue
logging.error("Cursor进程未找到")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(5)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
if attempt < max_retries - 1:
logging.warning(f"启动尝试 {attempt + 1} 失败: {str(e)},准备重试...")
time.sleep(3)
continue
logging.error(f"启动Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为最后的备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(3)
time.sleep(5)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
except Exception as e:
logging.error(f"启动Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为备选方案
try:
os.startfile(str(cursor_exe))
time.sleep(3)
logging.info("使用备选方案启动Cursor")
return True
except Exception as e:
logging.error(f"备选启动方案失败: {str(e)}")
return False
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
try:
subprocess.run("open -a Cursor", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
@@ -519,6 +575,7 @@ class AccountSwitcher:
elif sys.platform == "linux":
try:
subprocess.run("cursor &", shell=True, check=True)
time.sleep(5)
logging.info("Cursor启动成功")
return True
except subprocess.CalledProcessError as e:
@@ -529,16 +586,6 @@ class AccountSwitcher:
except Exception as e:
logging.error(f"重启Cursor失败: {str(e)}")
# 尝试使用 os.startfile 作为最后的备选方案
try:
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
os.startfile(str(cursor_exe))
time.sleep(3)
logging.info("使用最终备选方案启动Cursor")
return True
except:
pass
return False
def activate_and_switch(self, activation_code: str) -> Tuple[bool, str]: