""" 作者:太一 微信:taiyi1224 邮箱:shoubo1224@qq.com """ import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext import os import json import pyperclip from database import LicenseDatabase from encryptor import EXEEncryptor from machine_code import get_machine_code from tkinter import font as tkfont def set_dark_theme(root): style = ttk.Style(root) # 仅 Windows 支持 'vista',其它系统可改为 'clam' style.theme_use('clam') style.configure('.', background='#2e2e2e', foreground='#ffffff', fieldbackground='#3c3f41', selectbackground='#0078d4', insertbackground='#ffffff', borderwidth=1, focuscolor='none') style.map('.', background=[('active', '#0078d4')]) style.configure('TButton', padding=6, relief='flat', background='#0078d4', foreground='#ffffff') style.map('TButton', background=[('active', '#106ebe')]) style.configure('TLabel', background='#2e2e2e', foreground='#ffffff') style.configure('TEntry', fieldbackground='#3c3f41', foreground='#ffffff', insertbackground='#ffffff', relief='flat', padding=5) style.configure('Treeview', background='#252526', foreground='#ffffff', fieldbackground='#252526') style.configure('Treeview.Heading', background='#3c3f41', foreground='#ffffff') class EXEEncryptionTool(tk.Tk): def __init__(self): super().__init__() self.title("EXE文件加密保护系统") self.geometry("800x600") self.minsize(800, 600) # set_dark_theme(self) # 数据库配置 self.db_config = { 'host': '', 'database': 'license_system', 'user': '', 'password': '' } # 初始化数据库连接 self.db = None # 创建界面 self.create_widgets() # 加载保存的配置 self.load_config() def create_widgets(self): """创建界面组件""" # 创建标签页 tab_control = ttk.Notebook(self) # 数据库配置标签页 self.tab_db_config = ttk.Frame(tab_control) tab_control.add(self.tab_db_config, text="数据库配置") # 卡密生成标签页 self.tab_key_gen = ttk.Frame(tab_control) tab_control.add(self.tab_key_gen, text="卡密生成") # EXE加密标签页 self.tab_encrypt = ttk.Frame(tab_control) tab_control.add(self.tab_encrypt, text="EXE加密") # 卡密管理标签页 self.tab_key_manage = ttk.Frame(tab_control) tab_control.add(self.tab_key_manage, text="卡密管理") tab_control.pack(expand=1, fill="both") # 初始化各个标签页 self.init_db_config_tab() self.init_key_gen_tab() self.init_encrypt_tab() self.init_key_manage_tab() # 状态栏 self.status_var = tk.StringVar(value="就绪") status_bar = ttk.Label(self, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # def unbind_selected_key(self): # key_code = self.get_selected_key_code() # if not key_code: # return # if messagebox.askyesno("确认", f"确定解除卡密 {key_code} 与当前机器的绑定?"): # ok, msg = self.db.unbind_key(key_code) # messagebox.showinfo("结果", msg) # self.load_all_keys() def unbind_selected_key(self): """后台解除卡密与当前机器的绑定""" key_code = self.get_selected_key_code() if not key_code: return if not self.db or not self.db.connection.is_connected(): messagebox.showerror("错误", "请先连接数据库") return if messagebox.askyesno("确认", f"确定解除卡密 {key_code} 与当前机器的绑定?"): ok, msg = self.db.unbind_key(key_code) messagebox.showinfo("结果", msg) self.load_all_keys() # 刷新列表 def init_db_config_tab(self): """初始化数据库配置标签页""" frame = ttk.LabelFrame(self.tab_db_config, text="数据库连接设置") frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 表单布局 grid_frame = ttk.Frame(frame) grid_frame.pack(padx=10, pady=10, fill=tk.X) # 主机 ttk.Label(grid_frame, text="主机:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W) self.entry_db_host = ttk.Entry(grid_frame) self.entry_db_host.grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW) # 数据库名 ttk.Label(grid_frame, text="数据库:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W) self.entry_db_name = ttk.Entry(grid_frame) self.entry_db_name.grid(row=1, column=1, padx=5, pady=5, sticky=tk.EW) self.entry_db_name.insert(0, "") # 用户名 ttk.Label(grid_frame, text="用户名:").grid(row=2, column=0, padx=5, pady=5, sticky=tk.W) self.entry_db_user = ttk.Entry(grid_frame) self.entry_db_user.grid(row=2, column=1, padx=5, pady=5, sticky=tk.EW) # 密码 ttk.Label(grid_frame, text="密码:").grid(row=3, column=0, padx=5, pady=5, sticky=tk.W) self.entry_db_password = ttk.Entry(grid_frame, show="*") self.entry_db_password.grid(row=3, column=1, padx=5, pady=5, sticky=tk.EW) # 按钮 button_frame = ttk.Frame(frame) button_frame.pack(padx=10, pady=10, fill=tk.X) self.btn_connect_db = ttk.Button(button_frame, text="连接数据库", command=self.connect_db) self.btn_connect_db.pack(side=tk.LEFT, padx=5) self.btn_create_tables = ttk.Button(button_frame, text="创建数据库表", command=self.create_db_tables) self.btn_create_tables.pack(side=tk.LEFT, padx=5) self.btn_save_db_config = ttk.Button(button_frame, text="保存配置", command=self.save_db_config) self.btn_save_db_config.pack(side=tk.RIGHT, padx=5) # 连接状态 self.label_db_status = ttk.Label(frame, text="未连接数据库", foreground="red") self.label_db_status.pack(anchor=tk.W, padx=10, pady=5) # 日志区域 log_frame = ttk.LabelFrame(frame, text="操作日志") log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.text_db_log = scrolledtext.ScrolledText(log_frame, height=10) self.text_db_log.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.text_db_log.config(state=tk.DISABLED) # 设置网格权重 grid_frame.columnconfigure(1, weight=1) def init_key_gen_tab(self): """初始化卡密生成标签页""" frame = ttk.LabelFrame(self.tab_key_gen, text="卡密生成设置") frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 配置区域 config_frame = ttk.Frame(frame) config_frame.pack(fill=tk.X, padx=10, pady=10) # 有效期 ttk.Label(config_frame, text="有效期(天):").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W) self.entry_valid_days = ttk.Entry(config_frame, width=10) self.entry_valid_days.grid(row=0, column=1, padx=5, pady=5) self.entry_valid_days.insert(0, "30") # 生成数量 ttk.Label(config_frame, text="生成数量:").grid(row=0, column=2, padx=5, pady=5, sticky=tk.W) self.entry_key_count = ttk.Entry(config_frame, width=10) self.entry_key_count.grid(row=0, column=3, padx=5, pady=5) self.entry_key_count.insert(0, "1") # 生成按钮 self.btn_generate_keys = ttk.Button(config_frame, text="生成卡密", command=self.generate_keys) self.btn_generate_keys.grid(row=0, column=4, padx=20, pady=5) # 卡密列表 list_frame = ttk.LabelFrame(frame, text="生成的卡密") list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.text_keys = scrolledtext.ScrolledText(list_frame) self.text_keys.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 操作按钮 button_frame = ttk.Frame(frame) button_frame.pack(fill=tk.X, padx=10, pady=10) self.btn_copy_keys = ttk.Button(button_frame, text="复制所有卡密", command=self.copy_keys) self.btn_copy_keys.pack(side=tk.LEFT, padx=5) self.btn_export_keys = ttk.Button(button_frame, text="导出卡密到文件", command=self.export_keys) self.btn_export_keys.pack(side=tk.LEFT, padx=5) # 设置网格权重 config_frame.columnconfigure(5, weight=1) def init_encrypt_tab(self): """初始化EXE加密标签页""" frame = ttk.LabelFrame(self.tab_encrypt, text="EXE文件加密") frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 源文件 source_frame = ttk.Frame(frame) source_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(source_frame, text="源EXE文件:").pack(side=tk.LEFT, padx=5) self.entry_source_file = ttk.Entry(source_frame) self.entry_source_file.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.btn_browse_source = ttk.Button(source_frame, text="浏览...", command=self.browse_source_file) self.btn_browse_source.pack(side=tk.LEFT, padx=5) # 目标文件 dest_frame = ttk.Frame(frame) dest_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(dest_frame, text="加密后文件:").pack(side=tk.LEFT, padx=5) self.entry_dest_file = ttk.Entry(dest_frame) self.entry_dest_file.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.btn_browse_dest = ttk.Button(dest_frame, text="浏览...", command=self.browse_dest_file) self.btn_browse_dest.pack(side=tk.LEFT, padx=5) # 验证程序 validator_frame = ttk.Frame(frame) validator_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(validator_frame, text="验证程序:").pack(side=tk.LEFT, padx=5) self.entry_validator = ttk.Entry(validator_frame) self.entry_validator.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.btn_browse_validator = ttk.Button(validator_frame, text="浏览...", command=self.browse_validator) self.btn_browse_validator.pack(side=tk.LEFT, padx=5) # 加密按钮 self.btn_encrypt = ttk.Button(frame, text="加密EXE文件", command=self.encrypt_exe) self.btn_encrypt.pack(pady=10) # 加密日志 log_frame = ttk.LabelFrame(frame, text="加密日志") log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.text_encrypt_log = scrolledtext.ScrolledText(log_frame, height=10) self.text_encrypt_log.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.text_encrypt_log.config(state=tk.DISABLED) def init_key_manage_tab(self): """初始化卡密管理标签页""" frame = ttk.LabelFrame(self.tab_key_manage, text="卡密管理") frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 搜索区域 search_frame = ttk.Frame(frame) search_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(search_frame, text="搜索卡密:").pack(side=tk.LEFT, padx=5) self.entry_key_search = ttk.Entry(search_frame) self.entry_key_search.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.btn_search_keys = ttk.Button(search_frame, text="搜索", command=self.search_keys) self.btn_search_keys.pack(side=tk.LEFT, padx=5) self.btn_refresh_keys = ttk.Button(search_frame, text="刷新", command=self.load_all_keys) self.btn_refresh_keys.pack(side=tk.LEFT, padx=5) # 卡密列表 columns = ("id", "key_code", "machine_code", "start_time", "end_time", "status", "created_at") self.tree_keys = ttk.Treeview(frame, columns=columns, show="headings") # 设置列标题 self.tree_keys.heading("id", text="ID") self.tree_keys.heading("key_code", text="卡密") self.tree_keys.heading("machine_code", text="机器码") self.tree_keys.heading("start_time", text="开始时间") self.tree_keys.heading("end_time", text="结束时间") self.tree_keys.heading("status", text="状态") self.tree_keys.heading("created_at", text="创建时间") # 设置列宽 self.tree_keys.column("id", width=50) self.tree_keys.column("key_code", width=150) self.tree_keys.column("machine_code", width=120) self.tree_keys.column("start_time", width=120) self.tree_keys.column("end_time", width=120) self.tree_keys.column("status", width=80) self.tree_keys.column("created_at", width=120) # 添加滚动条 scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.tree_keys.yview) self.tree_keys.configure(yscroll=scrollbar.set) self.tree_keys.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=5) scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=5) # 操作按钮 button_frame = ttk.Frame(frame) button_frame.pack(fill=tk.X, padx=10, pady=10) self.btn_ban_key = ttk.Button(button_frame, text="封禁选中卡密", command=self.ban_selected_key) self.btn_ban_key.pack(side=tk.LEFT, padx=5) self.btn_unban_key = ttk.Button(button_frame, text="解封选中卡密", command=self.unban_selected_key) self.btn_unban_key.pack(side=tk.LEFT, padx=5) self.btn_release_key = ttk.Button(button_frame, text="释放选中卡密", command=self.release_selected_key) self.btn_release_key.pack(side=tk.LEFT, padx=5) self.btn_delete_key = ttk.Button(button_frame, text="删除选中卡密", command=self.delete_selected_key) self.btn_delete_key.pack(side=tk.LEFT, padx=5) # ✅ 新增:解绑卡密按钮 self.btn_unbind = ttk.Button(button_frame, text="解绑卡密", command=self.unbind_selected_key) self.btn_unbind.pack(side=tk.LEFT, padx=5) # 数据库相关方法 def log(self, text, widget=None): """添加日志信息""" if not widget: widget = self.text_db_log widget.config(state=tk.NORMAL) widget.insert(tk.END, text + "\n") widget.see(tk.END) widget.config(state=tk.DISABLED) self.update_idletasks() def connect_db(self): """连接到数据库""" self.db_config['host'] = self.entry_db_host.get() self.db_config['database'] = self.entry_db_name.get() self.db_config['user'] = self.entry_db_user.get() self.db_config['password'] = self.entry_db_password.get() self.log(f"尝试连接到数据库: {self.db_config['host']}/{self.db_config['database']}") self.db = LicenseDatabase( self.db_config['host'], self.db_config['database'], self.db_config['user'], self.db_config['password'] ) if self.db.connect(): self.label_db_status.config(text="已连接到数据库", foreground="green") self.log("数据库连接成功") messagebox.showinfo("成功", "数据库连接成功") # 连接成功后加载卡密列表 self.load_all_keys() else: self.label_db_status.config(text="数据库连接失败", foreground="red") self.log("数据库连接失败") messagebox.showerror("错误", "无法连接到数据库,请检查配置") def create_db_tables(self): """创建数据库表""" if not self.db or not self.db.connection.is_connected(): if not self.connect_db(): return self.log("尝试创建数据库表...") if self.db.create_tables(): self.log("数据库表创建成功") messagebox.showinfo("成功", "数据库表创建成功") else: self.log("数据库表创建失败") messagebox.showerror("错误", "数据库表创建失败") def save_db_config(self): """保存数据库配置""" self.db_config['host'] = self.entry_db_host.get() self.db_config['database'] = self.entry_db_name.get() self.db_config['user'] = self.entry_db_user.get() self.db_config['password'] = self.entry_db_password.get() try: with open('db_config.json', 'w') as f: json.dump(self.db_config, f) self.log("数据库配置保存成功") messagebox.showinfo("成功", "数据库配置已保存") except Exception as e: self.log(f"保存配置失败: {str(e)}") messagebox.showerror("错误", f"保存配置失败: {str(e)}") def load_config(self): """加载保存的配置""" try: if os.path.exists('db_config.json'): with open('db_config.json', 'r') as f: config = json.load(f) self.entry_db_host.insert(0, config.get('host', '')) self.entry_db_name.insert(0, config.get('database', 'license_system')) self.entry_db_user.insert(0, config.get('user', '')) self.entry_db_password.insert(0, config.get('password', '')) except Exception as e: print(f"加载配置失败: {e}") # 卡密生成相关方法 def generate_keys(self): """生成卡密""" if not self.db or not self.db.connection.is_connected(): messagebox.showerror("错误", "请先连接数据库") return try: days = int(self.entry_valid_days.get()) count = int(self.entry_key_count.get()) if days <= 0 or count <= 0: messagebox.showerror("错误", "有效期和数量必须为正数") return self.text_keys.delete(1.0, tk.END) self.log(f"开始生成 {count} 个有效期为 {days} 天的卡密...", self.text_keys) keys = [] for i in range(count): key = self.db.generate_key(days) if key: keys.append(key) self.text_keys.insert(tk.END, key + "\n") self.text_keys.see(tk.END) self.update_idletasks() self.log(f"\n成功生成 {len(keys)} 个卡密", self.text_keys) messagebox.showinfo("成功", f"成功生成 {len(keys)} 个卡密") # 刷新卡密列表 self.load_all_keys() except ValueError: messagebox.showerror("错误", "请输入有效的数字") except Exception as e: messagebox.showerror("错误", f"生成卡密失败: {str(e)}") def copy_keys(self): """复制卡密到剪贴板""" keys = self.text_keys.get(1.0, tk.END).strip() if keys: pyperclip.copy(keys) messagebox.showinfo("成功", "卡密已复制到剪贴板") else: messagebox.showinfo("提示", "没有可复制的卡密") def export_keys(self): """导出卡密到文件""" keys = self.text_keys.get(1.0, tk.END).strip() if not keys: messagebox.showinfo("提示", "没有可导出的卡密") return file_path = filedialog.asksaveasfilename( defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if file_path: try: with open(file_path, 'w') as f: f.write(keys) messagebox.showinfo("成功", f"卡密已导出到 {file_path}") except Exception as e: messagebox.showerror("错误", f"导出失败: {str(e)}") # EXE加密相关方法 def browse_source_file(self): """浏览源文件""" file_path = filedialog.askopenfilename( filetypes=[("EXE文件", "*.exe"), ("所有文件", "*.*")] ) if file_path: self.entry_source_file.delete(0, tk.END) self.entry_source_file.insert(0, file_path) # 自动填充目标文件路径 dir_name, file_name = os.path.split(file_path) name, ext = os.path.splitext(file_name) dest_file = os.path.join(dir_name, f"{name}_encrypted{ext}") self.entry_dest_file.delete(0, tk.END) self.entry_dest_file.insert(0, dest_file) # 自动填充验证器路径(使用新的validator_wrapper.py) validator_path = os.path.join(os.path.dirname(__file__), "validator_wrapper.py") self.entry_validator.delete(0, tk.END) self.entry_validator.insert(0, validator_path) def browse_dest_file(self): """浏览目标文件""" file_path = filedialog.asksaveasfilename( defaultextension=".exe", filetypes=[("EXE文件", "*.exe"), ("文本文件", "*.py"), ("所有文件", "*.*")] ) if file_path: self.entry_dest_file.delete(0, tk.END) self.entry_dest_file.insert(0, file_path) def browse_validator(self): """浏览验证程序""" file_path = filedialog.askopenfilename( filetypes=[("Python程序", "*.*"), ("EXE文件", "*.exe"), ("所有文件", "*.*")] ) if file_path: self.entry_validator.delete(0, tk.END) self.entry_validator.insert(0, file_path) def encrypt_exe(self): """加密EXE文件""" source_path = self.entry_source_file.get() dest_path = self.entry_dest_file.get() validator_path = self.entry_validator.get() # 检查文件路径 if not source_path or not os.path.exists(source_path): messagebox.showerror("错误", "请选择有效的源文件") return if not dest_path: messagebox.showerror("错误", "请选择目标文件路径") return if not validator_path or not os.path.exists(validator_path): messagebox.showerror("错误", "请选择有效的验证程序") return # 检查数据库连接 if not self.db or not self.db.connection.is_connected(): if not self.connect_db(): return # 执行加密 self.log("开始加密文件...", self.text_encrypt_log) self.log(f"源文件: {source_path}", self.text_encrypt_log) try: encryptor = EXEEncryptor() success, msg = encryptor.encrypt_file( source_path, dest_path, validator_path, self.db_config ) self.log(msg, self.text_encrypt_log) if success: self.log(f"加密后的文件已保存到: {dest_path}", self.text_encrypt_log) messagebox.showinfo("成功", f"文件加密成功\n保存到: {dest_path}") else: messagebox.showerror("错误", msg) except Exception as e: error_msg = f"加密过程出错: {str(e)}" self.log(error_msg, self.text_encrypt_log) messagebox.showerror("错误", error_msg) # 卡密管理相关方法 def load_all_keys(self): """加载所有卡密""" if not self.db or not self.db.connection.is_connected(): return # 清空现有列表 for item in self.tree_keys.get_children(): self.tree_keys.delete(item) try: keys = self.db.get_all_keys() for key in keys: # 格式化日期时间 start_time = key['start_time'].strftime("%Y-%m-%d") if key['start_time'] else "" end_time = key['end_time'].strftime("%Y-%m-%d") if key['end_time'] else "" created_at = key['created_at'].strftime("%Y-%m-%d") if key['created_at'] else "" self.tree_keys.insert("", tk.END, values=( key['id'], key['key_code'], key['machine_code'] or "", start_time, end_time, key['status'], created_at )) # 根据状态设置行颜色 item = self.tree_keys.get_children()[-1] if key['status'] == 'active': self.tree_keys.item(item, tags=('active',)) elif key['status'] == 'expired': self.tree_keys.item(item, tags=('expired',)) elif key['status'] == 'banned': self.tree_keys.item(item, tags=('banned',)) # 设置标签样式 self.tree_keys.tag_configure('active', foreground='green') self.tree_keys.tag_configure('expired', foreground='gray') self.tree_keys.tag_configure('banned', foreground='red') except Exception as e: print(f"加载卡密列表失败: {e}") def search_keys(self): """搜索卡密""" if not self.db or not self.db.connection.is_connected(): return search_text = self.entry_key_search.get().strip().lower() if not search_text: self.load_all_keys() return # 清空现有列表 for item in self.tree_keys.get_children(): self.tree_keys.delete(item) try: keys = self.db.get_all_keys() for key in keys: # 检查是否匹配搜索文本 if (search_text in key['key_code'].lower() or search_text in key['status'].lower() or (key['machine_code'] and search_text in key['machine_code'].lower())): # 格式化日期时间 start_time = key['start_time'].strftime("%Y-%m-%d") if key['start_time'] else "" end_time = key['end_time'].strftime("%Y-%m-%d") if key['end_time'] else "" created_at = key['created_at'].strftime("%Y-%m-%d") if key['created_at'] else "" self.tree_keys.insert("", tk.END, values=( key['id'], key['key_code'], key['machine_code'] or "", start_time, end_time, key['status'], created_at )) except Exception as e: print(f"搜索卡密失败: {e}") def get_selected_key_code(self): """获取选中的卡密""" selected_items = self.tree_keys.selection() if not selected_items: messagebox.showinfo("提示", "请先选择一个卡密") return None item = selected_items[0] key_code = self.tree_keys.item(item, "values")[1] return key_code def ban_selected_key(self): """封禁选中的卡密""" key_code = self.get_selected_key_code() if not key_code: return if messagebox.askyesno("确认", f"确定要封禁卡密 {key_code} 吗?"): if self.db.update_key_status(key_code, 'banned'): messagebox.showinfo("成功", "卡密已封禁") self.load_all_keys() else: messagebox.showerror("错误", "封禁卡密失败") def unban_selected_key(self): """解封选中的卡密""" key_code = self.get_selected_key_code() if not key_code: return if messagebox.askyesno("确认", f"确定要解封卡密 {key_code} 吗?"): # 检查原状态是未使用还是已激活 try: selected_items = self.tree_keys.selection() item = selected_items[0] original_status = self.tree_keys.item(item, "values")[5] new_status = 'unused' if original_status == 'banned' and not self.tree_keys.item(item, "values")[ 2] else 'active' if self.db.update_key_status(key_code, new_status): messagebox.showinfo("成功", "卡密已解封") self.load_all_keys() else: messagebox.showerror("错误", "解封卡密失败") except Exception as e: messagebox.showerror("错误", f"操作失败: {str(e)}") def release_selected_key(self): """释放选中的已使用激活码""" key_code = self.get_selected_key_code() if not key_code: return # 获取选择项的状态信息 selected_items = self.tree_keys.selection() if selected_items: item = selected_items[0] status = self.tree_keys.item(item, "values")[5] if status != 'active': messagebox.showwarning("警告", "只能释放处于'激活'状态的激活码") return if messagebox.askyesno("确认释放", f"确定要释放激活码 '{key_code}' 吗?\n\n释放后:\n1. 该激活码将变为未使用状态\n2. 机器码将被清空\n3. 可以重新在任何机器上使用\n\n此操作不可撤销!"): if not self.db or not self.db.connection.is_connected(): if not self.connect_db(): return success, msg = self.db.release_key(key_code) if success: messagebox.showinfo("成功", f"激活码 '{key_code}' 已释放成功\n{msg}") self.load_all_keys() # 刷新列表 else: messagebox.showerror("失败", msg) def delete_selected_key(self): """删除选中的卡密""" key_code = self.get_selected_key_code() if not key_code: return if messagebox.askyesno("确认", f"确定要删除卡密 {key_code} 吗?\n此操作不可恢复!"): # 实际项目中应该实现delete_key方法 messagebox.showinfo("提示", "为安全起见,当前版本不允许删除激活码,建议使用封禁或释放功能") if __name__ == "__main__": app = EXEEncryptionTool() app.mainloop()