feat: 优化硬件ID生成方案,增加计算机名作为备选方案,确保ID生成的稳定性 v3.5.2
This commit is contained in:
@@ -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]:
|
||||
|
||||
Reference in New Issue
Block a user