Exeprotector/main.py
2025-09-05 11:52:53 +08:00

751 lines
30 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
作者:太一
微信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()